1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use proc_macro_crate::{crate_name, FoundCrate};
6use quote::quote;
7use syn::{parse_macro_input, Ident, ItemStruct};
8
9#[proc_macro_attribute]
10pub fn denom(_attr: TokenStream, item: TokenStream) -> TokenStream {
17 let input = parse_macro_input!(item as ItemStruct);
19
20 if !input.fields.is_empty() {
22 return syn::Error::new_spanned(input, "Struct must be zero-sized")
23 .to_compile_error()
24 .into();
25 }
26
27 let name = &input.ident;
29
30 let found_crate = crate_name("monetary").expect("Failed to find the `monetary` crate");
32
33 let mut derives = vec![
35 quote! { Clone },
36 quote! { Debug },
37 quote! { PartialEq },
38 quote! { Eq },
39 quote! { PartialOrd },
40 quote! { Ord },
41 quote! { Copy },
42 quote! { Default },
43 ];
44
45 let mut attributes = vec![];
46
47 #[cfg(feature = "serde")]
49 match &found_crate {
50 FoundCrate::Itself => {
51 derives.push(quote! { crate::__derive_import::serde::Serialize });
52 derives.push(quote! { crate::__derive_import::serde::Deserialize });
53 attributes.push(quote! { #[serde(crate = "crate::__derive_import::serde")] });
54 }
55 FoundCrate::Name(crate_name) => {
56 let ident = Ident::new(crate_name, Span::call_site());
57 derives.push(quote! { #ident::__derive_import::serde::Serialize });
58 derives.push(quote! { #ident::__derive_import::serde::Deserialize });
59 let serde_crate = format!("::{}::__derive_import::serde", crate_name);
60 attributes.push(quote! { #[serde(crate = #serde_crate)] });
61 }
62 }
63
64 #[cfg(feature = "schemars")]
65 match &found_crate {
66 FoundCrate::Itself => {
67 derives.push(quote! { crate::__derive_import::schemars::JsonSchema });
68 attributes.push(quote! { #[schemars(crate = "crate::__derive_import::schemars")] });
69 }
70 FoundCrate::Name(crate_name) => {
71 let ident = Ident::new(crate_name, Span::call_site());
72 derives.push(quote! { #ident::__derive_import::schemars::JsonSchema });
73 let schemars_crate = format!("::{}::__derive_import::schemars", crate_name);
74 attributes.push(quote! { #[schemars(crate = #schemars_crate)] });
75 }
76 }
77
78 let derives = quote! { #[derive(#(#derives),*)] };
80 let attributes = quote! { #(#attributes)* };
81
82 let trait_impl = match found_crate {
84 FoundCrate::Itself => quote! {
85 unsafe impl crate::Denomination for #name {}
86 },
87 FoundCrate::Name(crate_name) => {
88 let ident = Ident::new(&crate_name, Span::call_site());
89 quote! {
90 unsafe impl #ident::Denomination for #name {}
91 }
92 }
93 };
94
95 let expanded = quote! {
97 #derives
98 #attributes
99 #input
100 #trait_impl
101 };
102
103 TokenStream::from(expanded)
105}