nested_struct/
lib.rs

1#![doc = include_str!("../README.md")]
2
3#[macro_export]
4macro_rules! nested_struct {
5    // [MAIN] Primary rule to generate the struct
6    (
7        $(#[$meta:meta])*
8        $vis:vis struct $name:ident {
9            $(
10                $(#[$field_meta:meta])*
11                $(@nested(#[$field_nested_meta:meta]))*
12                $field_vis:vis $field_name:ident : $field_ty:ident $({
13                    $($field_ty_inner:tt)*
14                })?
15            ),*
16        $(,)? }
17    ) => {
18        // Generate our primary struct
19        $(#[$meta])* $vis struct $name {
20            $(
21                $(#[$field_meta])*
22                $field_vis $field_name : $field_ty
23            ),*
24        }
25
26        // Generate our inner structs for fields
27        $(nested_struct! {
28            @nested
29            $(#[$field_nested_meta])*
30            $field_vis $field_ty $({
31                $($field_ty_inner)*
32            })?
33        })*
34    };
35
36    // [INCLUDE] Used to filter out struct generation to only nested types
37    (@nested $(#[$meta:meta])* $vis:vis $name:ident {$($fields:tt)*}) => {
38        nested_struct! {
39            $(#[$meta])*
40            $vis struct $name {
41                $($fields)*
42            }
43        }
44    };
45
46    // [EXCLUDE] Used to filter out struct generation to only nested types
47    (@nested $(#[$meta:meta])* $vis:vis $name:ident) => {};
48
49    // Any garbage we will ignore, including generating an invalid struct
50    /* ($($other:tt)*) => {
51        compile_error!(stringify!($($other)*));
52    }; */
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn supports_empty_named_struct() {
61        nested_struct! {
62            struct TestStruct {}
63        }
64
65        let _ = TestStruct {};
66    }
67
68    #[test]
69    #[allow(dead_code)]
70    fn supports_named_struct_with_regular_fields() {
71        nested_struct! {
72            struct TestStruct {
73                field: u32
74            }
75        }
76
77        let _ = TestStruct { field: 123 };
78    }
79
80    #[test]
81    #[allow(dead_code)]
82    fn supports_named_struct_with_nested_fields() {
83        nested_struct! {
84            struct TestStruct {
85                field: NestedField {
86                    field: u32
87                }
88            }
89        }
90
91        let _ = TestStruct {
92            field: NestedField { field: 123 },
93        };
94    }
95
96    #[test]
97    #[allow(dead_code)]
98    fn supports_named_struct_with_deeply_nested_fields() {
99        nested_struct! {
100            struct TestStruct {
101                field: NestedField {
102                    field: NestedField2 { field: u32 }
103                }
104            }
105        }
106
107        let _ = TestStruct {
108            field: NestedField {
109                field: NestedField2 { field: 123 },
110            },
111        };
112    }
113
114    #[test]
115    #[allow(dead_code)]
116    fn supports_named_struct_with_nested_fields_using_nested_attribute() {
117        // Single nested version
118        {
119            nested_struct! {
120                #[derive(Clone)]
121                struct TestStruct {
122                    @nested(#[derive(Clone)])
123                    field: NestedField {
124                        field: u32
125                    }
126                }
127            }
128
129            let _ = TestStruct {
130                field: NestedField { field: 123 },
131            };
132        }
133
134        // Deeply nested version
135        {
136            nested_struct! {
137                #[derive(Clone)]
138                struct TestStruct {
139                    @nested(#[derive(Clone)])
140                    field: NestedField {
141                        @nested(#[derive(Clone)])
142                        field: NestedField2 {
143                            field: u32
144                        }
145                    }
146                }
147            }
148
149            let _ = TestStruct {
150                field: NestedField {
151                    field: NestedField2 { field: 123 },
152                },
153            };
154        }
155    }
156}