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
76pub 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
133pub 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}