use core::cmp::{Ord, PartialOrd};
use core::fmt;
use displaydoc::Display;
use zerovec::ule::{AsULE, UleError, ULE};
#[derive(Display, Debug, PartialEq, Copy, Clone)]
#[non_exhaustive]
pub enum LengthError {
#[displaydoc("Invalid length")]
InvalidLength,
}
impl core::error::Error for LengthError {}
#[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_datetime::fields))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[allow(clippy::exhaustive_enums)] pub enum FieldLength {
One,
Two,
Three,
Four,
Five,
Six,
NumericOverride(FieldNumericOverrides),
}
const FIRST_NUMERIC_OVERRIDE: u8 = 17;
const LAST_NUMERIC_OVERRIDE: u8 = 31;
impl FieldLength {
#[inline]
pub(crate) fn idx(self) -> u8 {
match self {
FieldLength::One => 1,
FieldLength::Two => 2,
FieldLength::Three => 3,
FieldLength::Four => 4,
FieldLength::Five => 5,
FieldLength::Six => 6,
FieldLength::NumericOverride(o) => FIRST_NUMERIC_OVERRIDE
.saturating_add(o as u8)
.min(LAST_NUMERIC_OVERRIDE),
}
}
#[inline]
pub(crate) fn from_idx(idx: u8) -> Result<Self, LengthError> {
Ok(match idx {
1 => Self::One,
2 => Self::Two,
3 => Self::Three,
4 => Self::Four,
5 => Self::Five,
6 => Self::Six,
idx if (FIRST_NUMERIC_OVERRIDE..=LAST_NUMERIC_OVERRIDE).contains(&idx) => {
Self::NumericOverride((idx - FIRST_NUMERIC_OVERRIDE).try_into()?)
}
_ => return Err(LengthError::InvalidLength),
})
}
#[inline]
pub(crate) fn to_len(self) -> usize {
match self {
FieldLength::One => 1,
FieldLength::Two => 2,
FieldLength::Three => 3,
FieldLength::Four => 4,
FieldLength::Five => 5,
FieldLength::Six => 6,
FieldLength::NumericOverride(o) => FIRST_NUMERIC_OVERRIDE as usize + o as usize,
}
}
pub(crate) fn numeric_to_abbr(self) -> Self {
match self {
FieldLength::One | FieldLength::Two => FieldLength::Three,
other => other,
}
}
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct FieldLengthULE(u8);
impl AsULE for FieldLength {
type ULE = FieldLengthULE;
fn to_unaligned(self) -> Self::ULE {
FieldLengthULE(self.idx())
}
fn from_unaligned(unaligned: Self::ULE) -> Self {
#[expect(clippy::unwrap_used)] Self::from_idx(unaligned.0).unwrap()
}
}
impl FieldLengthULE {
#[inline]
pub(crate) fn validate_byte(byte: u8) -> Result<(), UleError> {
FieldLength::from_idx(byte)
.map(|_| ())
.map_err(|_| UleError::parse::<FieldLength>())
}
}
unsafe impl ULE for FieldLengthULE {
fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
for byte in bytes {
Self::validate_byte(*byte)?;
}
Ok(())
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_datetime::fields))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[non_exhaustive]
pub enum FieldNumericOverrides {
Hanidec = 0,
Hanidays = 1,
Hebr = 2,
Romanlow = 3,
Jpnyear = 4,
}
impl TryFrom<u8> for FieldNumericOverrides {
type Error = LengthError;
fn try_from(other: u8) -> Result<Self, LengthError> {
Ok(match other {
0 => Self::Hanidec,
1 => Self::Hanidays,
2 => Self::Hebr,
3 => Self::Romanlow,
4 => Self::Jpnyear,
_ => return Err(LengthError::InvalidLength),
})
}
}
impl FieldNumericOverrides {
pub fn as_str(self) -> &'static str {
match self {
Self::Hanidec => "hanidec",
Self::Hanidays => "hanidays",
Self::Hebr => "hebr",
Self::Romanlow => "romanlow",
Self::Jpnyear => "jpnyear",
}
}
}
impl fmt::Display for FieldNumericOverrides {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.as_str().fmt(f)
}
}