1use defmt_parser::ParserMode;
2use proc_macro::TokenStream;
3use proc_macro_error2::abort;
4use quote::format_ident;
5use quote::quote;
6use syn::parse_macro_input;
7
8use crate::{construct, function_like::log};
9
10pub(crate) fn expand(args: TokenStream) -> TokenStream {
11 let args = parse_macro_input!(args as log::Args);
12
13 let format_string = args.format_string.value();
14
15 let fragments = match defmt_parser::parse(&format_string, ParserMode::Strict) {
16 Ok(args) => args,
17 Err(e) => abort!(args.format_string, "{}", e),
18 };
19
20 let formatting_exprs: Vec<_> = args
21 .formatting_args
22 .map(|punctuated| punctuated.into_iter().collect())
23 .unwrap_or_default();
24
25 let log::Codegen { patterns, exprs } = log::Codegen::new(
26 &fragments,
27 formatting_exprs.len(),
28 args.format_string.span(),
29 );
30
31 let var_name = format_ident!("S");
32 let var_item = construct::static_variable(&var_name, &format_string, "timestamp", None);
33
34 quote!(
35 const _: () = {
36 #[export_name = "_defmt_timestamp"]
37 #[inline(never)]
38 fn defmt_timestamp(fmt: defmt::Formatter<'_>) {
39 match (#(&(#formatting_exprs)),*) {
40 (#(#patterns),*) => {
41 // NOTE: No format string index, and no finalize call.
42 #(#exprs;)*
43 }
44 }
45 }
46
47 #var_item;
48
49 // Unique symbol name to prevent multiple `timestamp!` invocations in the crate graph.
50 // Uses `#var_name` to ensure it is not discarded by the linker.
51 // This symbol itself is retained via a `EXTERN` directive in the linker script.
52 #[no_mangle]
53 #[cfg_attr(target_os = "macos", link_section = ".defmt,end.timestamp")]
54 #[cfg_attr(not(target_os = "macos"), link_section = ".defmt.end.timestamp")]
55 static __DEFMT_MARKER_TIMESTAMP_WAS_DEFINED: &u8 = &#var_name;
56 };
57 )
58 .into()
59}
60