1#![doc = r#"The macros here should auto generate several traits and the major TildeAble trait.
2
3For example:
4
5```rust
6#[derive(Debug, PartialEq, TildeAble)]
7pub enum TildeKind {
8 /// ~C ~:C
9 #[implTo(char)]
10 Char,
11
12 /// ~$ ~5$ ~f
13 #[implTo(float)]
14 Float(Option<String>),
15
16 /// ~d ~:d ~:@d
17 Digit(Option<String>),
18
19 /// ~a
20 #[implTo(float, char, String)]
21 Va,
22
23 /// loop
24 Loop(Vec<Tilde>),
25
26 /// text inside the tilde
27 Text(String),
28
29 /// vec
30 VecTilde(Vec<Tilde>),
31}
32```
33
34Will generate:
35
36```rust
37/// all default method is return none.
38trait TildeAble {
39 fn len(&self) -> usize;
40 fn into_tildekind_char(&self) -> Option<&dyn TildeKindChar>{None}
41 fn into_tildekind_va(&self) -> Option<&dyn TildeKindVa>{None}
42 // and all other fields...
43}
44
45impl TildeAble for char {
46 fn into_tildekind_char(&self) -> Option<&dyn TildeKindChar> {
47 Some(self)
48 }
49
50 fn into_tildekind_va(&self) -> Option<&dyn TildeKindVa> {
51 Some(self)
52 }
53}
54
55impl TildeAble for float {
56 fn into_tildekind_va(&self) -> Option<&dyn TildeKindVa> {
57 Some(self)
58 }
59}
60
61impl TildeAble for String {
62 fn into_tildekind_va(&self) -> Option<&dyn TildeKindVa> {
63 Some(self)
64 }
65}
66
67trait TildeKindChar {
68 fn format(&self, tkind: &TildeKind, buf: &mut String) -> Result<(), TildeError> {
69 Err("un-implenmented yet".into())
70 }
71}
72
73trait TildeKindVa {
74 fn format(&self, tkind: &TildeKind, buf: &mut String) -> Result<(), TildeError> {
75 Err("un-implenmented yet".into())
76 }
77}
78
79```
80"#]
81
82use std::{collections::HashMap, error::Error};
83
84use proc_macro::TokenStream;
85use proc_macro2::{Ident, Literal, Span};
86use quote::quote;
87use syn::{parse_macro_input, spanned::Spanned, Attribute, Data, DataEnum, DeriveInput, Variant};
88
89#[proc_macro_derive(TildeAble, attributes(implTo))]
90pub fn derive_tilde_able(input: TokenStream) -> TokenStream {
91 let input = parse_macro_input!(input as DeriveInput);
92
93 let mut return_types_traits = vec![];
94 let mut all_default_methods = vec![];
95 let mut types_impl_methods = HashMap::new();
96
97 match input.data {
98 Data::Enum(DataEnum { ref variants, .. }) => {
99 let all_vars = variants.iter().map(|var| parse_variant_attrs(var));
100
101 all_vars.for_each(|(field, tys)| {
102 let fname = Ident::new(
103 &(String::from("into_tildekind_") + &field.to_lowercase()),
104 Span::call_site(),
105 );
106
107 let return_type =
108 Ident::new(&(String::from("TildeKind") + &field), Span::call_site());
109
110 all_default_methods
112 .push(quote! {
113 fn #fname(&self) -> Option<&dyn #return_type> {
114 None
115 }});
116
117 tys.for_each(|ty| {
119 let en = types_impl_methods.entry(ty).or_insert(vec![]);
120 en.push(quote! {fn #fname(&self) -> Option<&dyn #return_type> {
121 Some(self)
122 }})
123 });
124
125 let doc = Literal::string(&(return_type.to_string() + " is the trait that contains implementation of type for TildeKind::"+ &field + ".\n\nGenerated by cl-format-macro"));
127 return_types_traits.push(quote! {
128 #[doc = #doc]
129 pub trait #return_type: Debug {
130 fn format(&self, tkind: &TildeKind, buf: &mut String) -> Result<(), TildeError> {
131 Err(TildeError::new(ErrorKind::EmptyImplenmentError, "haven't implenmented yet").into(),)
132 }
133 }})
134 });
135 }
136 _ => panic!("only support the enum"),
137 };
138
139 let mut result = vec![];
140
141 let tilde_able_trait = quote! {
143 pub trait TildeAble:Debug {
147 fn len(&self) -> usize;
148 #(#all_default_methods)*
149 }
150 };
151
152 let mut auto_impl_for_types = types_impl_methods
153 .iter()
154 .map(|(ty, methods)| {
155 quote! {
156 impl TildeAble for #ty {
157 fn len(&self) -> usize {
158 1
159 }
160 #(#methods)*
161 }
162 }
163 })
164 .collect();
165
166 result.push(tilde_able_trait);
168 result.append(&mut auto_impl_for_types);
169 result.append(&mut return_types_traits);
170
171 proc_macro2::TokenStream::from_iter(result.into_iter()).into()
172}
173
174fn parse_variant_attrs(variant: &Variant) -> (String, impl Iterator<Item = Ident> + '_) {
176 let all_impl_to_type = variant
177 .attrs
178 .iter()
179 .filter(|attr| attr.path().get_ident().map(|d| d.to_string()) == Some("implTo".to_string()))
180 .map(|attr| get_types_impl_to(attr).unwrap())
181 .flatten();
182
183 let field = variant.ident.to_string();
184
185 (field.clone(), all_impl_to_type)
186}
187
188fn get_types_impl_to(attribute: &Attribute) -> Result<impl Iterator<Item = Ident>, Box<dyn Error>> {
190 let mut result = vec![];
191 attribute.parse_nested_meta(|meta| {
192 result.push(
193 meta.path
194 .get_ident()
195 .ok_or(syn::Error::new(meta.path.span(), "get_ident issue"))?
196 .clone(),
197 );
198 Ok(())
199 })?;
200
201 Ok(result.into_iter())
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use syn::parse_quote;
208
209 #[test]
210 fn test_get_types_impl_to() -> Result<(), Box<dyn Error>> {
211 let test_case: Attribute = parse_quote! {
212 #[implTo(a,b,c,d)]
213 };
214
215 assert_eq!(
217 vec!["a", "b", "c", "d"]
218 .into_iter()
219 .map(|s| s.to_string())
220 .collect::<Vec<String>>(),
221 get_types_impl_to(&test_case)
222 .unwrap()
223 .into_iter()
224 .map(|x| x.to_string())
225 .collect::<Vec<String>>()
226 );
227
228 let test_case: Attribute = parse_quote! {
229 #[implTo(a)]
230 };
231
232 assert_eq!(
234 vec!["a"]
235 .into_iter()
236 .map(|s| s.to_string())
237 .collect::<Vec<String>>(),
238 get_types_impl_to(&test_case)
239 .unwrap()
240 .into_iter()
241 .map(|x| x.to_string())
242 .collect::<Vec<String>>()
243 );
244
245 Ok(())
246 }
247
248 #[test]
249 fn test_parse_variant_attrs() -> Result<(), Box<dyn Error>> {
250 let test_case: Variant = parse_quote! {
251 #[implTo(a,b,c,d)]
252 A
253 };
254
255 let result = parse_variant_attrs(&test_case);
257 assert_eq!(result.0, "A");
258 assert_eq!(
259 result.1.map(|i| i.to_string()).collect::<Vec<_>>(),
260 vec!["a", "b", "c", "d"]
261 .into_iter()
262 .map(|s| s.to_string())
263 .collect::<Vec<String>>(),
264 );
265
266 let test_case: Variant = parse_quote! {
268 B
269 };
270
271 let mut result = parse_variant_attrs(&test_case);
273 assert_eq!(result.0, "B");
274 assert_eq!(result.1.next(), None);
275
276 Ok(())
277 }
278
279 #[test]
280 fn test_args_picker() -> Result<(), Box<dyn Error>> {
281 Ok(())
290 }
291}