proc_macro_roids/
fields_named_append.rs

1use syn::{DeriveInput, Fields, FieldsNamed};
2
3use crate::DeriveInputStructExt;
4
5const ERR_MUST_BE_UNIT_OR_NAMED: &str = "Macro must be used on either a unit struct or a struct with named fields.\n\
6     This derive does not work on tuple structs.";
7
8/// Indicates this type may have `FieldsNamed` appended to it.
9pub trait FieldsNamedAppend {
10    /// Appends the specified `fields_named` to this type.
11    fn append_named(&mut self, fields_named: FieldsNamed);
12}
13
14impl FieldsNamedAppend for DeriveInput {
15    fn append_named(&mut self, fields_named: FieldsNamed) {
16        self.fields_mut().append_named(fields_named);
17        self.data_struct_mut().semi_token = None;
18    }
19}
20
21impl FieldsNamedAppend for Fields {
22    fn append_named(&mut self, fields_named: FieldsNamed) {
23        match self {
24            Fields::Named(self_fields_named) => self_fields_named.append_named(fields_named),
25            Fields::Unit => *self = Fields::from(fields_named),
26            Fields::Unnamed(_) => panic!("{}", ERR_MUST_BE_UNIT_OR_NAMED),
27        }
28    }
29}
30
31impl FieldsNamedAppend for FieldsNamed {
32    fn append_named(&mut self, fields_named: FieldsNamed) {
33        self.named.extend(fields_named.named);
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use syn::{parse_quote, DeriveInput, Fields, FieldsNamed};
40
41    use super::FieldsNamedAppend;
42
43    #[test]
44    fn append_fields_named_to_fields_named() {
45        let mut fields: FieldsNamed = parse_quote!({ a: u32, b: i32 });
46        let fields_additional: FieldsNamed = parse_quote!({ c: i64, d: usize });
47        let fields_expected: FieldsNamed = parse_quote!({ a: u32, b: i32, c: i64, d: usize });
48
49        fields.append_named(fields_additional);
50
51        assert_eq!(fields_expected, fields);
52    }
53
54    #[test]
55    fn append_fields_named_to_fields_unit() {
56        let mut fields = Fields::Unit;
57        let fields_additional: FieldsNamed = parse_quote!({ c: i64, d: usize });
58        let fields_expected: Fields = Fields::Named(parse_quote!({ c: i64, d: usize }));
59
60        fields.append_named(fields_additional);
61
62        assert_eq!(fields_expected, fields);
63    }
64
65    #[test]
66    #[should_panic(
67        expected = "Macro must be used on either a unit struct or a struct with named fields.\n\
68                    This derive does not work on tuple structs."
69    )]
70    fn append_fields_named_to_fields_unnamed_panics() {
71        let mut fields: Fields = Fields::Unnamed(parse_quote!((u32, i32)));
72        let fields_additional: FieldsNamed = parse_quote!({ c: i64, d: usize });
73
74        fields.append_named(fields_additional);
75    }
76
77    #[test]
78    fn append_fields_named_to_struct_named() {
79        let mut ast: DeriveInput = parse_quote! {
80            struct StructNamed { a: u32, b: i32 }
81        };
82
83        let fields_additional: FieldsNamed = parse_quote!({ c: i64, d: usize });
84        ast.append_named(fields_additional);
85
86        let ast_expected: DeriveInput = parse_quote! {
87            struct StructNamed { a: u32, b: i32, c: i64, d: usize }
88        };
89        assert_eq!(ast_expected, ast);
90    }
91
92    #[test]
93    fn append_fields_named_to_struct_unit() {
94        let mut ast: DeriveInput = parse_quote! {
95            struct StructUnit;
96        };
97
98        let fields_additional: FieldsNamed = parse_quote!({ c: i64, d: usize });
99        ast.append_named(fields_additional);
100
101        let ast_expected: DeriveInput = parse_quote! {
102            struct StructUnit { c: i64, d: usize }
103        };
104        assert_eq!(ast_expected, ast);
105    }
106}