struct_auto_from/
lib.rs

1use std::collections::HashMap;
2
3use darling::FromField;
4use proc_macro::TokenStream;
5use proc_macro2::{Ident, Span};
6use quote::quote;
7use syn::{parse_macro_input, Expr, Field, ItemStruct, Meta, MetaList};
8
9/// Auto implement `From` trait.
10///
11/// When specifying conversion, all fields in the receiving struct type
12/// must either be defined in the sender, or have their default values
13/// defined on the receiver.
14///
15/// Default value attribute lets you override data from sender.
16///
17/// <br>
18///
19/// # Examples
20/// ```
21/// use struct_auto_from::auto_from;
22///
23/// #[derive(Clone)]
24/// struct Model1 {
25///     id: i32,
26///     name: String,
27///     attrs: Vec<String>,
28/// }
29///
30/// #[auto_from(Model1)]
31/// struct Model2 {
32///     id: i32,
33///     name: String,
34///     attrs: Vec<String>,
35/// }
36///
37///
38/// let m1: Model1 = Model1 {
39///     id: 0,
40///     name: "M".into(),
41///     attrs: vec![],
42/// };
43/// let m2: Model2 = m1.clone().into();
44///
45/// assert_eq!(m1.id, m2.id);
46/// assert_eq!(m1.name, m2.name);
47/// assert_eq!(m1.attrs, m2.attrs);
48/// ```
49///
50/// Using the default values
51///
52/// ```
53/// use std::collections::HashMap;
54/// use struct_auto_from::auto_from;
55///
56/// #[derive(Clone)]
57/// struct Model1 {
58///     id: i32,
59///     name: String,
60///     attrs: Vec<String>,
61/// }
62///
63/// #[auto_from(Model1)]
64/// struct Model2 {
65///     #[auto_from_attr(default_value = -1)]
66///     id: i32,
67///     name: String,
68///     attrs: Vec<String>,
69///     #[auto_from_attr(default_value = Default::default())]
70///     metadata: HashMap<String, String>,
71/// }
72///
73///
74/// let m1: Model1 = Model1 {
75///     id: 0,
76///     name: "M".into(),
77///     attrs: vec![],
78/// };
79/// let m2: Model2 = m1.clone().into();
80///
81/// assert_eq!(-1, m2.id);
82/// assert_eq!(m1.name, m2.name);
83/// assert_eq!(m1.attrs, m2.attrs);
84/// assert!(m2.metadata.is_empty());
85/// ```
86///
87#[proc_macro_attribute]
88pub fn auto_from(attrs: TokenStream, input: TokenStream) -> TokenStream {
89    let from = parse_macro_input!(attrs as Ident);
90
91    let into = parse_macro_input!(input as ItemStruct);
92    let ImplData {
93        raw_into,
94        into,
95        fields,
96        default_fields,
97        default_values,
98    } = ImplData::from_parsed_input(into);
99
100    let tokens = quote! {
101        #raw_into
102
103        impl From<#from> for #into {
104            fn from(value: #from) -> Self {
105                Self {
106                    #(
107                        #fields: value.#fields
108                    ),*
109                    ,
110                    #(
111                        #default_fields: #default_values
112                    ),*
113                }
114            }
115        }
116    };
117
118    tokens.into()
119}
120
121struct ImplData {
122    raw_into: ItemStruct,
123    into: Ident,
124    fields: Vec<Ident>,
125    default_fields: Vec<Ident>,
126    default_values: Vec<Expr>,
127}
128
129impl ImplData {
130    fn from_parsed_input(input: ItemStruct) -> Self {
131        let mut raw_into = input.clone();
132        let into = input.ident;
133        let (default_fields, default_values): (Vec<_>, Vec<_>) =
134            Self::extract_defaults_from_input(&mut raw_into)
135                .into_iter()
136                .unzip();
137        let fields = input
138            .fields
139            .into_iter()
140            .filter_map(|f| f.ident)
141            .filter(|i| !default_fields.contains(i))
142            .collect();
143
144        Self {
145            raw_into,
146            into,
147            fields,
148            default_fields,
149            default_values,
150        }
151    }
152
153    fn extract_defaults_from_input(input: &mut ItemStruct) -> HashMap<Ident, Expr> {
154        let mut defaults = HashMap::new();
155
156        for field in input.fields.iter_mut() {
157            let attrs = AutoFromAttr::from_field(field).unwrap();
158
159            if let (Some(ident), Some(default_value)) = (&mut field.ident, attrs.default_value) {
160                defaults.insert(ident.clone(), default_value);
161                Self::remove_attrs(field);
162            }
163        }
164
165        defaults
166    }
167
168    fn remove_attrs(field: &mut Field) {
169        field.attrs.retain(|a| {
170            let Meta::List(MetaList { path, .. }) = &a.meta else {
171                return false
172            };
173
174            !path.is_ident(&Ident::new("auto_from_attr", Span::call_site()))
175        })
176    }
177}
178
179#[derive(FromField, Default, Debug)]
180#[darling(default, attributes(auto_from_attr))]
181struct AutoFromAttr {
182    default_value: Option<Expr>,
183}