| // Copyright 2023 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| use proc_macro::TokenStream; |
| use proc_macro2::Ident; |
| use pw_format::macros::{ |
| generate, generate_core_fmt, generate_printf, Arg, CoreFmtFormatMacroGenerator, |
| CoreFmtFormatStringParser, FormatAndArgsFlavor, FormatMacroGenerator, FormatStringParser, |
| IntegerDisplayType, PrintfFormatMacroGenerator, PrintfFormatStringFragment, |
| PrintfFormatStringParser, Result, |
| }; |
| use quote::{quote, ToTokens}; |
| use syn::parse_macro_input; |
| |
| type TokenStream2 = proc_macro2::TokenStream; |
| |
| // Generator for testing `generate()`. |
| struct TestGenerator { |
| code_fragments: Vec<TokenStream2>, |
| } |
| |
| impl TestGenerator { |
| pub fn new() -> Self { |
| Self { |
| code_fragments: Vec::new(), |
| } |
| } |
| } |
| |
| impl FormatMacroGenerator for TestGenerator { |
| fn finalize(self) -> Result<TokenStream2> { |
| let code_fragments = self.code_fragments; |
| |
| Ok(quote! { |
| { |
| use pw_format_test_macros_test::TestGeneratorOps; |
| let mut ops = Vec::new(); |
| #(#code_fragments);*; |
| ops.push(TestGeneratorOps::Finalize); |
| ops |
| } |
| }) |
| } |
| |
| fn string_fragment(&mut self, string: &str) -> Result<()> { |
| self.code_fragments.push(quote! { |
| ops.push(TestGeneratorOps::StringFragment(#string.to_string())); |
| }); |
| Ok(()) |
| } |
| |
| // This example ignores display type and width. |
| fn integer_conversion( |
| &mut self, |
| display_type: IntegerDisplayType, |
| type_width: u8, // in bits |
| expression: Arg, |
| ) -> Result<()> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(TestGeneratorOps::IntegerConversion{ |
| display_type: #display_type, |
| type_width: #type_width, |
| arg: #expression.to_string(), |
| }); |
| }); |
| Ok(()) |
| } |
| |
| fn string_conversion(&mut self, expression: Arg) -> Result<()> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(TestGeneratorOps::StringConversion(#expression.to_string())); |
| }); |
| Ok(()) |
| } |
| |
| fn char_conversion(&mut self, expression: Arg) -> Result<()> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(TestGeneratorOps::CharConversion(#expression.to_string())); |
| }); |
| Ok(()) |
| } |
| |
| fn untyped_conversion(&mut self, expression: Arg) -> Result<()> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(TestGeneratorOps::UntypedConversion(#expression.to_string())); |
| }); |
| Ok(()) |
| } |
| } |
| |
| fn generator_test_macro_impl<T: FormatStringParser>(tokens: TokenStream) -> TokenStream { |
| let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor<T>); |
| let generator = TestGenerator::new(); |
| match generate(generator, format_and_args.into()) { |
| Ok(token_stream) => token_stream.into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| #[proc_macro] |
| pub fn printf_format_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| generator_test_macro_impl::<PrintfFormatStringParser>(tokens) |
| } |
| |
| #[proc_macro] |
| pub fn core_fmt_format_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| generator_test_macro_impl::<CoreFmtFormatStringParser>(tokens) |
| } |
| |
| // Generator for testing `generate_printf()`. Allows control over the return |
| // value of the conversion functions. |
| struct PrintfTestGenerator { |
| code_fragments: Vec<TokenStream2>, |
| integer_specifier_override: Option<String>, |
| string_specifier_override: Option<String>, |
| char_specifier_override: Option<String>, |
| } |
| |
| impl PrintfTestGenerator { |
| pub fn new() -> Self { |
| Self { |
| code_fragments: Vec::new(), |
| integer_specifier_override: None, |
| string_specifier_override: None, |
| char_specifier_override: None, |
| } |
| } |
| |
| pub fn with_integer_specifier(mut self, specifier: &str) -> Self { |
| self.integer_specifier_override = Some(specifier.to_string()); |
| self |
| } |
| |
| pub fn with_string_specifier(mut self, specifier: &str) -> Self { |
| self.string_specifier_override = Some(specifier.to_string()); |
| self |
| } |
| |
| pub fn with_char_specifier(mut self, specifier: &str) -> Self { |
| self.char_specifier_override = Some(specifier.to_string()); |
| self |
| } |
| } |
| |
| impl PrintfFormatMacroGenerator for PrintfTestGenerator { |
| fn finalize( |
| self, |
| format_string_fragments: &[PrintfFormatStringFragment], |
| ) -> Result<TokenStream2> { |
| // Create locally scoped alias so we can refer to them in `quote!()`. |
| let code_fragments = self.code_fragments; |
| let format_string_pieces: Vec<_> = format_string_fragments |
| .iter() |
| .map(|fragment| fragment.as_token_stream("pw_format_core")) |
| .collect::<Result<Vec<_>>>()?; |
| |
| Ok(quote! { |
| { |
| use pw_format_test_macros_test::PrintfTestGeneratorOps; |
| let mut ops = Vec::new(); |
| let format_string = pw_bytes::concat_static_strs!(#(#format_string_pieces),*); |
| #(#code_fragments);*; |
| ops.push(PrintfTestGeneratorOps::Finalize); |
| (format_string, ops) |
| } |
| }) |
| } |
| fn string_fragment(&mut self, string: &str) -> Result<()> { |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::StringFragment(#string.to_string())); |
| }); |
| Ok(()) |
| } |
| fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>> { |
| let ty_str = format!("{ty}"); |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::IntegerConversion{ |
| ty: #ty_str.to_string(), |
| arg: #expression.to_string(), |
| }); |
| }); |
| Ok(self.integer_specifier_override.clone()) |
| } |
| |
| fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::StringConversion(#expression.to_string())); |
| }); |
| Ok(self.string_specifier_override.clone()) |
| } |
| |
| fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::CharConversion(#expression.to_string())); |
| }); |
| Ok(self.char_specifier_override.clone()) |
| } |
| |
| fn untyped_conversion(&mut self, expression: Arg) -> Result<()> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::UntypedConversion(#expression.to_string())); |
| }); |
| Ok(()) |
| } |
| } |
| |
| fn printf_generator_test_macro_impl<T: FormatStringParser>(tokens: TokenStream) -> TokenStream { |
| let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor<T>); |
| let generator = PrintfTestGenerator::new(); |
| match generate_printf(generator, format_and_args.into()) { |
| Ok(token_stream) => token_stream.into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| #[proc_macro] |
| pub fn printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| printf_generator_test_macro_impl::<PrintfFormatStringParser>(tokens) |
| } |
| |
| #[proc_macro] |
| pub fn core_fmt_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| printf_generator_test_macro_impl::<CoreFmtFormatStringParser>(tokens) |
| } |
| |
| // Causes the generator to substitute %d with %K. |
| #[proc_macro] |
| pub fn integer_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| let format_and_args = |
| parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>); |
| let generator = PrintfTestGenerator::new().with_integer_specifier("%K"); |
| match generate_printf(generator, format_and_args.into()) { |
| Ok(token_stream) => token_stream.into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| // Causes the generator to substitute %s with %K. |
| #[proc_macro] |
| pub fn string_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| let format_and_args = |
| parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>); |
| let generator = PrintfTestGenerator::new().with_string_specifier("%K"); |
| match generate_printf(generator, format_and_args.into()) { |
| Ok(token_stream) => token_stream.into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| // Causes the generator to substitute %c with %K. |
| #[proc_macro] |
| pub fn char_sub_printf_format_printf_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| let format_and_args = |
| parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>); |
| let generator = PrintfTestGenerator::new().with_char_specifier("%K"); |
| match generate_printf(generator, format_and_args.into()) { |
| Ok(token_stream) => token_stream.into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| // Generator for testing `generate_core_fmt()`. Allows control over the return |
| // value of the conversion functions. |
| struct CoreFmtTestGenerator { |
| code_fragments: Vec<TokenStream2>, |
| integer_specifier_override: Option<String>, |
| string_specifier_override: Option<String>, |
| char_specifier_override: Option<String>, |
| } |
| |
| impl CoreFmtTestGenerator { |
| pub fn new() -> Self { |
| Self { |
| code_fragments: Vec::new(), |
| integer_specifier_override: None, |
| string_specifier_override: None, |
| char_specifier_override: None, |
| } |
| } |
| |
| pub fn with_integer_specifier(mut self, specifier: &str) -> Self { |
| self.integer_specifier_override = Some(specifier.to_string()); |
| self |
| } |
| |
| pub fn with_string_specifier(mut self, specifier: &str) -> Self { |
| self.string_specifier_override = Some(specifier.to_string()); |
| self |
| } |
| |
| pub fn with_char_specifier(mut self, specifier: &str) -> Self { |
| self.char_specifier_override = Some(specifier.to_string()); |
| self |
| } |
| } |
| |
| // Until they diverge, we reuse `PrintfTestGeneratorOps` here. |
| impl CoreFmtFormatMacroGenerator for CoreFmtTestGenerator { |
| fn finalize(self, format_string: String) -> Result<TokenStream2> { |
| // Create locally scoped alias so we can refer to them in `quote!()`. |
| let code_fragments = self.code_fragments; |
| |
| Ok(quote! { |
| { |
| use pw_format_test_macros_test::PrintfTestGeneratorOps; |
| let mut ops = Vec::new(); |
| #(#code_fragments);*; |
| ops.push(PrintfTestGeneratorOps::Finalize); |
| (#format_string, ops) |
| } |
| }) |
| } |
| fn string_fragment(&mut self, string: &str) -> Result<()> { |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::StringFragment(#string.to_string())); |
| }); |
| Ok(()) |
| } |
| fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>> { |
| let ty_str = format!("{ty}"); |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::IntegerConversion{ |
| ty: #ty_str.to_string(), |
| arg: #expression.to_string() |
| }); |
| }); |
| Ok(self.integer_specifier_override.clone()) |
| } |
| |
| fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::StringConversion(#expression.to_string())); |
| }); |
| Ok(self.string_specifier_override.clone()) |
| } |
| |
| fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::CharConversion(#expression.to_string())); |
| }); |
| Ok(self.char_specifier_override.clone()) |
| } |
| |
| fn untyped_conversion(&mut self, expression: Arg) -> Result<()> { |
| let expression = format!("{}", expression.to_token_stream()); |
| self.code_fragments.push(quote! { |
| ops.push(PrintfTestGeneratorOps::UntypedConversion(#expression.to_string())); |
| }); |
| Ok(()) |
| } |
| } |
| |
| fn core_fmt_generator_test_macro_impl<T: FormatStringParser>(tokens: TokenStream) -> TokenStream { |
| let format_and_args = parse_macro_input!(tokens as FormatAndArgsFlavor<T>); |
| let generator = CoreFmtTestGenerator::new(); |
| match generate_core_fmt(generator, format_and_args.into()) { |
| Ok(token_stream) => token_stream.into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| #[proc_macro] |
| pub fn printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| core_fmt_generator_test_macro_impl::<PrintfFormatStringParser>(tokens) |
| } |
| |
| #[proc_macro] |
| pub fn printf_core_fmt_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| core_fmt_generator_test_macro_impl::<CoreFmtFormatStringParser>(tokens) |
| } |
| |
| // Causes the generator to substitute {} with {:?}. |
| #[proc_macro] |
| pub fn integer_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| let format_and_args = |
| parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>); |
| let generator = CoreFmtTestGenerator::new().with_integer_specifier("{:?}"); |
| match generate_core_fmt(generator, format_and_args.into()) { |
| Ok(token_stream) => token_stream.into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| // Causes the generator to substitute {} with {:?}. |
| #[proc_macro] |
| pub fn string_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| let format_and_args = |
| parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>); |
| let generator = CoreFmtTestGenerator::new().with_string_specifier("{:?}"); |
| match generate_core_fmt(generator, format_and_args.into()) { |
| Ok(token_stream) => token_stream.into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |
| |
| // Causes the generator to substitute {} with {:?}. |
| #[proc_macro] |
| pub fn char_sub_printf_format_core_fmt_generator_test_macro(tokens: TokenStream) -> TokenStream { |
| let format_and_args = |
| parse_macro_input!(tokens as FormatAndArgsFlavor<PrintfFormatStringParser>); |
| let generator = CoreFmtTestGenerator::new().with_char_specifier("{:?}"); |
| match generate_core_fmt(generator, format_and_args.into()) { |
| Ok(token_stream) => token_stream.into(), |
| Err(e) => e.to_compile_error().into(), |
| } |
| } |