use core::num::NonZeroU8;
#[cfg(feature = "formatting")]
use super::Iso8601;
use super::{Config, DateKind, FormattedComponents as FC, OffsetPrecision, TimePrecision};
#[doc(hidden)]
pub type DoNotRelyOnWhatThisIs = u128;
pub type EncodedConfig = DoNotRelyOnWhatThisIs;
#[cfg(feature = "formatting")]
impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
const CONFIG: Config = Config::decode(CONFIG);
pub(crate) const FORMAT_DATE: bool = matches!(
Self::CONFIG.formatted_components,
FC::Date | FC::DateTime | FC::DateTimeOffset
);
pub(crate) const FORMAT_TIME: bool = matches!(
Self::CONFIG.formatted_components,
FC::Time | FC::DateTime | FC::DateTimeOffset | FC::TimeOffset
);
pub(crate) const FORMAT_OFFSET: bool = matches!(
Self::CONFIG.formatted_components,
FC::Offset | FC::DateTimeOffset | FC::TimeOffset
);
pub(crate) const YEAR_IS_SIX_DIGITS: bool = Self::CONFIG.year_is_six_digits;
pub(crate) const USE_SEPARATORS: bool = Self::CONFIG.use_separators;
pub(crate) const DATE_KIND: DateKind = Self::CONFIG.date_kind;
pub(crate) const TIME_PRECISION: TimePrecision = Self::CONFIG.time_precision;
pub(crate) const OFFSET_PRECISION: OffsetPrecision = Self::CONFIG.offset_precision;
}
impl Config {
pub const fn encode(&self) -> EncodedConfig {
let mut bytes = [0; EncodedConfig::BITS as usize / 8];
bytes[0] = match self.formatted_components {
FC::None => 0,
FC::Date => 1,
FC::Time => 2,
FC::Offset => 3,
FC::DateTime => 4,
FC::DateTimeOffset => 5,
FC::TimeOffset => 6,
};
bytes[1] = self.use_separators as _;
bytes[2] = self.year_is_six_digits as _;
bytes[3] = match self.date_kind {
DateKind::Calendar => 0,
DateKind::Week => 1,
DateKind::Ordinal => 2,
};
bytes[4] = match self.time_precision {
TimePrecision::Hour { .. } => 0,
TimePrecision::Minute { .. } => 1,
TimePrecision::Second { .. } => 2,
};
bytes[5] = match self.time_precision {
TimePrecision::Hour { decimal_digits }
| TimePrecision::Minute { decimal_digits }
| TimePrecision::Second { decimal_digits } => match decimal_digits {
None => 0,
Some(decimal_digits) => decimal_digits.get(),
},
};
bytes[6] = match self.offset_precision {
OffsetPrecision::Hour => 0,
OffsetPrecision::Minute => 1,
};
EncodedConfig::from_be_bytes(bytes)
}
pub(super) const fn decode(encoded: EncodedConfig) -> Self {
let bytes = encoded.to_be_bytes();
let formatted_components = match bytes[0] {
0 => FC::None,
1 => FC::Date,
2 => FC::Time,
3 => FC::Offset,
4 => FC::DateTime,
5 => FC::DateTimeOffset,
6 => FC::TimeOffset,
_ => panic!("invalid configuration"),
};
let use_separators = match bytes[1] {
0 => false,
1 => true,
_ => panic!("invalid configuration"),
};
let year_is_six_digits = match bytes[2] {
0 => false,
1 => true,
_ => panic!("invalid configuration"),
};
let date_kind = match bytes[3] {
0 => DateKind::Calendar,
1 => DateKind::Week,
2 => DateKind::Ordinal,
_ => panic!("invalid configuration"),
};
let time_precision = match bytes[4] {
0 => TimePrecision::Hour {
decimal_digits: NonZeroU8::new(bytes[5]),
},
1 => TimePrecision::Minute {
decimal_digits: NonZeroU8::new(bytes[5]),
},
2 => TimePrecision::Second {
decimal_digits: NonZeroU8::new(bytes[5]),
},
_ => panic!("invalid configuration"),
};
let offset_precision = match bytes[6] {
0 => OffsetPrecision::Hour,
1 => OffsetPrecision::Minute,
_ => panic!("invalid configuration"),
};
let mut idx = 7; while idx < EncodedConfig::BITS as usize / 8 {
assert!(bytes[idx] == 0, "invalid configuration");
idx += 1;
}
Self {
formatted_components,
use_separators,
year_is_six_digits,
date_kind,
time_precision,
offset_precision,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! eq {
($a:expr, $b:expr) => {{
let a = $a;
let b = $b;
a.formatted_components == b.formatted_components
&& a.use_separators == b.use_separators
&& a.year_is_six_digits == b.year_is_six_digits
&& a.date_kind == b.date_kind
&& a.time_precision == b.time_precision
&& a.offset_precision == b.offset_precision
}};
}
#[test]
fn encoding_roundtrip() {
macro_rules! assert_roundtrip {
($config:expr) => {
let config = $config;
let encoded = config.encode();
let decoded = Config::decode(encoded);
assert!(eq!(config, decoded));
};
}
assert_roundtrip!(Config::DEFAULT);
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::None));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Date));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Time));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Offset));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTime));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTimeOffset));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::TimeOffset));
assert_roundtrip!(Config::DEFAULT.set_use_separators(false));
assert_roundtrip!(Config::DEFAULT.set_use_separators(true));
assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(false));
assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(true));
assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Calendar));
assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Week));
assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Ordinal));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour {
decimal_digits: None,
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute {
decimal_digits: None,
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second {
decimal_digits: None,
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour {
decimal_digits: NonZeroU8::new(1),
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute {
decimal_digits: NonZeroU8::new(1),
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second {
decimal_digits: NonZeroU8::new(1),
}));
assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Hour));
assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Minute));
}
macro_rules! assert_decode_fail {
($encoding:expr) => {
assert!(
std::panic::catch_unwind(|| {
Config::decode($encoding);
})
.is_err()
);
};
}
#[test]
fn decode_fail() {
assert_decode_fail!(0x07_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_02_00_00_00_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_02_00_00_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_00_03_00_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_00_00_03_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_00_00_00_00_02_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_00_00_00_00_00_01_00_00_00_00_00_00_00_00);
}
}