Skip to content

Commit

Permalink
Add option to totally remove defmt support from generated code.
Browse files Browse the repository at this point in the history
  • Loading branch information
de-vri-es committed Jan 2, 2025
1 parent 7af7ceb commit de3e854
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 38 deletions.
10 changes: 8 additions & 2 deletions 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 @@ -78,9 +78,15 @@ 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)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#defmt
pub enum Interrupt {
#interrupts
}
Expand Down
39 changes: 26 additions & 13 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 Down Expand Up @@ -50,6 +50,22 @@ 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(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 Down Expand Up @@ -81,17 +97,8 @@ pub fn render(_opts: &super::Options, _ir: &IR, e: &Enum, path: &str) -> Result<
}
}

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

});
} else {
let variants: BTreeMap<_, _> = e.variants.iter().map(|v| (v.value, v)).collect();
Expand All @@ -114,11 +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, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#defmt
pub enum #name {
#items
}
Expand Down
42 changes: 24 additions & 18 deletions src/generate/fieldset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ 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());
Expand Down Expand Up @@ -189,6 +189,28 @@ pub fn render(_opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Res
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 Down Expand Up @@ -216,23 +238,7 @@ pub fn render(_opts: &super::Options, ir: &IR, fs: &FieldSet, path: &str) -> Res
}
}

#[cfg(feature = "defmt")]
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)
}
}
#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
76 changes: 72 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ struct Generate {
/// Transforms file path
#[clap(long)]
transform: Vec<String>,
/// Use an external `common` module.
#[clap(long)]
#[clap(value_name = "MODULE_PATH")]
common_module: Option<ModulePath>,
/// Specify the feature name used in the generated code to conditionally enable defmt support.
#[clap(long)]
#[clap(value_name = "FEATURE")]
#[clap(default_value = "defmt")]
#[clap(conflicts_with = "no_defmt")]
defmt_feature: String,
/// Do not add defmt support to the generated code at all.
#[clap(long)]
no_defmt: bool,
}

/// Reformat a YAML
Expand Down Expand Up @@ -122,6 +135,10 @@ struct GenBlock {
/// Output YAML path
#[clap(short, long)]
output: String,
/// Use an external `common` module.
#[clap(long)]
#[clap(value_name = "MODULE_PATH")]
common_module: Option<ModulePath>,
}

fn main() -> Result<()> {
Expand Down Expand Up @@ -256,9 +273,17 @@ fn gen(args: Generate) -> Result<()> {
apply_transform(&mut ir, transform)?;
}

let generate_opts = generate::Options {
common_module: generate::CommonModule::Builtin,
let common_module = match args.common_module {
None => generate::CommonModule::Builtin,
Some(module) => generate::CommonModule::External(module.tokens()),
};
let defmt_feature = match args.no_defmt {
true => None,
false => Some(args.defmt_feature),
};
let generate_opts = generate::Options::default()
.with_common_module(common_module)
.with_defmt_feature(defmt_feature);
let items = generate::render(&ir, &generate_opts).unwrap();
fs::write("lib.rs", items.to_string())?;

Expand Down Expand Up @@ -379,9 +404,11 @@ fn gen_block(args: GenBlock) -> Result<()> {
// Ensure consistent sort order in the YAML.
chiptool::transform::sort::Sort {}.run(&mut ir).unwrap();

let generate_opts = generate::Options {
common_module: generate::CommonModule::Builtin,
let common_module = match args.common_module {
None => generate::CommonModule::Builtin,
Some(module) => generate::CommonModule::External(module.tokens()),
};
let generate_opts = generate::Options::default().with_common_module(common_module);
let items = generate::render(&ir, &generate_opts).unwrap();
fs::write(&args.output, items.to_string())?;

Expand Down Expand Up @@ -410,3 +437,44 @@ fn apply_transform<P: AsRef<std::path::Path>>(ir: &mut IR, p: P) -> anyhow::Resu

Ok(())
}

/// Struct holding a valid module path as a string.
///
/// Implements `FromStr` so it can be used directly as command line argument.
#[derive(Clone)]
struct ModulePath {
path: String,
}

impl ModulePath {
/// Get the module path as a TokenStream.
fn tokens(&self) -> proc_macro2::TokenStream {
self.path.parse().unwrap()
}
}

impl std::str::FromStr for ModulePath {
type Err = anyhow::Error;

fn from_str(data: &str) -> Result<Self, Self::Err> {
data.parse::<proc_macro2::TokenStream>()
.map_err(|e| anyhow::anyhow!("{e}"))?;

for (i, component) in data.split("::").enumerate() {
if component.is_empty() && i != 0 {
anyhow::bail!("path components can not be empty")
}
for (i, c) in component.chars().enumerate() {
if c.is_alphabetic() || c == '_' {
continue;
}
if i > 0 && c.is_alphanumeric() {
continue;
}
anyhow::bail!("path components may only consist of letters, digits and underscore")
}
}

Ok(Self { path: data.into() })
}
}

0 comments on commit de3e854

Please sign in to comment.