blob: 35e43a50c8a8974b692018260b71ecd12132379c [file] [log] [blame]
// 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 quote::quote;
use syn::{
parse::{Parse, ParseStream},
parse_macro_input, Expr, Token,
};
use pw_format::macros::{
generate_core_fmt, Arg, CoreFmtFormatMacroGenerator, CoreFmtFormatStringParser,
FormatAndArgsFlavor, FormatStringParser, PrintfFormatStringParser, Result,
};
type TokenStream2 = proc_macro2::TokenStream;
// Arguments to `pw_log[f]_backend`. A log level followed by a [`pw_format`]
// format string.
#[derive(Debug)]
struct PwLogArgs<T: FormatStringParser> {
log_level: Expr,
format_and_args: FormatAndArgsFlavor<T>,
}
impl<T: FormatStringParser> Parse for PwLogArgs<T> {
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
let log_level: Expr = input.parse()?;
input.parse::<Token![,]>()?;
let format_and_args: FormatAndArgsFlavor<_> = input.parse()?;
Ok(PwLogArgs {
log_level,
format_and_args,
})
}
}
// Generator that implements [`pw_format::CoreFmtFormatMacroGenerator`] to take
// a log line and turn it into [`std::println`] calls.
struct LogfGenerator<'a> {
log_level: &'a Expr,
args: Vec<TokenStream2>,
}
impl<'a> LogfGenerator<'a> {
fn new(log_level: &'a Expr) -> Self {
Self {
log_level,
args: Vec::new(),
}
}
}
// Use a [`pw_format::CoreFmtFormatMacroGenerator`] to prepare arguments to call
// [`std::println`].
impl<'a> CoreFmtFormatMacroGenerator for LogfGenerator<'a> {
fn finalize(self, format_string: String) -> Result<TokenStream2> {
let log_level = self.log_level;
let args = &self.args;
let format_string = format!("[{{}}] {format_string}");
Ok(quote! {
{
use std::println;
println!(#format_string, __pw_log_backend_crate::log_level_tag(#log_level), #(#args),*);
}
})
}
fn string_fragment(&mut self, _string: &str) -> Result<()> {
// String fragments are encoded directly into the format string.
Ok(())
}
fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>> {
self.args.push(quote! {((#expression) as #ty)});
Ok(None)
}
fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
self.args.push(quote! {((#expression) as &str)});
Ok(None)
}
fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
self.args.push(quote! {((#expression) as char)});
Ok(None)
}
fn untyped_conversion(&mut self, expression: Arg) -> Result<()> {
self.args.push(quote! {(#expression)});
Ok(())
}
}
#[proc_macro]
pub fn _pw_log_backend(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as PwLogArgs<CoreFmtFormatStringParser>);
let generator = LogfGenerator::new(&input.log_level);
match generate_core_fmt(generator, input.format_and_args.into()) {
Ok(token_stream) => token_stream.into(),
Err(e) => e.to_compile_error().into(),
}
}
#[proc_macro]
pub fn _pw_logf_backend(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as PwLogArgs<PrintfFormatStringParser>);
let generator = LogfGenerator::new(&input.log_level);
match generate_core_fmt(generator, input.format_and_args.into()) {
Ok(token_stream) => token_stream.into(),
Err(e) => e.to_compile_error().into(),
}
}