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 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 and turn it into [`std::println`] calls.
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 // Use a [`pw_format::CoreFmtFormatMacroGenerator`] to prepare arguments to call
68 // [`std::println`].
69 impl<'a> CoreFmtFormatMacroGenerator for LogfGenerator<'a> {
finalize(self, format_string: String) -> Result<TokenStream2>70 fn finalize(self, format_string: String) -> Result<TokenStream2> {
71 let log_level = self.log_level;
72 let args = &self.args;
73 let format_string = format!("[{{}}] {format_string}");
74 Ok(quote! {
75 {
76 use std::println;
77 println!(#format_string, __pw_log_backend_crate::log_level_tag(#log_level), #(#args),*);
78 }
79 })
80 }
81
string_fragment(&mut self, _string: &str) -> Result<()>82 fn string_fragment(&mut self, _string: &str) -> Result<()> {
83 // String fragments are encoded directly into the format string.
84 Ok(())
85 }
86
integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>87 fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>> {
88 self.args.push(quote! {((#expression) as #ty)});
89 Ok(None)
90 }
91
string_conversion(&mut self, expression: Arg) -> Result<Option<String>>92 fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
93 self.args.push(quote! {((#expression) as &str)});
94 Ok(None)
95 }
96
char_conversion(&mut self, expression: Arg) -> Result<Option<String>>97 fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
98 self.args.push(quote! {((#expression) as char)});
99 Ok(None)
100 }
101
untyped_conversion(&mut self, expression: Arg) -> Result<()>102 fn untyped_conversion(&mut self, expression: Arg) -> Result<()> {
103 self.args.push(quote! {(#expression)});
104 Ok(())
105 }
106 }
107
108 #[proc_macro]
_pw_log_backend(tokens: TokenStream) -> TokenStream109 pub fn _pw_log_backend(tokens: TokenStream) -> TokenStream {
110 let input = parse_macro_input!(tokens as PwLogArgs<CoreFmtFormatStringParser>);
111
112 let generator = LogfGenerator::new(&input.log_level);
113
114 match generate_core_fmt(generator, input.format_and_args.into()) {
115 Ok(token_stream) => token_stream.into(),
116 Err(e) => e.to_compile_error().into(),
117 }
118 }
119
120 #[proc_macro]
_pw_logf_backend(tokens: TokenStream) -> TokenStream121 pub fn _pw_logf_backend(tokens: TokenStream) -> TokenStream {
122 let input = parse_macro_input!(tokens as PwLogArgs<PrintfFormatStringParser>);
123
124 let generator = LogfGenerator::new(&input.log_level);
125
126 match generate_core_fmt(generator, input.format_and_args.into()) {
127 Ok(token_stream) => token_stream.into(),
128 Err(e) => e.to_compile_error().into(),
129 }
130 }
131