Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bitbybit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ introspect = []
extra-traits = ["syn/extra-traits"]
# Required for the field example to work.
examples = ["arbitrary-int/defmt"]
default = ["extra-traits"]

[dependencies]
syn = { version = "2.0", features = ["full"] }
Expand Down
83 changes: 74 additions & 9 deletions bitbybit/src/bitfield/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const_name, mask_name, setter_name, with_name, ArrayInfo, BaseDataSize, BitfieldAttributes,
CustomType, DefmtVariant, FieldDefinition, BITCOUNT_BOOL,
};
use proc_macro2::{Ident, TokenStream as TokenStream2, TokenStream, TokenTree};
use proc_macro2::{Ident, Literal, TokenStream as TokenStream2, TokenStream, TokenTree};

Check warning on line 5 in bitbybit/src/bitfield/codegen.rs

View workflow job for this annotation

GitHub Actions / build-and-test

unused import: `Literal`

Check warning on line 5 in bitbybit/src/bitfield/codegen.rs

View workflow job for this annotation

GitHub Actions / build-and-test (with-defmt, --release --no-default-features --features defmt)

unused import: `Literal`

Check warning on line 5 in bitbybit/src/bitfield/codegen.rs

View workflow job for this annotation

GitHub Actions / build-and-test (without-defmt, --release --no-default-features)

unused import: `Literal`
use quote::{quote, TokenStreamExt as _};
use std::ops::Range;
use std::str::FromStr;
Expand Down Expand Up @@ -121,7 +121,8 @@
quote! {}
};

