• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025 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 quote::quote;
18 use syn::{
19     parse::{Parse, ParseStream},
20     parse_macro_input, Expr, Token,
21 };
22 
23 use pw_format::macros::{
24     generate_core_fmt, Arg, CoreFmtFormatMacroGenerator, CoreFmtFormatStringParser,
25     FormatAndArgsFlavor, FormatStringParser, PrintfFormatStringParser, Result,
26 };
27 
28 type TokenStream2 = proc_macro2::TokenStream;
29 
30 // Arguments to `pw_log[f]_backend`.  A log level followed by a [`pw_format`]
31 // format string.
32 #[derive(Debug)]
33 struct PwLogArgs<T: FormatStringParser> {
34     log_level: Expr,
35     format_and_args: FormatAndArgsFlavor<T>,
36 }
37 
38 impl<T: FormatStringParser> Parse for PwLogArgs<T> {
parse(input: ParseStream) -> syn::parse::Result<Self>39     fn parse(input: ParseStream) -> syn::parse::Result<Self> {
40         let log_level: Expr = input.parse()?;
41         input.parse::<Token![,]>()?;
42         let format_and_args: FormatAndArgsFlavor<_> = input.parse()?;
43 
44         Ok(PwLogArgs {
45             log_level,
46             format_and_args,
47         })
48     }
49 }
50 
51 // Generator that implements [`pw_format::CoreFmtFormatMacroGenerator`] to take
52 // a log line output it through the system console.
53 struct LogfGenerator<'a> {
54     log_level: &'a Expr,
55     args: Vec<TokenStream2>,
56 }
57 
58 impl<'a> LogfGenerator<'a> {
new(log_level: &'a Expr) -> Self59     fn new(log_level: &'a Expr) -> Self {
60         Self {
61             log_level,
62             args: Vec::new(),
63         }
64     }
65 }
66 
67 impl CoreFmtFormatMacroGenerator for LogfGenerator<'_> {
finalize(self, format_string: String) -> Result<TokenStream2>68     fn finalize(self, format_string: String) -> Result<TokenStream2> {
69         let log_level = self.log_level;
70         let args = &self.args;
71         let format_string = format!("[{{}}] {format_string}\n");
72         Ok(quote! {
73           {
74             use __pw_log_backend_crate::embedded_io::Write;
75             let mut console = __pw_log_backend_crate::console::Console::new();
76             let _ = core::write!(
77                 &mut console,
78                 #format_string,
79                 __pw_log_backend_crate::log_level_tag(#log_level),
80                 #(#args),*
81             );
82           }
83         })
84     }
85 
string_fragment(&mut self, _string: &str) -> Result<()>86     fn string_fragment(&mut self, _string: &str) -> Result<()> {
87         // String fragments are encoded directly into the format string.
88         Ok(())
89     }
90 
integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>91     fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>> {
92         self.args.push(quote! {((#expression) as #ty)});
93         Ok(None)
94     }
95 
string_conversion(&mut self, expression: Arg) -> Result<Option<String>>96     fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
97         self.args.push(quote! {((#expression) as &str)});
98         Ok(None)
99     }
100 
char_conversion(&mut self, expression: Arg) -> Result<Option<String>>101     fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
102         self.args.push(quote! {((#expression) as char)});
103         Ok(None)
104     }
105 
untyped_conversion(&mut self, expression: Arg) -> Result<()>106     fn untyped_conversion(&mut self, expression: Arg) -> Result<()> {
107         self.args.push(quote! {(#expression)});
108         Ok(())
109     }
110 }
111 
112 #[proc_macro]
_pw_log_backend(tokens: TokenStream) -> TokenStream113 pub fn _pw_log_backend(tokens: TokenStream) -> TokenStream {
114     let input = parse_macro_input!(tokens as PwLogArgs<CoreFmtFormatStringParser>);
115 
116     let generator = LogfGenerator::new(&input.log_level);
117 
118     match generate_core_fmt(generator, input.format_and_args.into()) {
119         Ok(token_stream) => token_stream.into(),
120         Err(e) => e.to_compile_error().into(),
121     }
122 }
123 
124 #[proc_macro]
_pw_logf_backend(tokens: TokenStream) -> TokenStream125 pub fn _pw_logf_backend(tokens: TokenStream) -> TokenStream {
126     let input = parse_macro_input!(tokens as PwLogArgs<PrintfFormatStringParser>);
127 
128     let generator = LogfGenerator::new(&input.log_level);
129 
130     match generate_core_fmt(generator, input.format_and_args.into()) {
131         Ok(token_stream) => token_stream.into(),
132         Err(e) => e.to_compile_error().into(),
133     }
134 }
135