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;