1use proc_macro::TokenStream;
2use proc_macro_error2::{abort, abort_call_site};
3use quote::{quote, ToTokens};
4use syn::{parse_macro_input, parse_quote, Data, DeriveInput};
5
6mod codegen;
7
8pub(crate) fn expand(input: TokenStream) -> TokenStream {
9 let DeriveInput {
10 attrs,
11 vis: _,
12 ident,
13 mut generics,
14 data,
15 } = parse_macro_input!(input as DeriveInput);
16
17 let defmt_path = match defmt_crate_path(&attrs) {
18 Ok(defmt_path) => defmt_path,
19 Err(e) => abort!(e),
20 };
21
22 let encode_data = match &data {
23 Data::Enum(data) => codegen::encode_enum_data(&ident, data, &defmt_path),
24 Data::Struct(data) => codegen::encode_struct_data(&ident, data, &defmt_path),
25 Data::Union(_) => abort_call_site!("`#[derive(Format)]` does not support unions"),
26 };
27
28 let codegen::EncodeData {
29 format_tag,
30 stmts,
31 where_predicates,
32 } = match encode_data {
33 Ok(data) => data,
34 Err(e) => return e.into_compile_error().into(),
35 };
36
37 let codegen::Generics {
38 impl_generics,
39 type_generics,
40 where_clause,
41 } = codegen::Generics::codegen(&mut generics, where_predicates);
42
43 quote!(
44 #[automatically_derived]
45 impl #impl_generics #defmt_path::Format for #ident #type_generics #where_clause {
46 fn format(&self, f: #defmt_path::Formatter) {
47 use #defmt_path as defmt;
48 #defmt_path::unreachable!()
49 }
50
51 fn _format_tag() -> #defmt_path::Str {
52 #format_tag
53 }
54
55 fn _format_data(&self) {
56 #(#stmts)*
57 }
58 }
59 )
60 .into()
61}
62
63fn defmt_crate_path(attrs: &[syn::Attribute]) -> Result<syn::Path, syn::Error> {
64 let mut defmt_path: Path = parse_quote!(defmt);
65 let res: Result<(), Error> = attrsimpl Iterator
66 .iter()
67 .filter(|attr: &&Attribute| attr.path().is_ident("defmt"))
68 .try_for_each(|attr: &Attribute| {
69 attr.parse_nested_meta(|meta: ParseNestedMeta<'_>| {
70 if meta.path.get_ident().is_some_and(|ident: &Ident| ident == "crate") {
71 meta.input.parse::<syn::Token![=]>()?;
72 defmt_path = meta.input.parse::<syn::Path>()?;
73 Ok(())
74 } else {
75 let path: String = meta.path.to_token_stream().to_string().replace(from:' ', to:"");
76 Err(meta.error(msg:format_args!("unknown defmt attribute `{path}`")))
77 }
78 })
79 });
80 res.map(|()| defmt_path)
81}
82