xref: /aosp_15_r20/external/pigweed/pw_format/rust/pw_format_test_macros.rs (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 use proc_macro::TokenStream;
16 use proc_macro2::Ident;
17 use pw_format::macros::{
18     generate, generate_core_fmt, generate_printf, Arg, CoreFmtFormatMacroGenerator,
19     CoreFmtFormatStringParser, FormatAndArgsFlavor, FormatMacroGenerator, FormatParams,
20     FormatStringParser, PrintfFormatMacroGenerator, PrintfFormatStringFragment,
21     PrintfFormatStringParser, Result,
22 };
23 use quote::{quote, ToTokens};
24 use syn::parse_macro_input;
25 
26 type TokenStream2 = proc_macro2::TokenStream;
27 
28 // Generator for testing `generate()`.
29 struct TestGenerator {
30     code_fragments: Vec<TokenStream2>,
31 }
32 
33 impl TestGenerator {
new() -> Self34     pub fn new() -> Self {
35         Self {
36             code_fragments: Vec::new(),
37         }
38     }
39 }
40 
41 impl FormatMacroGenerator for TestGenerator {
finalize(self) -> Result<TokenStream2>42     fn finalize(self) -> Result<TokenStream2> {
43         let code_fragments = self.code_fragments;
44 
45         Ok(quote! {
46           {
47             use pw_format_test_macros_test::TestGeneratorOps;
48             let mut ops = Vec::new();
49             #(#code_fragments);*;
50             ops.push(TestGeneratorOps::Finalize);
51             ops
52           }
53         })
54     }
55 
string_fragment(&mut self, string: &str) -> Result<()>56     fn string_fragment(&mut self, string: &str) -> Result<()> {
57         self.code_fragments.push(quote! {
58             ops.push(TestGeneratorOps::StringFragment(#string.to_string()));
59         });
60         Ok(())
61     }
62 
63     // This example ignores display type and width.
integer_conversion( &mut self, params: &FormatParams, signed: bool, type_width: u8, expression: Arg, ) -> Result<()>64     fn integer_conversion(
65         &mut self,
66         params: &FormatParams,
67         signed: bool,
68         type_width: u8, // in bits
69         expression: Arg,
70     ) -> Result<()> {
71         let expression = format!("{}", expression.to_token_stream());
72         self.code_fragments.push(quote! {
73             ops.push(TestGeneratorOps::IntegerConversion{
74                 params: #params,
75                 signed: #signed,
76                 type_width: #type_width,
77                 arg: #expression.to_string(),
78             });
79         });
80         Ok(())
81     }
82 
string_conversion(&mut self, expression: Arg) -> Result<()>83     fn string_conversion(&mut self, expression: Arg) -> Result<()> {
84         let expression = format!("{}", expression.to_token_stream());
85         self.code_fragments.push(quote! {
86             ops.push(TestGeneratorOps::StringConversion(#expression.to_string()));
87         });
88         Ok(())
89     }
90 
char_conversion(&mut self, expression: Arg) -> Result<()>91     fn char_conversion(&mut self, expression: Arg) -> Result<()> {
92         let expression = format!("{}", expression.to_token_stream());
93         self.code_fragments.push(quote! {
94             ops.push(TestGeneratorOps::CharConversion(#expression.to_string()));
95         });
96         Ok(())
97     }
98 
untyped_conversion(&mut self, expression: Arg, _params: &FormatParams) -> Result<()>99     fn untyped_conversion(&mut self, expression: Arg, _params: &FormatParams) -> Result<()> {
100         let expression = format!("{}", expression.to_token_stream());
101         self.code_fragments.push(quote! {
102             ops.push(TestGeneratorOps::UntypedConversion(#expression.to_string()));
103         });
104         Ok(())
105     }
106 }
107 
generator_test_macro_impl<T: FormatStringParser>(tokens: TokenStream) -> TokenStream108 fn generator_test_macro_impl<T: FormatStringParser>(tokens: TokenStream) -> TokenStream {
109     let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor<T>);
110     let generator = TestGenerator::new();
111     match generate(generator, format_and_args.into()) {
112         Ok(token_stream) => token_stream.into(),
113         Err(e) => e.to_compile_error().into(),
114     }
115 }
116 
117 #[proc_macro]
printf_format_generator_test_macro(tokens: TokenStream) -> TokenStream118 pub fn printf_format_generator_test_macro(tokens: TokenStream) -> TokenStream {
119     generator_test_macro_impl::<PrintfFormatStringParser>(tokens)
120 }
121 
122 #[proc_macro]
core_fmt_format_generator_test_macro(tokens: TokenStream) -> TokenStream123 pub fn core_fmt_format_generator_test_macro(tokens: TokenStream) -> TokenStream {
124     generator_test_macro_impl::<CoreFmtFormatStringParser>(tokens)
125 }
126 
127 // Generator for testing `generate_printf()`.  Allows control over the return
128 // value of the conversion functions.
129 struct PrintfTestGenerator {
130     code_fragments: Vec<TokenStream2>,
131     integer_specifier_override: Option<String>,
132     string_specifier_override: Option<String>,
133     char_specifier_override: Option<String>,
134 }
135 
136 impl PrintfTestGenerator {
new() -> Self137     pub fn new() -> Self {
138         Self {
139             code_fragments: Vec::new(),
140             integer_specifier_override: None,
141             string_specifier_override: None,
142             char_specifier_override: None,
143         }
144     }
145 
with_integer_specifier(mut self, specifier: &str) -> Self146     pub fn with_integer_specifier(mut self, specifier: &str) -> Self {
147         self.integer_specifier_override = Some(specifier.to_string());
148         self
149     }
150 
with_string_specifier(mut self, specifier: &str) -> Self151     pub fn with_string_specifier(mut self, specifier: &str) -> Self {
152         self.string_specifier_override = Some(specifier.to_string());
153         self
154     }
155 
with_char_specifier(mut self, specifier: &str) -> Self156     pub fn with_char_specifier(mut self, specifier: &str) -> Self {
157         self.char_specifier_override = Some(specifier.to_string());
158         self
159     }
160 }
161 
162 impl PrintfFormatMacroGenerator for PrintfTestGenerator {
finalize( self, format_string_fragments: &[PrintfFormatStringFragment], ) -> Result<TokenStream2>163     fn finalize(
164         self,
165         format_string_fragments: &[PrintfFormatStringFragment],
166     ) -> Result<TokenStream2> {
167         // Create locally scoped alias so we can refer to them in `quote!()`.
168         let code_fragments = self.code_fragments;
169         let format_string_pieces: Vec<_> = format_string_fragments
170             .iter()
171             .map(|fragment| fragment.as_token_stream("pw_format_core"))
172             .collect::<Result<Vec<_>>>()?;
173 
174         Ok(quote! {
175           {
176             use pw_format_test_macros_test::PrintfTestGeneratorOps;
177             let mut ops = Vec::new();
178             let format_string = pw_bytes::concat_static_strs!(#(#format_string_pieces),*);
179             #(#code_fragments);*;
180             ops.push(PrintfTestGeneratorOps::Finalize);
181             (format_string, ops)
182           }
183         })
184     }
string_fragment(&mut self, string: &str) -> Result<()>185     fn string_fragment(&mut self, string: &str) -> Result<()> {
186         self.code_fragments.push(quote! {
187             ops.push(PrintfTestGeneratorOps::StringFragment(#string.to_string()));
188         });
189         Ok(())
190     }
integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>191     fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>> {
192         let ty_str = format!("{ty}");
193         let expression = format!("{}", expression.to_token_stream());
194         self.code_fragments.push(quote! {
195             ops.push(PrintfTestGeneratorOps::IntegerConversion{
196                ty: #ty_str.to_string(),
197                arg: #expression.to_string(),
198             });
199         });
200         Ok(self.integer_specifier_override.clone())
201     }
202 
string_conversion(&mut self, expression: Arg) -> Result<Option<String>>203     fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
204         let expression = format!("{}", expression.to_token_stream());
205         self.code_fragments.push(quote! {
206             ops.push(PrintfTestGeneratorOps::StringConversion(#expression.to_string()));
207         });
208         Ok(self.string_specifier_override.clone())
209     }
210 
char_conversion(&mut self, expression: Arg) -> Result<Option<String>>211     fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
212         let expression = format!("{}", expression.to_token_stream());
213         self.code_fragments.push(quote! {
214             ops.push(PrintfTestGeneratorOps::CharConversion(#expression.to_string()));
215         });
216         Ok(self.char_specifier_override.clone())
217     }
218 
untyped_conversion(&mut self, expression: Arg) -> Result<()>219     fn untyped_conversion(&mut self, expression: Arg) -> Result<()> {
220         let expression = format!("{}", expression.to_token_stream());
221         self.code_fragments.push(quote! {
222             ops.push(PrintfTestGeneratorOps::UntypedConversion(#expression.to_string()));
223         });
224         Ok(())
225     }
226 }
227 
printf_generator_test_macro_impl<T: FormatStringParser>(tokens: TokenStream) -> TokenStream228 fn printf_generator_test_macro_impl<T: FormatStringParser>(tokens: TokenStream) -> TokenStream {
229     let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor<T>);
230     let generator = PrintfTestGenerator::new();
231     match generate_printf(generator, format_and_args.into()) {
232         Ok(token_stream) => token_stream.into(),
233         Err(e) => e.to_compile_error().into(),
234     }
235 }
236 
237 #[proc_macro]
printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream238 pub fn printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
239     printf_generator_test_macro_impl::<PrintfFormatStringParser>(tokens)
240 }
241 
242 #[proc_macro]
core_fmt_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream243 pub fn core_fmt_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
244     printf_generator_test_macro_impl::<CoreFmtFormatStringParser>(tokens)
245 }
246 
247 // Causes the generator to substitute %d with %K.
248 #[proc_macro]
integer_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream249 pub fn integer_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
250     let format_and_args =
251         parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>);
252     let generator = PrintfTestGenerator::new().with_integer_specifier("%K");
253     match generate_printf(generator, format_and_args.into()) {
254         Ok(token_stream) => token_stream.into(),
255         Err(e) => e.to_compile_error().into(),
256     }
257 }
258 
259 // Causes the generator to substitute %s with %K.
260 #[proc_macro]
string_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream261 pub fn string_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
262     let format_and_args =
263         parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>);
264     let generator = PrintfTestGenerator::new().with_string_specifier("%K");
265     match generate_printf(generator, format_and_args.into()) {
266         Ok(token_stream) => token_stream.into(),
267         Err(e) => e.to_compile_error().into(),
268     }
269 }
270 
271 // Causes the generator to substitute %c with %K.
272 #[proc_macro]
char_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream273 pub fn char_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream {
274     let format_and_args =
275         parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>);
276     let generator = PrintfTestGenerator::new().with_char_specifier("%K");
277     match generate_printf(generator, format_and_args.into()) {
278         Ok(token_stream) => token_stream.into(),
279         Err(e) => e.to_compile_error().into(),
280     }
281 }
282 
283 // Generator for testing `generate_core_fmt()`.  Allows control over the return
284 // value of the conversion functions.
285 struct CoreFmtTestGenerator {
286     code_fragments: Vec<TokenStream2>,
287     integer_specifier_override: Option<String>,
288     string_specifier_override: Option<String>,
289     char_specifier_override: Option<String>,
290 }
291 
292 impl CoreFmtTestGenerator {
new() -> Self293     pub fn new() -> Self {
294         Self {
295             code_fragments: Vec::new(),
296             integer_specifier_override: None,
297             string_specifier_override: None,
298             char_specifier_override: None,
299         }
300     }
301 
with_integer_specifier(mut self, specifier: &str) -> Self302     pub fn with_integer_specifier(mut self, specifier: &str) -> Self {
303         self.integer_specifier_override = Some(specifier.to_string());
304         self
305     }
306 
with_string_specifier(mut self, specifier: &str) -> Self307     pub fn with_string_specifier(mut self, specifier: &str) -> Self {
308         self.string_specifier_override = Some(specifier.to_string());
309         self
310     }
311 
with_char_specifier(mut self, specifier: &str) -> Self312     pub fn with_char_specifier(mut self, specifier: &str) -> Self {
313         self.char_specifier_override = Some(specifier.to_string());
314         self
315     }
316 }
317 
318 // Until they diverge, we reuse `PrintfTestGeneratorOps` here.
319 impl CoreFmtFormatMacroGenerator for CoreFmtTestGenerator {
finalize(self, format_string: String) -> Result<TokenStream2>320     fn finalize(self, format_string: String) -> Result<TokenStream2> {
321         // Create locally scoped alias so we can refer to them in `quote!()`.
322         let code_fragments = self.code_fragments;
323 
324         Ok(quote! {
325           {
326             use pw_format_test_macros_test::PrintfTestGeneratorOps;
327             let mut ops = Vec::new();
328             #(#code_fragments);*;
329             ops.push(PrintfTestGeneratorOps::Finalize);
330             (#format_string, ops)
331           }
332         })
333     }
string_fragment(&mut self, string: &str) -> Result<()>334     fn string_fragment(&mut self, string: &str) -> Result<()> {
335         self.code_fragments.push(quote! {
336             ops.push(PrintfTestGeneratorOps::StringFragment(#string.to_string()));
337         });
338         Ok(())
339     }
integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>340     fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>> {
341         let ty_str = format!("{ty}");
342         let expression = format!("{}", expression.to_token_stream());
343         self.code_fragments.push(quote! {
344             ops.push(PrintfTestGeneratorOps::IntegerConversion{
345                 ty: #ty_str.to_string(),
346                 arg: #expression.to_string()
347             });
348         });
349         Ok(self.integer_specifier_override.clone())
350     }
351 
string_conversion(&mut self, expression: Arg) -> Result<Option<String>>352     fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
353         let expression = format!("{}", expression.to_token_stream());
354         self.code_fragments.push(quote! {
355             ops.push(PrintfTestGeneratorOps::StringConversion(#expression.to_string()));
356         });
357         Ok(self.string_specifier_override.clone())
358     }
359 
char_conversion(&mut self, expression: Arg) -> Result<Option<String>>360     fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
361         let expression = format!("{}", expression.to_token_stream());
362         self.code_fragments.push(quote! {
363             ops.push(PrintfTestGeneratorOps::CharConversion(#expression.to_string()));
364         });
365         Ok(self.char_specifier_override.clone())
366     }
367 
untyped_conversion(&mut self, expression: Arg) -> Result<()>368     fn untyped_conversion(&mut self, expression: Arg) -> Result<()> {
369         let expression = format!("{}", expression.to_token_stream());
370         self.code_fragments.push(quote! {
371             ops.push(PrintfTestGeneratorOps::UntypedConversion(#expression.to_string()));
372         });
373         Ok(())
374     }
375 }
376 
core_fmt_generator_test_macro_impl<T: FormatStringParser>(tokens: TokenStream) -> TokenStream377 fn core_fmt_generator_test_macro_impl<T: FormatStringParser>(tokens: TokenStream) -> TokenStream {
378     let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor<T>);
379     let generator = CoreFmtTestGenerator::new();
380     match generate_core_fmt(generator, format_and_args.into()) {
381         Ok(token_stream) => token_stream.into(),
382         Err(e) => e.to_compile_error().into(),
383     }
384 }
385 
386 #[proc_macro]
printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream387 pub fn printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream {
388     core_fmt_generator_test_macro_impl::<PrintfFormatStringParser>(tokens)
389 }
390 
391 #[proc_macro]
core_fmt_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream392 pub fn core_fmt_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream {
393     core_fmt_generator_test_macro_impl::<CoreFmtFormatStringParser>(tokens)
394 }
395 
396 // Causes the generator to substitute {} with {:?}.
397 #[proc_macro]
integer_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream398 pub fn integer_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream {
399     let format_and_args =
400         parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>);
401     let generator = CoreFmtTestGenerator::new().with_integer_specifier("{:?}");
402     match generate_core_fmt(generator, format_and_args.into()) {
403         Ok(token_stream) => token_stream.into(),
404         Err(e) => e.to_compile_error().into(),
405     }
406 }
407 
408 // Causes the generator to substitute {} with {:?}.
409 #[proc_macro]
string_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream410 pub fn string_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream {
411     let format_and_args =
412         parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>);
413     let generator = CoreFmtTestGenerator::new().with_string_specifier("{:?}");
414     match generate_core_fmt(generator, format_and_args.into()) {
415         Ok(token_stream) => token_stream.into(),
416         Err(e) => e.to_compile_error().into(),
417     }
418 }
419 
420 // Causes the generator to substitute {} with {:?}.
421 #[proc_macro]
char_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream422 pub fn char_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream {
423     let format_and_args =
424         parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>);
425     let generator = CoreFmtTestGenerator::new().with_char_specifier("{:?}");
426     match generate_core_fmt(generator, format_and_args.into()) {
427         Ok(token_stream) => token_stream.into(),
428         Err(e) => e.to_compile_error().into(),
429     }
430 }
431