use super::field_config::FieldConfig;
use crate::errors::CombineError;
use core::any::TypeId;
use proc_macro2::Span;
use std::collections::{hash_map::Entry, HashMap};
use syn::parse::Result;
#[derive(Default)]
pub struct Config {
pub bytes: Option<ConfigValue<usize>>,
pub bits: Option<ConfigValue<usize>>,
pub filled: Option<ConfigValue<bool>>,
pub repr: Option<ConfigValue<ReprKind>>,
pub derive_debug: Option<ConfigValue<()>>,
pub derive_specifier: Option<ConfigValue<()>>,
pub retained_attributes: Vec<syn::Attribute>,
pub field_configs: HashMap<usize, ConfigValue<FieldConfig>>,
}
#[derive(Copy, Clone)]
pub enum ReprKind {
U8,
U16,
U32,
U64,
U128,
}
impl ReprKind {
pub fn bits(self) -> usize {
match self {
Self::U8 => 8,
Self::U16 => 16,
Self::U32 => 32,
Self::U64 => 64,
Self::U128 => 128,
}
}
}
impl core::fmt::Debug for ReprKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "#[repr(u{})]", self.bits())
}
}
#[derive(Clone)]
pub struct ConfigValue<T> {
pub value: T,
pub span: Span,
}
impl<T> ConfigValue<T> {
pub fn new(value: T, span: Span) -> Self {
Self { value, span }
}
}
impl Config {
pub fn filled_enabled(&self) -> bool {
self.filled.as_ref().map_or(true, |config| config.value)
}
fn ensure_no_bits_and_repr_conflict(&self) -> Result<()> {
if let (Some(bits), Some(repr)) = (self.bits.as_ref(), self.repr.as_ref()) {
if bits.value != repr.value.bits() {
return Err(format_err!(
Span::call_site(),
"encountered conflicting `bits = {}` and {:?} parameters",
bits.value,
repr.value,
)
.into_combine(
format_err!(bits.span, "conflicting `bits = {}` here", bits.value,)
.into_combine(format_err!(repr.span, "conflicting {:?} here", repr.value)),
));
}
}
Ok(())
}
fn ensure_no_bits_and_bytes_conflict(&self) -> Result<()> {
if let (Some(bits), Some(bytes)) = (self.bits.as_ref(), self.bytes.as_ref()) {
fn next_div_by_8(value: usize) -> usize {
((value.saturating_sub(1) / 8) + 1) * 8
}
if next_div_by_8(bits.value) / 8 != bytes.value {
return Err(format_err!(
Span::call_site(),
"encountered conflicting `bits = {}` and `bytes = {}` parameters",
bits.value,
bytes.value,
)
.into_combine(format_err!(
bits.span,
"conflicting `bits = {}` here",
bits.value
))
.into_combine(format_err!(
bytes.span,
"conflicting `bytes = {}` here",
bytes.value,
)));
}
}
Ok(())
}
pub fn ensure_no_repr_and_filled_conflict(&self) -> Result<()> {
if let (Some(repr), Some(filled @ ConfigValue { value: false, .. })) =
(self.repr.as_ref(), self.filled.as_ref())
{
return Err(format_err!(
Span::call_site(),
"encountered conflicting `{:?}` and `filled = {}` parameters",
repr.value,
filled.value,
)
.into_combine(format_err!(
repr.span,
"conflicting `{:?}` here",
repr.value
))
.into_combine(format_err!(
filled.span,
"conflicting `filled = {}` here",
filled.value,
)));
}
Ok(())
}
pub fn ensure_no_conflicts(&self) -> Result<()> {
self.ensure_no_bits_and_repr_conflict()?;
self.ensure_no_bits_and_bytes_conflict()?;
self.ensure_no_repr_and_filled_conflict()?;
Ok(())
}
fn raise_duplicate_error<T>(name: &str, span: Span, previous: &ConfigValue<T>) -> syn::Error
where
T: core::fmt::Debug + 'static,
{
if TypeId::of::<T>() == TypeId::of::<()>() {
format_err!(span, "encountered duplicate `{}` parameter", name,)
} else {
format_err!(
span,
"encountered duplicate `{}` parameter: duplicate set to {:?}",
name,
previous.value
)
}
.into_combine(format_err!(
previous.span,
"previous `{}` parameter here",
name
))
}
pub fn bytes(&mut self, value: usize, span: Span) -> Result<()> {
match &self.bytes {
Some(previous) => return Err(Self::raise_duplicate_error("bytes", span, previous)),
None => self.bytes = Some(ConfigValue::new(value, span)),
}
Ok(())
}
pub fn bits(&mut self, value: usize, span: Span) -> Result<()> {
match &self.bits {
Some(previous) => return Err(Self::raise_duplicate_error("bits", span, previous)),
None => self.bits = Some(ConfigValue::new(value, span)),
}
Ok(())
}
pub fn filled(&mut self, value: bool, span: Span) -> Result<()> {
match &self.filled {
Some(previous) => return Err(Self::raise_duplicate_error("filled", span, previous)),
None => self.filled = Some(ConfigValue::new(value, span)),
}
Ok(())
}
pub fn repr(&mut self, value: ReprKind, span: Span) -> Result<()> {
match &self.repr {
Some(previous) => {
return Err(Self::raise_duplicate_error("#[repr(uN)]", span, previous))
}
None => self.repr = Some(ConfigValue::new(value, span)),
}
Ok(())
}
pub fn derive_debug(&mut self, span: Span) -> Result<()> {
match &self.derive_debug {
Some(previous) => {
return Err(Self::raise_duplicate_error(
"#[derive(Debug)]",
span,
previous,
))
}
None => self.derive_debug = Some(ConfigValue::new((), span)),
}
Ok(())
}
pub fn derive_specifier(&mut self, span: Span) -> Result<()> {
match &self.derive_specifier {
Some(previous) => {
return Err(Self::raise_duplicate_error(
"#[derive(Specifier)]",
span,
previous,
))
}
None => self.derive_specifier = Some(ConfigValue::new((), span)),
}
Ok(())
}
pub fn push_retained_attribute(&mut self, retained_attr: syn::Attribute) {
self.retained_attributes.push(retained_attr);
}
pub fn field_config(&mut self, index: usize, span: Span, config: FieldConfig) -> Result<()> {
match self.field_configs.entry(index) {
Entry::Occupied(occupied) => {
return Err(format_err!(span, "encountered duplicate config for field")
.into_combine(format_err!(occupied.get().span, "previous config here")))
}
Entry::Vacant(vacant) => {
vacant.insert(ConfigValue::new(config, span));
}
}
Ok(())
}
}