use datatest_stable::Utf8Path;
use quote::ToTokens;
use syn::{DeriveInput, parse_quote};
#[path = "../src/internals/mod.rs"]
mod internals;
datatest_stable::harness! {
{ test = daft_snapshot, root = "tests/fixtures/valid", pattern = r"^.*(?<!\.output)\.rs$" },
{ test = daft_snapshot_invalid, root = "tests/fixtures/invalid", pattern = r"^.*(?<!\.output)\.rs$" },
}
fn daft_snapshot(
path: &Utf8Path,
input: String,
) -> datatest_stable::Result<()> {
let data = syn::parse_str::<syn::File>(&input)?;
let output = run_derive_macro(&data);
assert_derive_output(path, output);
Ok(())
}
fn daft_snapshot_invalid(
path: &Utf8Path,
input: String,
) -> datatest_stable::Result<()> {
let data = syn::parse_str::<syn::File>(&input)?;
let output = run_derive_macro(&data).map(|output| {
output.out
});
assert_derive_output(path, output);
Ok(())
}
fn run_derive_macro(
data: &syn::File,
) -> impl Iterator<Item = internals::DeriveDiffableOutput> + '_ {
let items = data.items.iter().filter_map(|item| match item {
syn::Item::Struct(item) => {
has_derive_diffable(&item.attrs).then(|| item.to_token_stream())
}
syn::Item::Enum(item) => {
has_derive_diffable(&item.attrs).then(|| item.to_token_stream())
}
syn::Item::Union(item) => {
has_derive_diffable(&item.attrs).then(|| item.to_token_stream())
}
_ => None,
});
items.enumerate().map(|(i, item)| {
let data = syn::parse2::<DeriveInput>(item).unwrap_or_else(|err| {
panic!("failed to parse item {i}: {err}");
});
internals::derive_diffable(data)
})
}
fn has_derive_diffable(attrs: &[syn::Attribute]) -> bool {
attrs.iter().any(|attr| {
if !attr.path().is_ident("derive") {
return false;
}
let mut is_diffable = false;
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("Diffable") {
is_diffable = true;
}
Ok(())
})
.expect("derive attributes parsed correctly");
is_diffable
})
}
fn assert_derive_output<T: ToTokens>(
path: &Utf8Path,
output: impl IntoIterator<Item = T>,
) {
let output = output.into_iter();
let file = parse_quote! {
#(#output)*
};
let output = prettyplease::unparse(&file);
let mut output_path = path.parent().unwrap().to_owned();
output_path.push("output");
output_path.push(path.file_name().unwrap());
output_path.set_extension("output.rs");
expectorate::assert_contents(&output_path, &output);
}