use core::fmt;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Error {
pub member: &'static str,
pub fault: Fault,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Fault {
Length {
given: usize,
min: usize,
max: usize,
unit: &'static str,
},
Range {
given: u64,
max: u64,
},
Value {
given: u64,
expected: &'static str,
},
Character {
given: char,
expected: &'static str,
},
Digit {
given: u8,
},
EndMark {
given: u8,
},
Octet {
given: u8,
expected: u8,
},
Spare {
given: u8,
},
Form {
expected: &'static str,
},
}
impl Error {
pub(crate) const fn new(member: &'static str, fault: Fault) -> Self {
Self { member, fault }
}
pub(crate) const fn length(
member: &'static str,
unit: &'static str,
given: usize,
min: usize,
max: usize,
) -> Self {
let fault = Fault::Length {
given,
min,
max,
unit,
};
Self::new(member, fault)
}
#[cfg(feature = "alloc")]
pub(crate) const fn of(mut self, member: &'static str) -> Self {
self.member = member;
self
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let member = self.member;
match self.fault {
Fault::Length {
given,
min,
max,
unit,
} => {
let one = unit.strip_suffix('s').unwrap_or(unit);
let counted = if given == 1 { one } else { unit };
write!(f, "`{member}` has {given} {counted}, and must have ")?;
match (min, max) {
(min, usize::MAX) => write!(f, "{min} at least"),
(min, max) if min == max => write!(f, "{min}"),
(min, max) if min + 1 == max => write!(f, "{min} or {max}"),
(min, max) => write!(f, "{min} to {max}"),
}
}
Fault::Range { given, max } => {
write!(f, "`{member}` is {given}, and must be {max} at most")
}
Fault::Value { given, expected } => {
write!(f, "`{member}` is {given}, and must be {expected}")
}
Fault::Character { given, expected } => {
write!(
f,
"`{member}` has the character `{given}`, and must have {expected}"
)
}
Fault::Digit { given } => {
write!(
f,
"`{member}` has the half octet {given:#x} where a digit is, and a digit is 0 to 9"
)
}
Fault::EndMark { given } => {
write!(
f,
"`{member}` has the half octet {given:#x} where its end mark is, and the end mark is 0xf"
)
}
Fault::Octet { given, expected } => {
write!(
f,
"`{member}` has the octet {given:#04x}, and must have {expected:#04x} there"
)
}
Fault::Spare { given } => {
write!(
f,
"`{member}` has a spare bit set in the octet {given:#04x}, and a spare bit is 0"
)
}
Fault::Form { expected } => write!(f, "`{member}` is written as {expected}"),
}
}
}
impl core::error::Error for Error {}
#[derive(Clone, Copy)]
pub struct Faults {
faults: [Error; Self::MOST],
length: u8,
}
impl Faults {
const MOST: usize = 4;
pub(crate) const fn new() -> Self {
let unused = Error::new("", Fault::Form { expected: "" });
Self {
faults: [unused; Self::MOST],
length: 0,
}
}
pub(crate) fn push(&mut self, fault: Error) {
debug_assert!(usize::from(self.length) < Self::MOST);
if let Some(place) = self.faults.get_mut(usize::from(self.length)) {
*place = fault;
self.length += 1;
}
}
#[cfg(feature = "alloc")]
pub(crate) fn of(mut self, member: &'static str) -> Self {
for fault in &mut self.faults {
*fault = fault.of(member);
}
self
}
pub fn as_slice(&self) -> &[Error] {
&self.faults[..usize::from(self.length)]
}
}
impl core::ops::Deref for Faults {
type Target = [Error];
fn deref(&self) -> &[Error] {
self.as_slice()
}
}
impl fmt::Debug for Faults {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_slice().fmt(f)
}
}
impl PartialEq for Faults {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Eq for Faults {}
impl<const N: usize> PartialEq<[Error; N]> for Faults {
fn eq(&self, other: &[Error; N]) -> bool {
self.as_slice() == other
}
}