| 1 | use defmt_parser::{Fragment, Parameter, Type}; |
| 2 | use proc_macro2::{Ident as Ident2, Span as Span2, TokenStream as TokenStream2}; |
| 3 | use proc_macro_error2::abort; |
| 4 | use quote::{format_ident, quote}; |
| 5 | |
| 6 | pub(crate) struct Codegen { |
| 7 | pub(crate) exprs: Vec<TokenStream2>, |
| 8 | pub(crate) patterns: Vec<Ident2>, |
| 9 | } |
| 10 | |
| 11 | impl 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, ¶ms, arg_index, &arg_ident); |
| 53 | |
| 54 | exprs.push(expr); |
| 55 | patterns.push(arg_ident); |
| 56 | } |
| 57 | |
| 58 | Codegen { exprs, patterns } |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | fn 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 | |