use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use crate::parser::{EnumDefinition, EnumPattern};
pub fn generate_enum(enum_def: &EnumDefinition) -> syn::Result<TokenStream2> {
let enum_name = &enum_def.name;
let underlying_type = &enum_def.underlying_type;
let attributes = &enum_def.attributes;
let variants = &enum_def.variants;
let bit_size = enum_def.get_effective_bit_size()?;
let mut enum_variants = Vec::new();
let mut from_unsigned_arms = Vec::new();
let mut to_unsigned_arms = Vec::new();
for variant in variants {
let variant_name = &variant.name;
let variant_attrs = &variant.attributes;
if let Some(cap_type) = &variant.capture_value {
let pattern = generate_pattern(&variant.pattern);
let doc = format!("Value pattern: `{pattern}`\n\nCaptures the specific value.");
enum_variants.push(quote! {
#(#variant_attrs)*
#[doc = ""]
#[doc = #doc]
#variant_name(#cap_type)
});
from_unsigned_arms.push(quote! {
#pattern => Self::#variant_name(value.into())
});
to_unsigned_arms.push(quote! {
Self::#variant_name(v) => *v as #underlying_type
});
} else {
let pattern = generate_pattern(&variant.pattern);
let doc = format!(
"Value pattern: `{}`\n\nRepresentative value: `{}`",
pattern, variant.representative
);
enum_variants.push(quote! {
#(#variant_attrs)*
#[doc = ""]
#[doc = #doc]
#variant_name
});
from_unsigned_arms.push(quote! {
#pattern => Self::#variant_name
});
let representative = &variant.representative;
to_unsigned_arms.push(quote! {
Self::#variant_name => #representative
});
}
}
let enum_doc = format!("Enum width: {bit_size} bits");
let derive_attrs = if cfg!(feature = "defmt") {
quote! {
#[derive(Copy, Clone, PartialEq, Eq, core::fmt::Debug, defmt::Format)]
}
} else {
quote! {
#[derive(Copy, Clone, PartialEq, Eq, core::fmt::Debug)]
}
};
let enum_def_code = quote! {
#(#attributes)*
#[doc = ""]
#[doc = #enum_doc]
#derive_attrs
pub enum #enum_name {
#(#enum_variants,)*
}
};
let has_wildcard = variants.iter().any(|v| matches!(v.pattern, EnumPattern::Wildcard));
let uses_all_bits = bit_size == enum_def.get_default_bit_size()?;
let from_unsigned_match = if has_wildcard || uses_all_bits {
quote! {
match value {
#(#from_unsigned_arms,)*
}
}
} else {
quote! {
match value {
#(#from_unsigned_arms,)*
_ => panic!("Unused bits in packed enum values must not be used! This shouldn't have happened, please file a bug report in embedded-interfaces."),
}
}
};
let packable_impl = quote! {
impl embedded_interfaces::packable::UnsignedPackable for #enum_name {
type Base = #underlying_type;
const BITS: usize = #bit_size;
fn from_unsigned(value: Self::Base) -> Self {
#from_unsigned_match
}
fn to_unsigned(&self) -> Self::Base {
match self {
#(#to_unsigned_arms,)*
}
}
}
};
Ok(quote! {
#enum_def_code
#packable_impl
})
}
fn generate_pattern(pattern: &EnumPattern) -> TokenStream2 {
match pattern {
EnumPattern::Single(val) => {
quote! { #val }
}
EnumPattern::Range(start, end) => {
quote! { #start..#end }
}
EnumPattern::RangeInclusive(start, end) => {
quote! { #start..=#end }
}
EnumPattern::Multiple(items) => {
let checks: Vec<_> = items.iter().map(generate_pattern).collect();
quote! { #(#checks)|* }
}
EnumPattern::Wildcard => {
quote! { _ }
}
}
}