Skip to main content

qust_derive/
lib.rs

1#![allow(unused_imports)]
2use std::env::args;
3
4use proc_macro::{self, TokenStream as ts1};
5use quote::{quote, quote_spanned, format_ident};
6use syn::{Attribute, ItemFn, Meta, MetaNameValue, ReturnType, TraitItem};
7use syn::{
8    parse_macro_input, DeriveInput, Generics, ImplItem, Data, Fields, 
9    spanned::Spanned, Index, Ident, GenericParam, parse_quote, ItemImpl, token::Trait, ItemTrait
10};
11use proc_macro2::TokenStream as ts2;
12use synstructure::{AddBounds, Structure};
13
14fn derive_as_ref(mut structure: Structure<'_>) -> ts2 {
15    structure
16        .underscore_const(true)
17        .add_bounds(AddBounds::None)
18        .gen_impl(quote! {
19            gen impl ::core::convert::AsRef<Self> for @Self {
20                fn as_ref(&self) -> &Self {
21                    &self
22                }
23            }
24        })
25}
26synstructure::decl_derive!([AsRef] => derive_as_ref);
27
28
29#[proc_macro_attribute]
30pub fn ta_derive(_metadata: ts1, input: ts1) -> ts1 {
31    let input_token: ts2 = input.into();
32    let output = quote! {
33        #[derive(Debug, Clone, Serialize, Deserialize, AsRef)]
34        #input_token
35    };
36    output.into()
37}
38
39#[proc_macro_attribute]
40pub fn ta_derive2(_metadata: ts1, input: ts1) -> ts1 {
41    let input_token: ts2 = input.into();
42    let output = quote! {
43        #[derive(PathDebug, Clone, Serialize, Deserialize, AsRef)]
44        #input_token
45    };
46    output.into()
47}
48
49#[proc_macro_attribute]
50pub fn clone_trait(_metadata: ts1, input: ts1) -> ts1 {
51    let input_token: ItemTrait = syn::parse(input).unwrap();
52    let name = input_token.ident.clone();
53    let name_str = name.to_string();
54    let name_to_box = format_ident!("{}_box", name_str.to_lowercase());
55    let type_ident = format_ident!("{}Box", name_str);
56    let visi = input_token.vis;
57    let item = input_token.items.into_iter();
58    let attrs = input_token.attrs.into_iter();
59    let gener = input_token.generics;
60    let output = quote! {
61        #[typetag::serde(tag = #name_str)]
62        #(#attrs)*
63        #visi trait #name #gener: DynClone + Send + Sync + std::fmt::Debug + 'static {
64            #(#item)*
65            fn #name_to_box(&self) -> Box<dyn #name>
66            where
67            Self: Clone,
68            {
69                Box::new(self.clone())
70            }
71        }
72        clone_trait_object!(#gener #name #gener);
73        impl PartialEq for Box<dyn #name> {
74            fn eq(&self, other: &Self) -> bool {
75                format!("{:?}", self) == format!("{:?}", other)
76            }
77        }
78        pub type #type_ident = Box<dyn #name>;
79    };
80    proc_macro::TokenStream::from(output)
81}
82
83// #[proc_macro_attribute]
84// pub fn typetag_name(_metadata: ts1, input: ts1) -> ts1 {
85//     let module_name = module_path!();
86//     let mut input_token: ItemImpl = syn::parse(input).unwrap();
87//     input_token.items.push(parse_quote! {
88//         #[doc(hidden)]
89//         fn typetag_name(&self) -> &'static str {
90//             #name
91//         }
92//     });
93//     let mut name_str = String::default();
94//     for t in input_token.items.iter() {
95//         if let ImplItem::Type(tz) = t {
96//             name_str = tz.ident.to_string();
97//         }
98//     }
99//     let tag_name = format!("{}::{:?}", module_name, name_str);
100//     quote! {
101//         #[typetag::serde(name = #tag_name)]
102//         #input_token
103//     }.into()
104// }
105
106// fn augment_impl(input: &mut ItemImpl, name: &ts2) {
107//     input.items.push(parse_quote! {
108//         #[doc(hidden)]
109//         fn typetag_name(&self) -> &'static str {
110//             #name
111//         }
112//     });
113//     input.items.push(parse_quote! {
114//         #[doc(hidden)]
115//         fn typetag_deserialize(&self) {}
116//     });
117// }
118
119#[proc_macro_derive(PathDebug)]
120pub fn path_debug(input: ts1) -> ts1 {
121    let ast: DeriveInput = syn::parse(input).unwrap();
122    let name = ast.ident.clone();
123    let b = get_fields_name(&ast.ident, &ast.data);
124    let generics = add_trait_bounds(ast.generics);
125    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
126    let output = quote! {
127        impl #impl_generics std::fmt::Debug for #name #ty_generics #where_clause {
128            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129                f
130                #b
131                .finish()
132            }
133        }
134
135    };
136    proc_macro::TokenStream::from(output)
137}
138
139
140fn add_trait_bounds(mut generics: Generics) -> Generics {
141    for param in &mut generics.params {
142        if let GenericParam::Type(ref mut type_param) = *param {
143            type_param.bounds.push(parse_quote!(std::fmt::Debug));
144        }
145    }
146    generics
147}
148
149fn get_fields_name(type_name: &Ident, data: &Data) -> ts2 {
150    let type_string = type_name.to_string();
151    match *data {
152        Data::Struct(ref data) => {
153            match data.fields {
154                Fields::Named(ref fields) => {
155                    let recurse = fields.named.iter().map(|f| {
156                        let name = &f.ident;
157                        let name_string = name.as_ref().map_or_else(String::default, |v| v.to_string());
158                        quote! { field(#name_string, &self.#name) }
159                    });
160                    quote! {
161                        .debug_struct(&format!("{}::{}", module_path!().split("::").last().unwrap(), #type_string))
162                        #(.#recurse)*
163                    }
164                }
165                Fields::Unnamed(ref fields) => {
166                    let recurse = fields.unnamed.iter().enumerate().map(|(i, _f)| {
167                        let index = Index::from(i);
168                        quote! { field(&self.#index) }
169                    });
170                    quote! {
171                        .debug_tuple(&format!("{}::{}", module_path!().split("::").last().unwrap(), #type_string))
172                        #(.#recurse)*
173                    }
174                }
175                Fields::Unit => {
176                    quote! {
177                        .debug_struct(&format!("{}::{}", module_path!().split("::").last().unwrap(), #type_string))
178                    }
179                }
180            }
181        }
182        Data::Enum(_) | Data::Union(_) => unimplemented!(),
183    }
184}
185
186#[proc_macro_attribute]
187pub fn lazy_init(attr: ts1, item: ts1) -> ts1 {
188    let input = parse_macro_input!(item as ItemTrait);
189    let di_expr = parse_macro_input!(attr as syn::Expr);
190
191    let trait_name = &input.ident;
192    let mut methods = quote!{};
193
194    for item in input.items.clone() {
195        if let TraitItem::Fn(trait_item_fn) = item {
196            let fn_name = trait_item_fn.sig.ident;
197            let fn_name_lazy = format_ident!("{}_lazy", fn_name);
198            let ReturnType::Type(_, return_type) = trait_item_fn.sig.output else { panic!("return type") };
199            methods.extend(quote! {
200                fn #fn_name_lazy(&self) -> #return_type {
201                    let mut cond_fn: Option<#return_type> = None;
202                    Box::new(move |data| {
203                        cond_fn.get_or_insert_with(|| self.#fn_name(#di_expr))(data)
204                    })
205                }
206            });
207            break;
208        }
209    }
210
211    let items = &input.items;
212    let attrs = &input.attrs;
213
214    let expanded = quote! {
215        #(#attrs)*
216        pub trait #trait_name {
217            #(#items)*
218            #methods
219        }
220    };
221    ts1::from(expanded)
222}