tlkit_expand/structure/
convert.rs

1use std::collections::HashMap;
2
3use procmeta::prelude::*;
4use quote::{format_ident, ToTokens};
5use syn::{braced, parse::Parse, DeriveInput};
6
7use crate::structure::get_fn_name_suffix;
8
9pub struct TargetPlugFields {
10    pub items: Vec<PlugField>,
11}
12
13pub struct PlugField {
14    pub ident: Ident,
15    pub value: Expr,
16}
17
18impl Parse for TargetPlugFields {
19    fn parse(input: ParseStream) -> Result<Self> {
20        let content;
21        let _ = braced!(content in input);
22        let mut items = vec![];
23        while !content.is_empty() {
24            let ident: Ident = content.parse()?;
25            let _: Token![:] = content.parse()?;
26            let value: Expr = content.parse()?;
27            if !content.is_empty() {
28                let _: Token![,] = content.parse()?;
29            }
30            items.push(PlugField { ident, value })
31        }
32        Ok(TargetPlugFields { items })
33    }
34}
35
36impl Converter<Self> for TargetPlugFields {
37    fn into(self) -> Result<Self> {
38        Ok(self)
39    }
40}
41
42#[derive(MetaParser)]
43pub enum ParsedTypeAttr {
44    #[name("from")]
45    From { source: Type, adaptor: Option<Type> },
46
47    #[name("into")]
48    Into {
49        target: Type,
50
51        adaptor: Option<Type>,
52
53        #[converter(TargetPlugFields)]
54        plug: Option<TargetPlugFields>,
55    },
56}
57
58#[derive(MetaParser)]
59pub enum ParsedFieldAttr {
60    #[name("from")]
61    From {
62        source: Option<Type>,
63        map: Option<Ident>,
64        assign: Option<Expr>,
65    },
66
67    #[name("into")]
68    Into {
69        target: Option<Type>,
70        map: Option<Ident>,
71        assign: Option<Expr>,
72        skip: Option<bool>,
73    },
74}
75
76/// pub fn from_xxx(source: Source, adaptor: Adaptor) -> Self {
77///     Self {
78///         xxxx
79///     }
80/// }
81pub struct FromItemContent {
82    pub adaptor: TokenStream,
83
84    pub source: Type,
85
86    pub fields: TokenStream,
87}
88
89impl FromItemContent {
90    pub fn new(source: Type, adaptor: Option<Type>) -> Self {
91        Self {
92            adaptor: adaptor.map(|t| quote!(, adaptor: #t)).unwrap_or_default(),
93            source,
94            fields: quote!(),
95        }
96    }
97
98    pub fn add_field(&mut self, ident: &Option<Ident>, map: Option<Ident>, assign: Option<Expr>) {
99        let mut value = quote!(source.#ident);
100        if let Some(map) = map {
101            value = quote!(source.#map);
102        }
103        if let Some(assign) = assign {
104            value = quote!(#assign);
105        }
106        let last_fields_token = &self.fields;
107        self.fields = quote! {
108            #last_fields_token
109            #ident: #value,
110        }
111    }
112}
113
114impl From<FromItemContent> for TokenStream {
115    fn from(value: FromItemContent) -> Self {
116        let source = value.source;
117        let fn_name = format_ident!(
118            "from{}",
119            get_fn_name_suffix(source.to_token_stream().to_string())
120        );
121        let adaptor = value.adaptor;
122        let fields = value.fields;
123        quote! {
124            pub fn #fn_name (source: #source #adaptor) -> Self {
125                Self {
126                    #fields
127                }
128            }
129        }
130    }
131}
132
133///
134/// pub fn into_xxx(self, adaptor) -> T {
135///     T {
136///         xxxx
137///     }
138/// }
139///
140///
141pub struct IntoItemContent {
142    pub adaptor: TokenStream,
143
144    pub target: Type,
145
146    pub fields: TokenStream,
147}
148
149impl IntoItemContent {
150    pub fn new(target: Type, adaptor: Option<Type>, plug: Option<TargetPlugFields>) -> Self {
151        Self {
152            adaptor: adaptor.map(|t| quote!(, adaptor: #t)).unwrap_or_default(),
153            target,
154            fields: plug
155                .map(|t| {
156                    let mut fields = quote!();
157                    for item in t.items {
158                        let ident = item.ident;
159                        let value = item.value;
160                        fields = quote! {
161                            #fields
162                            #ident: #value,
163                        };
164                    }
165                    fields
166                })
167                .unwrap_or_default(),
168        }
169    }
170
171    pub fn add_field(
172        &mut self,
173        ident: &Option<Ident>,
174        map: Option<Ident>,
175        assign: Option<Expr>,
176        skip: Option<bool>,
177    ) {
178        let skip = skip.unwrap_or(false);
179        if skip {
180            return;
181        }
182        let mut ident_token = quote!(#ident);
183        if let Some(map) = map {
184            ident_token = quote!(#map);
185        }
186        let mut value = quote!(self.#ident);
187        if let Some(assign) = assign {
188            value = quote!(#assign);
189        }
190        let last_fields_token = &self.fields;
191        self.fields = quote! {
192            #last_fields_token
193            #ident_token: #value,
194        }
195    }
196}
197
198impl From<IntoItemContent> for TokenStream {
199    fn from(value: IntoItemContent) -> Self {
200        let target = value.target;
201        let fn_name = format_ident!(
202            "into{}",
203            get_fn_name_suffix(target.to_token_stream().to_string())
204        );
205        let adaptor = value.adaptor;
206        let fields = value.fields;
207        quote! {
208            pub fn #fn_name (self #adaptor) -> #target {
209                #target {
210                    #fields
211                }
212            }
213        }
214    }
215}
216
217pub fn expand(input: DeriveInput) -> Result<TokenStream> {
218    let ty = &input.ident;
219    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
220    match input.data {
221        syn::Data::Struct(data) => {
222            let mut first_source_name = String::default();
223            let mut from_items: HashMap<String, FromItemContent> = HashMap::new();
224
225            let mut first_target_name = String::default();
226            let mut into_items: HashMap<String, IntoItemContent> = HashMap::new();
227
228            let mut result = quote!();
229            for attr in input.attrs {
230                let parsed_attr = <ParsedTypeAttr as MetaParser>::parse(&attr.meta)?;
231                match parsed_attr {
232                    ParsedTypeAttr::From { source, adaptor } => {
233                        let key = source.to_token_stream().to_string();
234                        if first_source_name.is_empty() {
235                            first_source_name = key.clone();
236                        }
237                        let content = FromItemContent::new(source, adaptor);
238                        from_items.insert(key, content);
239                    }
240                    ParsedTypeAttr::Into {
241                        target,
242                        adaptor,
243                        plug,
244                    } => {
245                        let key = target.to_token_stream().to_string();
246                        if first_target_name.is_empty() {
247                            first_target_name = key.clone();
248                        }
249                        let content = IntoItemContent::new(target, adaptor, plug);
250                        into_items.insert(key, content);
251                    }
252                }
253            }
254            for field in data.fields {
255                if field.attrs.is_empty() {
256                    for v in from_items.values_mut() {
257                        v.add_field(&field.ident, None, None);
258                    }
259                    for v in into_items.values_mut() {
260                        v.add_field(&field.ident, None, None, None);
261                    }
262                    continue;
263                }
264                for attr in &field.attrs {
265                    let parsed_attr = <ParsedFieldAttr as MetaParser>::parse(&attr.meta)?;
266                    match parsed_attr {
267                        ParsedFieldAttr::From {
268                            source,
269                            map,
270                            assign,
271                        } => {
272                            let key = source
273                                .map(|s| s.to_token_stream().to_string())
274                                .unwrap_or(first_source_name.clone());
275                            let from_item_content = from_items.get_mut(&key);
276                            let from_item_content = from_item_content.ok_or(Error::new(
277                                field.span(),
278                                "expected already define source type",
279                            ))?;
280                            from_item_content.add_field(&field.ident, map, assign);
281                        }
282                        ParsedFieldAttr::Into {
283                            target,
284                            map,
285                            assign,
286                            skip,
287                        } => {
288                            let key = target
289                                .map(|s| s.to_token_stream().to_string())
290                                .unwrap_or(first_target_name.clone());
291                            let into_item_content = into_items.get_mut(&key);
292                            let into_item_content = into_item_content.ok_or(Error::new(
293                                field.span(),
294                                "expected already define target type",
295                            ))?;
296                            into_item_content.add_field(&field.ident, map, assign, skip);
297                        }
298                    }
299                }
300            }
301            for v in from_items.into_values() {
302                let item_token: TokenStream = TokenStream::from(v);
303                result = quote! {
304                    #result
305                    #item_token
306                }
307            }
308            for v in into_items.into_values() {
309                let item_token: TokenStream = TokenStream::from(v);
310                result = quote! {
311                    #result
312                    #item_token
313                }
314            }
315            result = quote! {
316                impl #impl_generics #ty #ty_generics #where_clause  {
317                    #result
318                }
319            };
320            Ok(result)
321        }
322        _ => unimplemented!(),
323    }
324}