#[allow(dead_code)] #[path = "printers/array/mod.rs"] mod array; #[allow(dead_code)] #[path = "printers/attribute/mod.rs"] mod attribute; #[allow(dead_code)] #[path = "printers/enum/mod.rs"] mod r#enum; #[allow(dead_code)] #[path = "printers/features/mod.rs"] mod features; #[allow(dead_code)] #[path = "printers/formatting/mod.rs"] mod formatting; #[allow(dead_code)] #[path = "printers/map/mod.rs"] mod map; #[allow(dead_code)] #[path = "printers/metas/mod.rs"] mod metas; #[allow(dead_code)] #[path = "printers/optional/mod.rs"] mod optional; #[allow(dead_code)] #[path = "printers/struct/mod.rs"] mod r#struct; #[allow(dead_code)] #[path = "printers/tuple/mod.rs"] mod tuple; // --- mod prelude { #![allow(clippy::single_component_path_imports)] pub use super::*; pub use doku::Document; pub use serde::{Deserialize, Serialize}; macro_rules! printer_test { ( $( $file:literal => $assert_fn:ident $assert_args:tt ),+ $(,)? ) => { #[test] fn test() { let mut expectations = Vec::new(); $( let expected = printer_test!(@assert $assert_fn $assert_args); expectations.push(($file, expected)); )+ use std::path::{Path, PathBuf}; let dir: PathBuf = Path::new(file!()) .parent() .unwrap() .iter() .skip(1) .collect(); assert_dir(dir, expectations); } }; (@assert to_json($ty:ty)) => {{ doku::to_json::<$ty>() }}; (@assert to_json_without_comma($ty:ty)) => {{ printer_test!(@assert to_json_without_comma($ty, {})) }}; (@assert to_json_without_comma($ty:ty, $fmt:tt)) => {{ let fmt = serde_json::json!($fmt); let mut fmt: doku::json::Formatting = serde_json::from_value(fmt).expect("Given formatting is not valid"); fmt.objects_style.use_comma_as_separator = false; doku::to_json_fmt::<$ty>(&fmt) }}; (@assert to_json_without_key_quotes($ty:ty)) => {{ printer_test!(@assert to_json_without_key_quotes($ty, {})) }}; (@assert to_json_without_key_quotes($ty:ty, $fmt:tt)) => {{ let fmt = serde_json::json!($fmt); let mut fmt: doku::json::Formatting = serde_json::from_value(fmt).expect("Given formatting is not valid"); fmt.objects_style.surround_keys_with_quotes = false; doku::to_json_fmt::<$ty>(&fmt) }}; (@assert to_json_fmt($ty:ty, $fmt:tt)) => {{ let fmt = serde_json::json!($fmt); let fmt = serde_json::from_value(fmt).expect("Given formatting is not valid"); doku::to_json_fmt::<$ty>(&fmt) }}; (@assert to_json_fmt_val($ty:ty, $fmt:tt)) => {{ let fmt = serde_json::json!($fmt); let fmt = serde_json::from_value(fmt).expect("Given formatting is not valid"); doku::to_json_fmt_val(&fmt, &<$ty>::default()) }}; (@assert to_json_val($ty:ty)) => {{ doku::to_json_val(&<$ty>::default()) }}; (@assert to_json_val_without_comma($ty:ty)) => {{ let mut fmt = doku::json::Formatting::default(); fmt.objects_style.use_comma_as_separator = false; doku::to_json_fmt_val(&fmt, &<$ty>::default()) }}; (@assert to_json_val_without_key_quotes($ty:ty)) => {{ let mut fmt = doku::json::Formatting::default(); fmt.objects_style.surround_keys_with_quotes = false; doku::to_json_fmt_val(&fmt, &<$ty>::default()) }}; (@assert to_toml($ty:ty)) => {{ doku::to_toml::<$ty>() }}; (@assert to_toml_fmt($ty:ty, $fmt:tt)) => {{ let fmt = serde_json::json!($fmt); let fmt = serde_json::from_value(fmt).expect("Given formatting is not valid"); doku::to_toml_fmt::<$ty>(&fmt) }}; (@assert to_toml_fmt_val($ty:ty, $fmt:tt)) => {{ let fmt = serde_toml::toml!($fmt); let fmt = serde_toml::from_value(fmt).expect("Given formatting is not valid"); doku::to_toml_fmt_val(&fmt, &<$ty>::default()) }}; (@assert to_toml_val($ty:ty)) => {{ doku::to_toml_val(&<$ty>::default()) }}; } macro_rules! panic_test { ( $message:literal => $assert_fn:ident $assert_args:tt ) => { panic_test!{test: $message => $assert_fn $assert_args} }; ( $($mod_name:ident: $message:literal => $assert_fn:ident $assert_args:tt),+ $(,)? ) => { $(mod $mod_name { use super::*; #[test] #[should_panic = $message] fn panic_test() { printer_test!(@assert $assert_fn $assert_args); } })+ }; } #[derive(Document)] #[allow(dead_code)] pub(crate) struct TomlWrapper { inner: T, } pub(crate) use panic_test; pub(crate) use printer_test; } use difference::Changeset; use std::fs; use std::path::Path; type FileName = &'static str; type FileBody = String; type DidAssertSucceed = bool; pub fn assert_dir( dir: impl AsRef, expectations: Vec<(FileName, FileBody)>, ) { let dir = dir.as_ref(); let mut all_asserts_succeeded = true; for (file, expected) in expectations { all_asserts_succeeded &= assert(dir, file, expected); } if !all_asserts_succeeded { panic!("Some assertions failed"); } } fn assert(dir: &Path, file: FileName, expected: FileBody) -> DidAssertSucceed { let path = dir.join(file); let path_new = path.with_extension(format!( "{}.new", path.extension().unwrap().to_string_lossy() )); if path_new.exists() { fs::remove_file(&path_new).unwrap_or_else(|err| { panic!( "Couldn't remove new-fixture `{}`: {}", path_new.display(), err ) }) } let actual = if path.exists() { fs::read_to_string(&path).unwrap_or_else(|err| { panic!("Couldn't read fixture `{}`: {}", path.display(), err) }) } else { Default::default() }; if actual == expected { true } else { fs::write(&path_new, &expected).unwrap_or_else(|err| { panic!("Couldn't write fixture `{}`: {}", path_new.display(), err) }); eprintln!( "\nFound differences between `{}` and `{}`:\n{}", path.display(), path_new.display(), Changeset::new(&actual, &expected, "\n"), ); false } }