Skip to content

Commit e09c27d

Browse files
authored
Merge pull request #44 from de-vri-es/debug-and-defmt
Implement Debug and defmt::Format for fieldsets, enums and the interrupt enum.
2 parents d5ec99b + de3e854 commit e09c27d

File tree

9 files changed

+260
-23
lines changed

9 files changed

+260
-23
lines changed

src/generate/device.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub fn render_device_x(_ir: &IR, d: &Device) -> Result<String> {
1717
Ok(device_x)
1818
}
1919

20-
pub fn render(_opts: &super::Options, ir: &IR, d: &Device, path: &str) -> Result<TokenStream> {
20+
pub fn render(opts: &super::Options, ir: &IR, d: &Device, path: &str) -> Result<TokenStream> {
2121
let mut out = TokenStream::new();
2222
let span = Span::call_site();
2323

@@ -77,8 +77,16 @@ pub fn render(_opts: &super::Options, ir: &IR, d: &Device, path: &str) -> Result
7777
}
7878
}
7979
let n = util::unsuffixed(pos as u64);
80+
81+
let defmt = opts.defmt_feature.as_ref().map(|defmt_feature| {
82+
quote! {
83+
#[cfg_attr(feature = #defmt_feature, derive(defmt::Format))]
84+
}
85+
});
86+
8087
out.extend(quote!(
8188
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
89+
#defmt
8290
pub enum Interrupt {
8391
#interrupts
8492
}

src/generate/enumm.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::util;
1010

1111
use super::sorted;
1212

13-
pub fn render(_opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<TokenStream> {
13+
pub fn render(opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<TokenStream> {
1414
let span = Span::call_site();
1515

1616
// For very "sparse" enums, generate a newtype wrapping the uX.
@@ -33,17 +33,39 @@ pub fn render(_opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<
3333

3434
if newtype {
3535
let mut items = TokenStream::new();
36+
let mut item_names_str = Vec::with_capacity(e.variants.len());
37+
let mut item_values = Vec::with_capacity(e.variants.len());
3638

3739
for f in sorted(&e.variants, |f| (f.value, f.name.clone())) {
3840
let name = Ident::new(&f.name, span);
3941
let value = util::hex(f.value);
42+
43+
item_names_str.push(&f.name);
44+
item_values.push(value.clone());
45+
4046
let doc = util::doc(&f.description);
4147
items.extend(quote!(
4248
#doc
4349
pub const #name: Self = Self(#value);
4450
));
4551
}
4652

53+
let defmt = opts.defmt_feature.as_ref().map(|defmt_feature| {
54+
quote! {
55+
#[cfg(feature = #defmt_feature)]
56+
impl defmt::Format for #name {
57+
fn format(&self, f: defmt::Formatter) {
58+
match self.0 {
59+
#(
60+
#item_values => defmt::write!(f, #item_names_str),
61+
)*
62+
other => defmt::write!(f, "0x{:02X}", other),
63+
}
64+
}
65+
}
66+
}
67+
});
68+
4769
out.extend(quote! {
4870
#doc
4971
#[repr(transparent)]
@@ -63,6 +85,20 @@ pub fn render(_opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<
6385
self.0
6486
}
6587
}
88+
89+
impl core::fmt::Debug for #name {
90+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
91+
match self.0 {
92+
#(
93+
#item_values => f.write_str(#item_names_str),
94+
)*
95+
other => core::write!(f, "0x{:02X}", other),
96+
}
97+
}
98+
}
99+
100+
#defmt
101+
66102
});
67103
} else {
68104
let variants: BTreeMap<_, _> = e.variants.iter().map(|v| (v.value, v)).collect();
@@ -85,10 +121,17 @@ pub fn render(_opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<
85121
}
86122
}
87123

124+
let defmt = opts.defmt_feature.as_ref().map(|defmt_feature| {
125+
quote! {
126+
#[cfg_attr(feature = #defmt_feature, derive(defmt::Format))]
127+
}
128+
});
129+
88130
out.extend(quote! {
89131
#doc
90132
#[repr(#ty)]
91-
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
133+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
134+
#defmt
92135
pub enum #name {
93136
#items
94137
}

src/generate/fieldset.rs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
use anyhow::Result;
22
use proc_macro2::TokenStream;
3-
use proc_macro2::{Ident, Span};
3+
use proc_macro2::{Ident, Literal, Span};
44
use quote::quote;
55

66
use crate::ir::*;
77
use crate::util;
88

99
use super::sorted;
1010

11-
pub fn render(_opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Result<TokenStream> {
11+
pub fn render(opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Result<TokenStream> {
1212
let span = Span::call_site();
1313
let mut items = TokenStream::new();
14+
let mut field_names = Vec::with_capacity(fs.fields.len());
15+
let mut field_names_str = Vec::with_capacity(fs.fields.len());
16+
let mut field_getters = Vec::with_capacity(fs.fields.len());
17+
let mut field_types = Vec::with_capacity(fs.fields.len());
1418

1519
let ty = match fs.bit_size {
1620
1..=8 => quote!(u8),
@@ -64,6 +68,18 @@ pub fn render(_opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Res
6468
}
6569
}
6670

71+
field_names.push(name.clone());
72+
field_names_str.push(f.name.as_str());
73+
if let Some(array) = &f.array {
74+
let len = array.len();
75+
let i = 0..len;
76+
field_types.push(quote!([#field_ty; #len]));
77+
field_getters.push(quote!([#( self.#name(#i), )*]));
78+
} else {
79+
field_types.push(field_ty.clone());
80+
field_getters.push(quote!(self.#name()));
81+
}
82+
6783
match off_in_reg {
6884
BitOffset::Regular(off_in_reg) => {
6985
let off_in_reg = off_in_reg as usize;
@@ -165,9 +181,36 @@ pub fn render(_opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Res
165181
}
166182

167183
let (_, name) = super::split_path(path);
184+
let name_str = {
185+
let mut literal = Literal::string(name);
186+
literal.set_span(span);
187+
literal
188+
};
168189
let name = Ident::new(name, span);
169190
let doc = util::doc(&fs.description);
170191

192+
let impl_defmt_format = opts.defmt_feature.as_ref().map(|defmt_feature| {
193+
quote! {
194+
#[cfg(feature = #defmt_feature)]
195+
impl defmt::Format for #name {
196+
fn format(&self, f: defmt::Formatter) {
197+
#[derive(defmt::Format)]
198+
struct #name {
199+
#(
200+
#field_names: #field_types,
201+
)*
202+
}
203+
let proxy = #name {
204+
#(
205+
#field_names: #field_getters,
206+
)*
207+
};
208+
defmt::write!(f, "{}", proxy)
209+
}
210+
}
211+
}
212+
});
213+
171214
let out = quote! {
172215
#doc
173216
#[repr(transparent)]
@@ -184,6 +227,18 @@ pub fn render(_opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Res
184227
#name(0)
185228
}
186229
}
230+
231+
impl core::fmt::Debug for #name {
232+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
233+
f.debug_struct(#name_str)
234+
#(
235+
.field(#field_names_str, &#field_getters)
236+
)*
237+
.finish()
238+
}
239+
}
240+
241+
#impl_defmt_format
187242
};
188243

189244
Ok(out)

src/generate/mod.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,76 @@ impl Module {
5959
}
6060
}
6161

62+
#[derive(Debug, Default)]
6263
pub enum CommonModule {
64+
#[default]
6365
Builtin,
6466
External(TokenStream),
6567
}
6668

69+
/// Options for the code generator.
70+
///
71+
/// See the individual methods for the different options you can change.
72+
#[derive(Debug)]
6773
pub struct Options {
68-
pub common_module: CommonModule,
74+
common_module: CommonModule,
75+
defmt_feature: Option<String>,
76+
}
77+
78+
impl Default for Options {
79+
fn default() -> Self {
80+
Self::new()
81+
}
6982
}
7083

7184
impl Options {
85+
/// Create new options with all values set to the default.
86+
///
87+
/// This will use a builtin common module,
88+
/// and adds `defmt` support to the generated code gated behind a `feature = "defmt"` flag.
89+
pub fn new() -> Self {
90+
Self {
91+
common_module: CommonModule::Builtin,
92+
defmt_feature: Some("defmt".into()),
93+
}
94+
}
95+
96+
/// Get the path to the common module.
7297
fn common_path(&self) -> TokenStream {
7398
match &self.common_module {
7499
CommonModule::Builtin => TokenStream::from_str("crate::common").unwrap(),
75100
CommonModule::External(path) => path.clone(),
76101
}
77102
}
103+
104+
/// Get the configuration of the common module.
105+
pub fn common_module(&self) -> &CommonModule {
106+
&self.common_module
107+
}
108+
109+
/// Set the common module to use.
110+
///
111+
/// Specify [`CommonModule::Builtin`] for a built-in common module,
112+
/// or [`CommonModule::External`] to use an external common module.
113+
pub fn with_common_module(mut self, common_module: CommonModule) -> Self {
114+
self.common_module = common_module;
115+
self
116+
}
117+
118+
/// Set the feature for adding defmt support in the generated code.
119+
///
120+
/// You can fully remove `defmt` support in the generated code by specifying `None`.
121+
pub fn with_defmt_feature(mut self, defmt_feature: Option<String>) -> Self {
122+
self.defmt_feature = defmt_feature;
123+
self
124+
}
125+
126+
/// Get the feature flag used to enable/disable `defmt` support in the generated code.
127+
///
128+
/// If set to `None`, no `defmt` support will be added at all to the generated code.
129+
pub fn defmt_feature(&self) -> Option<&str> {
130+
self.defmt_feature.as_deref()
131+
}
78132
}
79133

80134
pub fn render(ir: &IR, opts: &Options) -> Result<TokenStream> {

src/ir.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ pub enum Array {
9999
Cursed(CursedArray),
100100
}
101101

102+
impl Array {
103+
/// Get the number of elements in the array.
104+
pub fn len(&self) -> usize {
105+
match self {
106+
Self::Regular(x) => x.len as usize,
107+
Self::Cursed(x) => x.offsets.len(),
108+
}
109+
}
110+
}
111+
102112
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
103113
pub struct RegularArray {
104114
pub len: u32,

0 commit comments

Comments
 (0)