into_from/
lib.rs

1use proc_macro::TokenStream;
2use syn::{parse::Parse, parse::ParseStream, parse_macro_input, Result as SynResult, Token};
3
4// 定义解析 into 属性参数的结构体
5struct IntoArgs {
6    target_type: syn::Type,
7    use_default: bool,
8}
9
10impl Parse for IntoArgs {
11    fn parse(input: ParseStream) -> SynResult<Self> {
12        let target_type = input.parse()?;
13        let use_default = if input.peek(Token![,]) {
14            input.parse::<Token![,]>()?;
15            input.parse::<syn::Ident>()? == "default"
16        } else {
17            false
18        };
19        Ok(IntoArgs {
20            target_type,
21            use_default,
22        })
23    }
24}
25
26/// impl Into<T> for Struct
27///
28/// ```rust no_run
29/// #[derive(Debug, Default)]
30/// struct Foo {
31///     field1: i32,
32///     field3: String,
33/// }
34///
35/// #[into(Foo)]  // no default
36/// //#[into(Foo, default)]  // with default field
37/// struct Bar {
38///     field1: i32,
39///     #[into_skip]
40///     field2: String,
41///     #[into(self.field3.to_string())]
42///     field3: i32,
43/// }
44/// ```
45#[proc_macro_attribute]
46pub fn into(args: TokenStream, input: TokenStream) -> TokenStream {
47    let args = parse_macro_input!(args as IntoArgs);
48    let target = args.target_type;
49    let use_default = args.use_default;
50    let mut input = parse_macro_input!(input as syn::ItemStruct);
51
52    // 获取源结构体字段, 生成字段转换
53    let field_conversions = match &input.fields {
54        syn::Fields::Named(fields) => fields
55            .named
56            .iter()
57            .filter(|field| !is_skip(field, "into_skip"))
58            .map(|field| {
59                let name = &field.ident;
60                field
61                    .attrs
62                    .iter()
63                    .find(|attr| attr.path().is_ident("into"))
64                    .map(|attr| {
65                        let value = attr.parse_args::<syn::Expr>().unwrap();
66                        quote::quote!(#name: #value)
67                    })
68                    .unwrap_or_else(|| quote::quote!(#name: self.#name))
69            })
70            .collect::<Vec<_>>(),
71        _ => panic!("Only named fields are supported"),
72    };
73
74    // 去掉 #[into_skip] 属性, 否则会报错
75    match &mut input.fields {
76        syn::Fields::Named(fields) => {
77            fields.named.iter_mut().for_each(|field| {
78                field.attrs.retain(|attr| {
79                    !attr.path().is_ident("into_skip") && !attr.path().is_ident("into")
80                });
81            });
82        },
83        _ => panic!("Only named fields are supported"),
84    }
85
86    let name = &input.ident;
87
88    let struct_init = if use_default {
89        quote::quote! {
90            #target {
91                #(#field_conversions,)*
92                ..Default::default()
93            }
94        }
95    } else {
96        quote::quote! {
97            #target {
98                #(#field_conversions,)*
99            }
100        }
101    };
102
103    let gen = quote::quote! {
104        #input
105
106        impl Into<#target> for #name {
107            fn into(self) -> #target {
108                #struct_init
109            }
110        }
111    };
112
113    gen.into()
114}
115
116fn is_skip(field: &syn::Field, name: &str) -> bool {
117    // 跳过含有指定属性的字段
118    field.attrs.iter().any(|attr| attr.path().is_ident(name))
119}
120
121/// impl From<T> for Struct
122///
123/// ```rust no_run
124/// struct Foo {
125///     field1: i32,
126///     field2: String,
127/// }
128///
129/// #[from(Foo)]
130/// struct Bar {
131///     field1: i32,
132///     #[from(source.field2.parse::<i32>().unwrap())]
133///     field3: i32,
134/// }
135///
136/// ```
137#[proc_macro_attribute]
138pub fn from(args: TokenStream, input: TokenStream) -> TokenStream {
139    // 解析目标类型参数
140    let target = parse_macro_input!(args as syn::Type);
141    let mut input = parse_macro_input!(input as syn::ItemStruct);
142
143    // 获取源结构体字段, 生成字段转换
144    let field_conversions = match &input.fields {
145        syn::Fields::Named(fields) => fields
146            .named
147            .iter()
148            .map(|field| {
149                let name = &field.ident;
150                // 如果字段有 #[from] 属性, 则使用该属性值的表达式作为value
151                field
152                    .attrs
153                    .iter()
154                    .find(|attr| attr.path().is_ident("from"))
155                    .map(|a| {
156                        let value = a.parse_args::<syn::Expr>().unwrap();
157                        quote::quote!(#name: #value)
158                    })
159                    .unwrap_or(quote::quote!(#name: source.#name))
160                // quote::quote! { #name: source.#name}
161            })
162            .collect::<Vec<_>>(),
163        _ => panic!("Only named fields are supported"),
164    };
165
166    // 去掉 #[from] 属性, 否则会报错
167    match &mut input.fields {
168        syn::Fields::Named(fields) => {
169            fields.named.iter_mut().for_each(|field| {
170                field.attrs.retain(|attr| !attr.path().is_ident("from"));
171            });
172        },
173        _ => panic!("Only named fields are supported"),
174    }
175
176    let name = &input.ident;
177    // let vis = &input.vis;
178    // let (_impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
179
180    let gen = quote::quote! {
181        #input
182
183        impl From<#target> for #name {
184            fn from(source: #target) -> Self {
185                #name {
186                    #(#field_conversions,)*
187                }
188            }
189        }
190    };
191
192    gen.into()
193}