Skip to content

Commit

Permalink
Merge pull request #44 from de-vri-es/debug-and-defmt
Browse files Browse the repository at this point in the history
Implement Debug and defmt::Format for fieldsets, enums and the interrupt enum.
  • Loading branch information
Dirbaio authored Jan 2, 2025
2 parents d5ec99b + de3e854 commit e09c27d
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 23 deletions.
10 changes: 9 additions & 1 deletion src/generate/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub fn render_device_x(_ir: &IR, d: &Device) -> Result<String> {
Ok(device_x)
}

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

Expand Down Expand Up @@ -77,8 +77,16 @@ pub fn render(_opts: &super::Options, ir: &IR, d: &Device, path: &str) -> Result
}
}
let n = util::unsuffixed(pos as u64);

let defmt = opts.defmt_feature.as_ref().map(|defmt_feature| {
quote! {
#[cfg_attr(feature = #defmt_feature, derive(defmt::Format))]
}
});

out.extend(quote!(
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#defmt
pub enum Interrupt {
#interrupts
}
Expand Down
47 changes: 45 additions & 2 deletions src/generate/enumm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::util;

use super::sorted;

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

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

if newtype {
let mut items = TokenStream::new();
let mut item_names_str = Vec::with_capacity(e.variants.len());
let mut item_values = Vec::with_capacity(e.variants.len());

for f in sorted(&e.variants, |f| (f.value, f.name.clone())) {
let name = Ident::new(&f.name, span);
let value = util::hex(f.value);

item_names_str.push(&f.name);
item_values.push(value.clone());

let doc = util::doc(&f.description);
items.extend(quote!(
#doc
pub const #name: Self = Self(#value);
));
}

let defmt = opts.defmt_feature.as_ref().map(|defmt_feature| {
quote! {
#[cfg(feature = #defmt_feature)]
impl defmt::Format for #name {
fn format(&self, f: defmt::Formatter) {
match self.0 {
#(
#item_values => defmt::write!(f, #item_names_str),
)*
other => defmt::write!(f, "0x{:02X}", other),
}
}
}
}
});

out.extend(quote! {
#doc
#[repr(transparent)]
Expand All @@ -63,6 +85,20 @@ pub fn render(_opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<
self.0
}
}

impl core::fmt::Debug for #name {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self.0 {
#(
#item_values => f.write_str(#item_names_str),
)*
other => core::write!(f, "0x{:02X}", other),
}
}
}

#defmt

});
} else {
let variants: BTreeMap<_, _> = e.variants.iter().map(|v| (v.value, v)).collect();
Expand All @@ -85,10 +121,17 @@ pub fn render(_opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<
}
}

let defmt = opts.defmt_feature.as_ref().map(|defmt_feature| {
quote! {
#[cfg_attr(feature = #defmt_feature, derive(defmt::Format))]
}
});

out.extend(quote! {
#doc
#[repr(#ty)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#defmt
pub enum #name {
#items
}
Expand Down
59 changes: 57 additions & 2 deletions src/generate/fieldset.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
use anyhow::Result;
use proc_macro2::TokenStream;
use proc_macro2::{Ident, Span};
use proc_macro2::{Ident, Literal, Span};
use quote::quote;

use crate::ir::*;
use crate::util;

use super::sorted;

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

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

field_names.push(name.clone());
field_names_str.push(f.name.as_str());
if let Some(array) = &f.array {
let len = array.len();
let i = 0..len;
field_types.push(quote!([#field_ty; #len]));
field_getters.push(quote!([#( self.#name(#i), )*]));
} else {
field_types.push(field_ty.clone());
field_getters.push(quote!(self.#name()));
}

match off_in_reg {
BitOffset::Regular(off_in_reg) => {
let off_in_reg = off_in_reg as usize;
Expand Down Expand Up @@ -165,9 +181,36 @@ pub fn render(_opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Res
}

let (_, name) = super::split_path(path);
let name_str = {
let mut literal = Literal::string(name);
literal.set_span(span);
literal
};
let name = Ident::new(name, span);
let doc = util::doc(&fs.description);

let impl_defmt_format = opts.defmt_feature.as_ref().map(|defmt_feature| {
quote! {
#[cfg(feature = #defmt_feature)]
impl defmt::Format for #name {
fn format(&self, f: defmt::Formatter) {
#[derive(defmt::Format)]
struct #name {
#(
#field_names: #field_types,
)*
}
let proxy = #name {
#(
#field_names: #field_getters,
)*
};
defmt::write!(f, "{}", proxy)
}
}
}
});

let out = quote! {
#doc
#[repr(transparent)]
Expand All @@ -184,6 +227,18 @@ pub fn render(_opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Res
#name(0)
}
}

impl core::fmt::Debug for #name {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct(#name_str)
#(
.field(#field_names_str, &#field_getters)
)*
.finish()
}
}

#impl_defmt_format
};

Ok(out)
Expand Down
56 changes: 55 additions & 1 deletion src/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,76 @@ impl Module {
}
}

#[derive(Debug, Default)]
pub enum CommonModule {
#[default]
Builtin,
External(TokenStream),
}

/// Options for the code generator.
///
/// See the individual methods for the different options you can change.
#[derive(Debug)]
pub struct Options {
pub common_module: CommonModule,
common_module: CommonModule,
defmt_feature: Option<String>,
}

impl Default for Options {
fn default() -> Self {
Self::new()
}
}

impl Options {
/// Create new options with all values set to the default.
///
/// This will use a builtin common module,
/// and adds `defmt` support to the generated code gated behind a `feature = "defmt"` flag.
pub fn new() -> Self {
Self {
common_module: CommonModule::Builtin,
defmt_feature: Some("defmt".into()),
}
}

/// Get the path to the common module.
fn common_path(&self) -> TokenStream {
match &self.common_module {
CommonModule::Builtin => TokenStream::from_str("crate::common").unwrap(),
CommonModule::External(path) => path.clone(),
}
}

/// Get the configuration of the common module.
pub fn common_module(&self) -> &CommonModule {
&self.common_module
}

/// Set the common module to use.
///
/// Specify [`CommonModule::Builtin`] for a built-in common module,
/// or [`CommonModule::External`] to use an external common module.
pub fn with_common_module(mut self, common_module: CommonModule) -> Self {
self.common_module = common_module;
self
}

/// Set the feature for adding defmt support in the generated code.
///
/// You can fully remove `defmt` support in the generated code by specifying `None`.
pub fn with_defmt_feature(mut self, defmt_feature: Option<String>) -> Self {
self.defmt_feature = defmt_feature;
self
}

/// Get the feature flag used to enable/disable `defmt` support in the generated code.
///
/// If set to `None`, no `defmt` support will be added at all to the generated code.
pub fn defmt_feature(&self) -> Option<&str> {
self.defmt_feature.as_deref()
}
}

pub fn render(ir: &IR, opts: &Options) -> Result<TokenStream> {
Expand Down
10 changes: 10 additions & 0 deletions src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ pub enum Array {
Cursed(CursedArray),
}

impl Array {
/// Get the number of elements in the array.
pub fn len(&self) -> usize {
match self {
Self::Regular(x) => x.len as usize,
Self::Cursed(x) => x.offsets.len(),
}
}
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct RegularArray {
pub len: u32,
Expand Down
Loading

0 comments on commit e09c27d

Please sign in to comment.