1use proc_macro::TokenStream;
10use quote::quote;
11use syn::{parse_macro_input, DeriveInput, LitStr};
12
13#[proc_macro_derive(JsonxConstructor, attributes(jsonx))]
27pub fn derive_jsonx_constructor(input: TokenStream) -> TokenStream {
28 let input = parse_macro_input!(input as DeriveInput);
29 expand(input)
30 .unwrap_or_else(syn::Error::into_compile_error)
31 .into()
32}
33
34fn expand(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
35 let ident = &input.ident;
36
37 let mut name: Option<LitStr> = None;
39 for attr in &input.attrs {
40 if !attr.path().is_ident("jsonx") {
41 continue;
42 }
43 attr.parse_nested_meta(|meta| {
44 if meta.path.is_ident("name") {
45 name = Some(meta.value()?.parse()?);
46 Ok(())
47 } else {
48 Err(meta.error("unknown `jsonx` option; expected `name = \"...\"`"))
49 }
50 })?;
51 }
52 let name = name.unwrap_or_else(|| LitStr::new(&ident.to_string().to_lowercase(), ident.span()));
53
54 if !is_valid_ctor_name(&name.value()) {
55 return Err(syn::Error::new_spanned(
56 &name,
57 "constructor name must match `[A-Za-z_][0-9A-Za-z_]*`",
58 ));
59 }
60
61 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
62
63 let mut de_generics = input.generics.clone();
65 de_generics
66 .params
67 .insert(0, syn::parse_quote!('de));
68 let (de_impl_generics, _, _) = de_generics.split_for_impl();
69
70 Ok(quote! {
71 impl #impl_generics ::jsonx::JsonxConstructor for #ident #ty_generics #where_clause {
72 const TOKEN: &'static str = ::jsonx::ctor!(#name);
73
74 fn to_jsonx_arg(&self) -> ::std::string::String {
75 ::std::string::ToString::to_string(self)
76 }
77
78 fn from_jsonx_arg(arg: &str) -> ::std::result::Result<Self, ::std::string::String> {
79 <Self as ::std::str::FromStr>::from_str(arg)
80 .map_err(|e| ::std::string::ToString::to_string(&e))
81 }
82 }
83
84 impl #impl_generics ::jsonx::__derive::serde::Serialize for #ident #ty_generics #where_clause {
85 fn serialize<__S>(&self, serializer: __S) -> ::std::result::Result<__S::Ok, __S::Error>
86 where
87 __S: ::jsonx::__derive::serde::Serializer,
88 {
89 ::jsonx::constructor::serialize(self, serializer)
90 }
91 }
92
93 impl #de_impl_generics ::jsonx::__derive::serde::Deserialize<'de> for #ident #ty_generics #where_clause {
94 fn deserialize<__D>(deserializer: __D) -> ::std::result::Result<Self, __D::Error>
95 where
96 __D: ::jsonx::__derive::serde::Deserializer<'de>,
97 {
98 ::jsonx::constructor::deserialize(deserializer)
99 }
100 }
101 })
102}
103
104fn is_valid_ctor_name(name: &str) -> bool {
108 let mut bytes = name.bytes();
109 match bytes.next() {
110 Some(b) if b.is_ascii_alphabetic() || b == b'_' => {}
111 _ => return false,
112 }
113 bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_')
114}