1use defmt_parser::{Level, ParserMode};
2use proc_macro::TokenStream;
3use proc_macro2::TokenStream as TokenStream2;
4use proc_macro_error2::abort;
5use quote::quote;
6use syn::{parse_macro_input, parse_quote};
7
8use crate::construct;
9
10use self::env_filter::EnvFilter;
11pub(crate) use self::{args::Args, codegen::Codegen};
12
13mod args;
14mod codegen;
15mod env_filter;
16
17pub(crate) fn expand(level: Level, args: TokenStream) -> TokenStream {
18 expand_parsed(level, args:parse_macro_input!(args as Args)).into()
19}
20
21pub(crate) fn expand_parsed(level: Level, args: Args) -> TokenStream2 {
22 let format_string = args.format_string.value();
23 let fragments = match defmt_parser::parse(&format_string, ParserMode::Strict) {
24 Ok(args) => args,
25 Err(e) => abort!(args.format_string, "{}", e),
26 };
27
28 let formatting_exprs = args
29 .formatting_args
30 .map(|punctuated| punctuated.into_iter().collect::<Vec<_>>())
31 .unwrap_or_default();
32
33 let Codegen { patterns, exprs } = Codegen::new(
34 &fragments,
35 formatting_exprs.len(),
36 args.format_string.span(),
37 );
38
39 let header = construct::interned_string(
40 &format_string,
41 level.as_str(),
42 true,
43 Some(level.as_str()),
44 &parse_quote!(defmt),
45 );
46 let env_filter = EnvFilter::from_env_var();
47
48 if let Some(filter_check) = env_filter.path_check(level) {
49 let content = if exprs.is_empty() {
50 quote!(
51 defmt::export::acquire_header_and_release(&#header);
52 )
53 } else {
54 quote!(
55 // safety: will be released a few lines further down
56 unsafe { defmt::export::acquire_and_header(&#header); };
57 #(#exprs;)*
58 // safety: acquire() was called a few lines above
59 unsafe { defmt::export::release() }
60 )
61 };
62
63 quote!(
64 {
65 option_env!("DEFMT_LOG");
66 match (#(&(#formatting_exprs)),*) {
67 (#(#patterns),*) => {
68 if #filter_check {
69 #content
70 }
71 }
72 }
73 }
74 )
75 } else {
76 // if logging is disabled match args, so they are not considered "unused"
77 quote!(
78 {
79 option_env!("DEFMT_LOG");
80 match (#(&(#formatting_exprs)),*) {
81 _ => {}
82 }
83 }
84 )
85 }
86}
87