let setter = if let Some(setter_type) = field_definition.setter_type.as_ref() {
let setter = {
let setter_type = field_definition.setter_type.clone() ;
let argument_converted =
match field_definition.custom_type {
CustomType::No => {
Expand Down Expand Up @@ -159,20 +160,30 @@
let setter_name = setter_name(field_name);
let with_name = with_name(field_name);

let public_status = if field_definition.setter_is_public {
quote! {
pub
}
} else {
quote!{

}
} ;

if let Some(array) = field_definition.array {
let indexed_count = array.count;
quote! {
#(#doc_comment)*
#[inline]
pub const fn #with_name(&self, index: usize, field_value: #setter_type) -> Self {
#public_status const fn #with_name(&self, index: usize, field_value: #setter_type) -> Self {
assert!(index < #indexed_count);
Self {
raw_value: #new_raw_value
}
}
#(#doc_comment)*
#[inline]
pub fn #setter_name(&mut self, index: usize, field_value: #setter_type) {
#public_status fn #setter_name(&mut self, index: usize, field_value: #setter_type) {
assert!(index < #indexed_count);
self.raw_value = #new_raw_value;
}
Expand All @@ -181,21 +192,22 @@
quote! {
#(#doc_comment)*
#[inline]
pub const fn #with_name(&self, field_value: #setter_type) -> Self {
#public_status const fn #with_name(&self, field_value: #setter_type) -> Self {
Self {
raw_value: #new_raw_value
}
}
#(#doc_comment)*
#[inline]
pub fn #setter_name(&mut self, field_value: #setter_type) {
#public_status fn #setter_name(&mut self, field_value: #setter_type) {
self.raw_value = #new_raw_value;
}
}
}
} else {
quote! {}
};




quote! {
#introspect
Expand All @@ -207,6 +219,58 @@
accessors
}

pub fn generate_field_defaults(field_definitions: &[FieldDefinition]) -> TokenStream {
let mut default_constructor = Vec::new();
let mut default_consts = Vec::new();

let default_from_fields = super::DefaultVal::UseFieldDefault ;
default_constructor.push(quote! {
pub const #default_from_fields: Self = Self::ZERO
});
for field_definition in field_definitions {
if let Some(default_value) = &field_definition.default_value {
let field_name = &field_definition.field_name;
let func_name = with_name(field_name);
let default_const = const_name(field_name, "DEFAULT");

let field_type = field_definition.setter_type.clone();
let converted_field_value = default_value;

// 3 cases: either a custom-type, or a basic Rust number type, or an arbitrary-int type
let const_def = if let CustomType::Yes(_) = field_definition.custom_type {
quote! {
// Custom type
pub const #default_const: #field_type = #converted_field_value;
}
}
else if field_definition.use_regular_int {
// Basic Rust number type
quote! {
pub const #default_const: #field_type = #converted_field_value;
}
} else {
// Arbitrary-int are created with ::new()
quote! {
pub const #default_const: #field_type = #field_type::new(#converted_field_value);
}
};

default_constructor.push(quote! {
.#func_name(Self::#default_const)
});
default_consts.push(const_def);
}
}
default_constructor.push(quote! {
;
});

quote! {
#(#default_consts)*
#(#default_constructor)*
}
}

/// If there are multiple ranges, this packs them together
fn getter_packed(
field_definition: &FieldDefinition,
Expand Down Expand Up @@ -436,7 +500,8 @@
});

for field_definition in field_definitions {
if let Some(setter_type) = field_definition.setter_type.as_ref() {
if field_definition.setter_is_public {
let setter_type = field_definition.setter_type.clone() ;
let field_name = &field_definition.field_name;
let with_name = with_name(field_name);

Expand Down
109 changes: 82 additions & 27 deletions bitbybit/src/bitfield/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use proc_macro2::Ident;
use proc_macro2::TokenStream as TokenStream2;
use quote::TokenStreamExt;
use quote::{quote, ToTokens};
use syn::Expr;
use std::ops::Range;
use std::str::FromStr;
use syn::meta::ParseNestedMeta;
Expand Down Expand Up @@ -66,14 +67,16 @@ struct FieldDefinition {
array: Option<ArrayInfo>,
field_type_size: usize,
getter_type: Option<Type>,
setter_type: Option<Type>,
setter_type: Type,
setter_is_public: bool,
field_type_size_from_data_type: Option<usize>,
is_signed: bool,
/// If non-null: (count, stride)
use_regular_int: bool,
primitive_type: TokenStream2,
custom_type: CustomType,
doc_comment: Vec<Attribute>,
default_value: Option<DefaultVal>, //TODO - check if it's the correct type
}

// If a convert_type is given, that will be the final getter/setter type. If not, it is the base type
Expand Down Expand Up @@ -115,17 +118,24 @@ impl BaseDataSize {
}
}

#[cfg_attr(feature = "extra-traits", derive(Debug))]
#[cfg_attr(feature = "extra-traits", derive(Debug, Clone, PartialEq, Eq))]
pub enum DefaultVal {
Lit(LitInt),
Constant(Ident),
Expr(Expr),
UseFieldDefault,
}

impl ToTokens for DefaultVal {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
DefaultVal::Lit(lit) => lit.to_tokens(tokens),
DefaultVal::Constant(ident) => ident.to_tokens(tokens),
DefaultVal::Expr(expr) => expr.to_tokens(tokens),
DefaultVal::UseFieldDefault => {
syn::parse_str::<Ident>("DEFAULTS_FROM_FIELDS")
.unwrap_or_else(|_| panic!("bitfield!: Error creating setter name"))
}.to_tokens(tokens),
}
}
}
Expand Down Expand Up @@ -176,7 +186,12 @@ impl BitfieldAttributes {
}
let path: Result<Ident, syn::Error> = stream.parse();
if let Ok(path) = path {
self.default_val = Some(DefaultVal::Constant(path));
if path == "use_field_defaults" {
self.default_val = Some(DefaultVal::UseFieldDefault);
} else {
self.default_val = Some(DefaultVal::Constant(path));
}

return Ok(());
}
return Ok(());
Expand Down Expand Up @@ -305,29 +320,54 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
let (default_constructor, default_trait) = if let Some(default_value) =
&bitfield_attrs.default_val
{
let default_value = default_value.to_token_stream();
let constructor = {
let comment = format!("An instance that uses the default value {}", default_value);
let deprecated_warning = format!(
"Use {}::Default (or {}::DEFAULT in const context) instead",
struct_name, struct_name
);

let default_raw_value = if base_data_size.exposed == base_data_size.internal {
quote! { const DEFAULT_RAW_VALUE: #base_data_type = #default_value; }
} else {
quote! { const DEFAULT_RAW_VALUE: #base_data_type = #base_data_type::new(#default_value); }
};
quote! {
#default_raw_value

#[doc = #comment]
pub const DEFAULT: Self = Self::new_with_raw_value(Self::DEFAULT_RAW_VALUE);

/// Creates a new instance of this struct using the default value
#[deprecated(note = #deprecated_warning)]
pub const fn new() -> Self {
Self::DEFAULT
let constructor = match default_value {
DefaultVal::UseFieldDefault => {
let default_value = default_value.to_token_stream();
let comment = format!("An instance that uses the default value {}", default_value);
let deprecated_warning = format!(
"Use {}::Default (or {}::DEFAULT in const context) instead",
struct_name, struct_name
);

quote! {
#[doc = #comment]
pub const DEFAULT: Self = Self::#default_value;

/// Creates a new instance of this struct using the default value
#[deprecated(note = #deprecated_warning)]
pub const fn new() -> Self {
Self::DEFAULT
}

#[doc = #comment]
const DEFAULT_RAW_VALUE: #base_data_type = Self::#default_value.raw_value();
}
}
_ => {
let default_value = default_value.to_token_stream();

let comment = format!("An instance that uses the default value {}", default_value);
let deprecated_warning = format!(
"Use {}::Default (or {}::DEFAULT in const context) instead",
struct_name, struct_name
);

let default_raw_value = if base_data_size.exposed == base_data_size.internal {
quote! { const DEFAULT_RAW_VALUE: #base_data_type = #default_value; }
} else {
quote! { const DEFAULT_RAW_VALUE: #base_data_type = #base_data_type::new(#default_value); }
};
quote! {
#default_raw_value

#[doc = #comment]
pub const DEFAULT: Self = Self::new_with_raw_value(Self::DEFAULT_RAW_VALUE);

/// Creates a new instance of this struct using the default value
#[deprecated(note = #deprecated_warning)]
pub const fn new() -> Self {
Self::DEFAULT
}
}
}
};
Expand Down Expand Up @@ -412,6 +452,11 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {
zero
);

//TODO - put this behing a feature gate
let field_defaults = codegen::generate_field_defaults(&field_definitions);
let field_defaults_module = syn::parse_str::<Type>(format!("{}Implementation", struct_name).as_str())
.unwrap_or_else(|_| panic!("bitfield!: Error parsing internal base data type"));

let expanded = quote! {
#[derive(Copy, Clone)]
#[repr(C)]
Expand All @@ -438,8 +483,18 @@ pub fn bitfield(args: TokenStream, input: TokenStream) -> TokenStream {

#new_with_constructor

#( #accessors )*
}


mod #field_defaults_module {
use super::* ;
impl super::#struct_name {
#( #accessors )*

#field_defaults
}
}

#default_trait

#debug_trait
Expand Down
Loading
Loading