kernel/cmdline/
macros.rs

1#[macro_export]
2macro_rules! cmdline_struct {
3    // Macro entry point, parsing the struct and calling `build_struct` and `build_parser`
4    (
5        $(#[$struct_attr: meta])*
6        $vis:vis struct $struct_name: ident $(< $lt: lifetime >)? {
7            $(
8                $(#[$($field_attrs: tt)*])*
9                $f_vis:vis $field_name: ident: $field_type: ty
10            ),* $(,)?
11        }
12    ) => {
13        $crate::cmdline_struct!(@build_struct $vis [$struct_name $(< $lt >)?] {
14            $(
15                $(#[$($field_attrs)*])*
16                $f_vis $field_name: $field_type,
17            )*
18        } => [$(#[$struct_attr])*] => []);
19
20        $crate::cmdline_struct!(@build_parser [$struct_name $(< $lt >)?] {
21            $(
22                $(#[$($field_attrs)*])*
23                $field_name: $field_type,
24            )*
25        });
26    };
27
28    // build the struct using recursion, as we need to have the fields prepared in 1 go
29    // this is the base case, where we have no more fields to process
30    // `$attribs` are the struct attributes from outside, and `$built` are the fields we have processed
31    (
32        @build_struct $vis:vis [$($name_ident: tt)*] { } => [$($attribs: tt)*] => [$($built: tt)*]
33    ) => {
34        $($attribs)*
35        $vis struct $($name_ident)* {
36            $($built)*
37        }
38    };
39    // the branch for handling the `default` custom attribute
40    // since this is our attribute, this is not useful for fields generation
41    // its only used in `build_parser` to generate the default value
42    // we just ignore it here
43    (
44        @build_struct $vis:vis [$($name_ident: tt)*] {
45            #[default = $default: expr]
46            $($rest:tt)*
47        } => [$($attribs: tt)*] => [$($built: tt)*]
48    ) => {
49        $crate::cmdline_struct!(@build_struct $vis [$($name_ident)*] {
50            $($rest)*
51        } => [$($attribs)*] => [$($built)*]);
52    };
53    // handle field with custom attributes
54    // these are not our attributes (everything we handle should be before this)
55    // so we just pass them through
56    (
57        @build_struct $vis:vis [$($name_ident: tt)*] {
58            #[$($field_attrs: tt)*]
59            $($rest:tt)*
60        } => [$($attribs: tt)*] => [$($built: tt)*]
61    ) => {
62        $crate::cmdline_struct!(@build_struct $vis [$($name_ident)*] {
63            $($rest)*
64        } => [$($attribs)*] => [
65            $($built)*
66            #[$($field_attrs)*]
67            // $field_name: $field_type,
68        ]);
69    };
70    // handle fields
71    // this is the main part of the struct fields generation
72    // we put the field in the `$built` list
73    // if it had attributes, it will be put before it by the above branch
74    (
75        @build_struct $vis:vis [$($name_ident: tt)*] {
76            $f_vis:vis $field_name: ident: $field_type: ty,
77            $($rest:tt)*
78        } => [$($attribs: tt)*]  => [$($built: tt)*]
79    ) => {
80        $crate::cmdline_struct!(@build_struct $vis [$($name_ident)*] {
81            $($rest)*
82        } => [$($attribs)*] => [
83            $($built)*
84            $f_vis $field_name: $field_type,
85        ]);
86    };
87
88    // build the parser for the struct
89    // we generate the `parse_cmdline` function for the struct
90    // first, we create all the fields, with the same name as its defined
91    // in the struct, we use the `Default` trait to create the default value
92    //
93    // users can then add the `default` attribute to change the default value
94    //
95    // after that, we loop through the tokens, and match the field name
96    // and call the appropriate `parse_cmdline` function for the field
97    //
98    // for each type used, it must implement `Default` and `CmdlineParse`,
99    // where `CmdlineParse` will use the `tokenizer.next_value` to get the string
100    // value and parse it as needed.
101    (
102        @build_parser [$($name_ident: tt)*] {
103            $(
104                $(#[$($field_attrs: tt)*])*
105                $field_name: ident: $field_type: ty,
106            )*
107        }
108    ) => {
109        impl<'a> CmdlineParse<'a> for $($name_ident)* {
110            #[allow(unused_assignments)]
111            fn parse_cmdline(tokenizer: &mut Tokenizer<'a>) -> parser::Result<'a, Self> {
112                $(
113                    let mut $field_name = Default::default();
114                )*
115                $(
116                    $(
117                        $crate::cmdline_struct!(
118                            @build_parser_attr
119                            #[$($field_attrs)*]
120                            $field_name: $field_type
121                        );
122                    )*
123                )*
124
125                while let Some((i, ident)) = tokenizer.next_ident() {
126                    match ident {
127                        $(
128                            stringify!($field_name) => {
129                                $field_name = CmdlineParse::parse_cmdline(tokenizer)?;
130                            }
131                        )*
132                        unknown => {
133                            return Err(parser::ParseError::new(parser::ParseErrorKind::UnexpectedId(unknown), i))
134                        }
135                    }
136                }
137
138                Ok(Self {
139                    $(
140                        $field_name,
141                    )*
142                })
143            }
144        }
145    };
146
147    // this handled the attributes for building the parser
148    // the `default` attribute will change the value of `$field_name` to the specified value
149    (
150        @build_parser_attr
151        #[default = $expr: expr]
152        $field_name: ident: $field_type: ty
153    ) => {
154        $field_name = $expr;
155    };
156    // other attributes are ignored here, they are handled by `build_struct`
157    (
158        @build_parser_attr
159        #[$($field_attrs: tt)*]
160        $field_name: ident: $field_type: ty
161    ) => {
162        // ignore, not handled by us
163    };
164}
165
166#[allow(unused_imports)]
167pub use cmdline_struct;