insta_fun_meta_macros/
lib.rs1use proc_macro::TokenStream;
2use proc_macro_crate::{FoundCrate, crate_name};
3use quote::{format_ident, quote};
4use syn::parse::{Parse, ParseStream};
5use syn::{Expr, Ident, Result, Token, punctuated::Punctuated};
6
7struct MetadataField {
8 name: Ident,
9 value: Expr,
10}
11
12impl Parse for MetadataField {
13 fn parse(input: ParseStream<'_>) -> Result<Self> {
14 let name: Ident = input.parse()?;
15 input.parse::<Token![:]>()?;
16 let value: Expr = input.parse()?;
17 Ok(Self { name, value })
18 }
19}
20
21struct MetadataInput {
22 fields: Punctuated<MetadataField, Token![,]>,
23}
24
25impl Parse for MetadataInput {
26 fn parse(input: ParseStream<'_>) -> Result<Self> {
27 Ok(Self {
28 fields: Punctuated::<MetadataField, Token![,]>::parse_terminated(input)?,
29 })
30 }
31}
32
33fn resolve_insta_fun_root() -> proc_macro2::TokenStream {
34 let found = crate_name("insta-fun").or_else(|_| crate_name("insta_fun"));
35
36 match found {
37 Ok(FoundCrate::Itself) => quote!(::insta_fun),
38 Ok(FoundCrate::Name(name)) => {
39 let ident = format_ident!("{}", name.replace('-', "_"));
40 quote!(::#ident)
41 }
42 Err(_) => quote!(::insta_fun),
43 }
44}
45
46#[proc_macro]
47pub fn insta_fun_meta(input: TokenStream) -> TokenStream {
48 let input = syn::parse_macro_input!(input as MetadataInput);
49 let crate_root = resolve_insta_fun_root();
50
51 let expanded_fields = input.fields.iter().map(|field| {
52 let name = field.name.to_string();
53 let value = &field.value;
54 quote! {
55 #crate_root::meta::MetaField::new(#name, #value)
56 }
57 });
58
59 quote! {
60 #crate_root::meta::SnapshotMetadata::new(vec![
61 #(#expanded_fields),*
62 ])
63 }
64 .into()
65}