use std::collections::HashSet;
use std::str::FromStr;
use getset::CopyGetters;
use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter, EnumString};
use syn::parse::{Parse, ParseStream};
use crate::parsing::common::attribute_argument_parser::{
parse_attribute_arguments, parse_boolean_attribute_argument,
};
use crate::parsing::common::compiler_error::create_user_parsing_compiler_error;
use crate::parsing::common::conversion_endian::{ConversionEndian, resolve_endian_feature};
const ORDER_LSB_FEATURE_ENABLED: bool = cfg!(feature = "order_lsb");
const ORDER_MSB_FEATURE_ENABLED: bool = cfg!(feature = "order_msb");
const FROM_ENDIAN_LITTLE_FEATURE_ENABLED: bool = cfg!(feature = "from_endian_little");
const FROM_ENDIAN_BIG_FEATURE_ENABLED: bool = cfg!(feature = "from_endian_big");
const INTO_ENDIAN_LITTLE_FEATURE_ENABLED: bool = cfg!(feature = "into_endian_little");
const INTO_ENDIAN_BIG_FEATURE_ENABLED: bool = cfg!(feature = "into_endian_big");
const WRITE_ENDIAN_LITTLE_FEATURE_ENABLED: bool = cfg!(feature = "write_endian_little");
const WRITE_ENDIAN_BIG_FEATURE_ENABLED: bool = cfg!(feature = "write_endian_big");
const GENERATE_NEW_FEATURE_ENABLED: bool = cfg!(feature = "generate_new");
const DISABLE_NEW_FEATURE_ENABLED: bool = cfg!(feature = "disable_new");
const GENERATE_FROM_INTO_BITS_FEATURE_ENABLED: bool = cfg!(feature = "generate_from_into_bits");
const DISABLE_FROM_INTO_BITS_FEATURE_ENABLED: bool = cfg!(feature = "disable_from_into_bits");
const GENERATE_FROM_TRAITS_FEATURE_ENABLED: bool = cfg!(feature = "generate_from_traits");
const DISABLE_FROM_TRAITS_FEATURE_ENABLED: bool = cfg!(feature = "disable_from_traits");
const GENERATE_DEFAULT_FEATURE_ENABLED: bool = cfg!(feature = "generate_default");
const DISABLE_DEFAULT_FEATURE_ENABLED: bool = cfg!(feature = "disable_default");
const GENERATE_DEBUG_FEATURE_ENABLED: bool = cfg!(feature = "generate_debug");
const DISABLE_DEBUG_FEATURE_ENABLED: bool = cfg!(feature = "disable_debug");
const DERIVE_COPY_FEATURE_ENABLED: bool = cfg!(feature = "derive_copy");
const DISABLE_COPY_FEATURE_ENABLED: bool = cfg!(feature = "disable_copy");
const GENERATE_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "generate_bit_ops");
const DISABLE_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "disable_bit_ops");
const GENERATE_WRITE_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "generate_write_bit_ops");
const DISABLE_WRITE_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "disable_write_bit_ops");
const GENERATE_CLEAR_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "generate_clear_bit_ops");
const DISABLE_CLEAR_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "disable_clear_bit_ops");
const GENERATE_SET_GET_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "generate_set_get_bit_ops");
const DISABLE_SET_GET_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "disable_set_get_bit_ops");
const GENERATE_INVERT_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "generate_invert_bit_ops");
const DISABLE_INVERT_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "disable_invert_bit_ops");
const GENERATE_TOGGLE_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "generate_toggle_bit_ops");
const DISABLE_TOGGLE_BIT_OPS_FEATURE_ENABLED: bool = cfg!(feature = "disable_toggle_bit_ops");
const GENERATE_BUILDER_FEATURE_ENABLED: bool = cfg!(feature = "generate_builder");
const DISABLE_BUILDER_FEATURE_ENABLED: bool = cfg!(feature = "disable_builder");
const ENABLE_ARRAY_HEAP_FEATURE_ENABLED: bool = cfg!(feature = "enable_array_heap");
const DISABLE_ARRAY_HEAP_FEATURE_ENABLED: bool = cfg!(feature = "disable_array_heap");
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum BitOrder {
Lsb,
Msb,
}
impl FromStr for BitOrder {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"lsb" => Ok(Self::Lsb),
"msb" => Ok(Self::Msb),
_ => Err(format!("Invalid order argument '{s}'. Valid values are 'lsb' or 'msb'.")),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, CopyGetters)]
#[getset(get_copy = "pub")]
pub struct BitfieldArguments {
order: BitOrder,
generate_new: bool,
generate_from_into_bits: bool,
from_endian: ConversionEndian,
into_endian: ConversionEndian,
generate_from_traits: bool,
generate_default: bool,
generate_debug: bool,
derive_copy: bool,
generate_bit_ops: bool,
generate_write_bit_ops: bool,
user_set_generate_write_bit_ops: bool,
write_endian: ConversionEndian,
generate_clear_bit_ops: bool,
user_set_clear_bit_ops: bool,
generate_set_get_bit_ops: bool,
user_set_set_get_bit_ops: bool,
generate_invert_bit_ops: bool,
user_set_invert_bit_ops: bool,
generate_toggle_bit_ops: bool,
user_set_toggle_bit_ops: bool,
generate_builder: bool,
array_heap: bool,
force_panic: bool,
}
impl Default for BitfieldArguments {
fn default() -> Self {
Self {
order: resolve_bit_order_feature(ORDER_LSB_FEATURE_ENABLED, ORDER_MSB_FEATURE_ENABLED),
from_endian: resolve_endian_feature(
FROM_ENDIAN_LITTLE_FEATURE_ENABLED,
FROM_ENDIAN_BIG_FEATURE_ENABLED,
ConversionEndian::Big,
),
into_endian: resolve_endian_feature(
INTO_ENDIAN_LITTLE_FEATURE_ENABLED,
INTO_ENDIAN_BIG_FEATURE_ENABLED,
ConversionEndian::Big,
),
write_endian: resolve_endian_feature(
WRITE_ENDIAN_LITTLE_FEATURE_ENABLED,
WRITE_ENDIAN_BIG_FEATURE_ENABLED,
ConversionEndian::Big,
),
generate_new: resolve_bool_feature(
GENERATE_NEW_FEATURE_ENABLED,
DISABLE_NEW_FEATURE_ENABLED,
),
generate_from_into_bits: resolve_bool_feature(
GENERATE_FROM_INTO_BITS_FEATURE_ENABLED,
DISABLE_FROM_INTO_BITS_FEATURE_ENABLED,
),
generate_from_traits: resolve_bool_feature(
GENERATE_FROM_TRAITS_FEATURE_ENABLED,
DISABLE_FROM_TRAITS_FEATURE_ENABLED,
),
generate_default: resolve_bool_feature(
GENERATE_DEFAULT_FEATURE_ENABLED,
DISABLE_DEFAULT_FEATURE_ENABLED,
),
generate_debug: resolve_bool_feature(
GENERATE_DEBUG_FEATURE_ENABLED,
DISABLE_DEBUG_FEATURE_ENABLED,
),
derive_copy: resolve_bool_feature(
DERIVE_COPY_FEATURE_ENABLED,
DISABLE_COPY_FEATURE_ENABLED,
),
generate_bit_ops: resolve_bool_feature(
GENERATE_BIT_OPS_FEATURE_ENABLED,
DISABLE_BIT_OPS_FEATURE_ENABLED,
),
generate_write_bit_ops: resolve_bool_feature(
GENERATE_WRITE_BIT_OPS_FEATURE_ENABLED,
DISABLE_WRITE_BIT_OPS_FEATURE_ENABLED,
),
generate_clear_bit_ops: resolve_bool_feature(
GENERATE_CLEAR_BIT_OPS_FEATURE_ENABLED,
DISABLE_CLEAR_BIT_OPS_FEATURE_ENABLED,
),
user_set_clear_bit_ops: false,
generate_set_get_bit_ops: resolve_bool_feature(
GENERATE_SET_GET_BIT_OPS_FEATURE_ENABLED,
DISABLE_SET_GET_BIT_OPS_FEATURE_ENABLED,
),
user_set_set_get_bit_ops: false,
generate_invert_bit_ops: resolve_bool_feature(
GENERATE_INVERT_BIT_OPS_FEATURE_ENABLED,
DISABLE_INVERT_BIT_OPS_FEATURE_ENABLED,
),
user_set_invert_bit_ops: false,
generate_toggle_bit_ops: resolve_bool_feature(
GENERATE_TOGGLE_BIT_OPS_FEATURE_ENABLED,
DISABLE_TOGGLE_BIT_OPS_FEATURE_ENABLED,
),
user_set_toggle_bit_ops: false,
generate_builder: resolve_bool_feature(
GENERATE_BUILDER_FEATURE_ENABLED,
DISABLE_BUILDER_FEATURE_ENABLED,
),
array_heap: ENABLE_ARRAY_HEAP_FEATURE_ENABLED && !DISABLE_ARRAY_HEAP_FEATURE_ENABLED,
user_set_generate_write_bit_ops: false,
force_panic: false,
}
}
}
const fn resolve_bool_feature(enabled: bool, disabled: bool) -> bool {
if enabled { true } else { !disabled }
}
const fn resolve_bit_order_feature(lsb: bool, msb: bool) -> BitOrder {
if lsb {
BitOrder::Lsb
} else if msb {
BitOrder::Msb
} else {
BitOrder::Lsb
}
}
#[derive(Display, EnumString, EnumIter, PartialEq, Eq)]
enum BitfieldArgumentKey {
#[strum(serialize = "order")]
Order,
#[strum(serialize = "from_endian")]
FromEndian,
#[strum(serialize = "into_endian")]
IntoEndian,
#[strum(serialize = "write_endian")]
WriteEndian,
#[strum(serialize = "new")]
New,
#[strum(serialize = "from_into_bits")]
FromIntoBits,
#[strum(serialize = "from_traits")]
FromIntoTraits,
#[strum(serialize = "default")]
Default,
#[strum(serialize = "debug")]
Debug,
#[strum(serialize = "copy")]
Copy,
#[strum(serialize = "bit_ops")]
BitOps,
#[strum(serialize = "write_bit_ops")]
WriteBitOps,
#[strum(serialize = "clear_bit_ops")]
ClearBitOps,
#[strum(serialize = "set_get_bit_ops")]
SetGetBitOps,
#[strum(serialize = "invert_bit_ops")]
InvertBitOps,
#[strum(serialize = "toggle_bit_ops")]
ToggleBitOps,
#[strum(serialize = "builder")]
Builder,
#[strum(serialize = "array_heap")]
ArrayHeap,
#[strum(serialize = "force_panic")]
ForcePanic,
}
impl Parse for BitfieldArguments {
fn parse(input: ParseStream) -> syn::Result<Self> {
let valid_keys = BitfieldArgumentKey::iter()
.filter(|argument_key| *argument_key != BitfieldArgumentKey::ForcePanic)
.map(|k| k.to_string())
.collect();
let internal_keys = HashSet::from([BitfieldArgumentKey::ForcePanic.to_string()]);
let attribute_arguments = parse_attribute_arguments(input, valid_keys, internal_keys)?;
let mut bitfield_arguments = Self::default();
for argument in attribute_arguments {
match BitfieldArgumentKey::from_str(argument.key().token().as_str())
.expect("This should be caught by the known keys check")
{
BitfieldArgumentKey::Order => {
bitfield_arguments.order =
BitOrder::from_str(argument.value().token().as_str()).map_err(|err| {
create_user_parsing_compiler_error(argument.value().span(), err)
})?;
},
BitfieldArgumentKey::FromEndian => {
bitfield_arguments.from_endian =
ConversionEndian::from_str(argument.value().token().as_str()).map_err(
|err| create_user_parsing_compiler_error(argument.value().span(), err),
)?;
},
BitfieldArgumentKey::IntoEndian => {
bitfield_arguments.into_endian =
ConversionEndian::from_str(argument.value().token().as_str()).map_err(
|err| create_user_parsing_compiler_error(argument.value().span(), err),
)?;
},
BitfieldArgumentKey::WriteEndian => {
bitfield_arguments.write_endian =
ConversionEndian::from_str(argument.value().token().as_str()).map_err(
|err| create_user_parsing_compiler_error(argument.value().span(), err),
)?;
},
BitfieldArgumentKey::New => {
bitfield_arguments.generate_new = parse_boolean_attribute_argument(argument)?;
},
BitfieldArgumentKey::FromIntoBits => {
bitfield_arguments.generate_from_into_bits =
parse_boolean_attribute_argument(argument)?;
},
BitfieldArgumentKey::FromIntoTraits => {
bitfield_arguments.generate_from_traits =
parse_boolean_attribute_argument(argument)?;
},
BitfieldArgumentKey::Default => {
bitfield_arguments.generate_default =
parse_boolean_attribute_argument(argument)?;
},
BitfieldArgumentKey::Debug => {
bitfield_arguments.generate_debug = parse_boolean_attribute_argument(argument)?;
},
BitfieldArgumentKey::BitOps => {
bitfield_arguments.generate_bit_ops =
parse_boolean_attribute_argument(argument)?;
},
BitfieldArgumentKey::WriteBitOps => {
bitfield_arguments.generate_write_bit_ops =
parse_boolean_attribute_argument(argument)?;
bitfield_arguments.user_set_generate_write_bit_ops = true;
},
BitfieldArgumentKey::ClearBitOps => {
bitfield_arguments.generate_clear_bit_ops =
parse_boolean_attribute_argument(argument)?;
bitfield_arguments.user_set_clear_bit_ops = true;
},
BitfieldArgumentKey::SetGetBitOps => {
bitfield_arguments.generate_set_get_bit_ops =
parse_boolean_attribute_argument(argument)?;
bitfield_arguments.user_set_set_get_bit_ops = true;
},
BitfieldArgumentKey::InvertBitOps => {
bitfield_arguments.generate_invert_bit_ops =
parse_boolean_attribute_argument(argument)?;
bitfield_arguments.user_set_invert_bit_ops = true;
},
BitfieldArgumentKey::ToggleBitOps => {
bitfield_arguments.generate_toggle_bit_ops =
parse_boolean_attribute_argument(argument)?;
bitfield_arguments.user_set_toggle_bit_ops = true;
},
BitfieldArgumentKey::Builder => {
bitfield_arguments.generate_builder =
parse_boolean_attribute_argument(argument)?;
},
BitfieldArgumentKey::ArrayHeap => {
bitfield_arguments.array_heap = parse_boolean_attribute_argument(argument)?;
},
BitfieldArgumentKey::Copy => {
bitfield_arguments.derive_copy = parse_boolean_attribute_argument(argument)?;
},
BitfieldArgumentKey::ForcePanic => {
bitfield_arguments.force_panic = parse_boolean_attribute_argument(argument)?;
},
}
}
Ok(bitfield_arguments)
}
}