1use defmt_parser::{Fragment, Parameter, Type};
2use proc_macro2::{Ident as Ident2, Span as Span2, TokenStream as TokenStream2};
3use proc_macro_error2::abort;
4use quote::{format_ident, quote};
5
6pub(crate) struct Codegen {
7 pub(crate) exprs: Vec<TokenStream2>,
8 pub(crate) patterns: Vec<Ident2>,
9}
10
11impl Codegen {
12 pub(crate) fn new(fragments: &[Fragment<'_>], given_arg_count: usize, span: Span2) -> Self {
13 let params = fragments
14 .iter()
15 .filter_map(|frag| match frag {
16 Fragment::Parameter(param) => Some(param.clone()),
17 Fragment::Literal(_) => None,
18 })
19 .collect::<Vec<_>>();
20
21 let expected_arg_count = params
22 .iter()
23 .map(|param| param.index + 1)
24 .max()
25 .unwrap_or(0);
26
27 if given_arg_count != expected_arg_count {
28 let mut only = "";
29 if given_arg_count < expected_arg_count {
30 only = "only ";
31 }
32
33 abort!(
34 span,
35 "format string requires {} arguments but {}{} were provided",
36 expected_arg_count,
37 only,
38 given_arg_count
39 )
40 }
41
42 let mut exprs = vec![];
43 let mut patterns = vec![];
44
45 for arg_index in 0..expected_arg_count {
46 let arg_ident = format_ident!("arg{}", arg_index);
47 let matching_param = params
48 .iter()
49 .find(|param| param.index == arg_index)
50 .unwrap();
51
52 let expr = encode_arg(&matching_param.ty, &params, arg_index, &arg_ident);
53
54 exprs.push(expr);
55 patterns.push(arg_ident);
56 }
57
58 Codegen { exprs, patterns }
59 }
60}
61
62fn encode_arg(ty: &Type, params: &[Parameter], arg_index: usize, arg: &Ident2) -> TokenStream2 {
63 match ty {
64 Type::I8 => quote!(defmt::export::i8(#arg)),
65 Type::I16 => quote!(defmt::export::i16(#arg)),
66 Type::I32 => quote!(defmt::export::i32(#arg)),
67 Type::I64 => quote!(defmt::export::i64(#arg)),
68 Type::I128 => quote!(defmt::export::i128(#arg)),
69 Type::Isize => quote!(defmt::export::isize(#arg)),
70
71 Type::U8 => quote!(defmt::export::u8(#arg)),
72 Type::U16 => quote!(defmt::export::u16(#arg)),
73 Type::U32 => quote!(defmt::export::u32(#arg)),
74 Type::U64 => quote!(defmt::export::u64(#arg)),
75 Type::U128 => quote!(defmt::export::u128(#arg)),
76 Type::Usize => quote!(defmt::export::usize(#arg)),
77
78 Type::F32 => quote!(defmt::export::f32(#arg)),
79 Type::F64 => quote!(defmt::export::f64(#arg)),
80
81 Type::Bool => quote!(defmt::export::bool(#arg)),
82
83 Type::Str => quote!(defmt::export::str(#arg)),
84 Type::IStr => quote!(defmt::export::istr(#arg)),
85 Type::Char => quote!(defmt::export::char(#arg)),
86
87 Type::Format => quote!(defmt::export::fmt(#arg)),
88 Type::FormatSlice => quote!(defmt::export::fmt_slice(#arg)),
89 Type::FormatArray(len) => quote!(defmt::export::fmt_array({
90 let tmp: &[_; #len] = #arg;
91 tmp
92 })),
93
94 Type::Debug => quote!(defmt::export::debug(#arg)),
95 Type::Display => quote!(defmt::export::display(#arg)),
96 Type::FormatSequence => unreachable!(),
97
98 Type::U8Slice => quote!(defmt::export::slice(#arg)),
99
100 // We cast to the expected array type (which should be a no-op cast) to provoke
101 // a type mismatch error on mismatched lengths:
102 // ``Symbol’s value as variable is void: //
103 Type::U8Array(len) => quote!(defmt::export::u8_array({
104 let tmp: &[u8; #len] = #arg;
105 tmp
106 })),
107
108 Type::BitField(_) => {
109 let all_bitfields = params.iter().filter(|param| param.index == arg_index);
110 let (smallest_bit_index, largest_bit_index) =
111 defmt_parser::get_max_bitfield_range(all_bitfields).unwrap();
112
113 // indices of the lowest and the highest octet which contains bitfield-relevant data
114 let lowest_byte = smallest_bit_index / 8;
115 let highest_byte = (largest_bit_index - 1) / 8;
116 let truncated_sz = highest_byte - lowest_byte + 1; // in bytes
117
118 // shift away unneeded lower octet
119 // TODO: create helper for shifting because readability
120 match truncated_sz {
121 1 => {
122 quote!(defmt::export::u8(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8))))
123 }
124 2 => {
125 quote!(defmt::export::u16(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8))))
126 }
127 3..=4 => {
128 quote!(defmt::export::u32(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8))))
129 }
130 5..=8 => {
131 quote!(defmt::export::u64(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8))))
132 }
133 9..=16 => {
134 quote!(defmt::export::u128(&defmt::export::truncate((*#arg) >> (#lowest_byte * 8))))
135 }
136 _ => unreachable!(),
137 }
138 }
139 }
140}
141