lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

value.rs (23637B)


      1 //! Validated service-neutral configuration leaf values.
      2 
      3 use core::fmt;
      4 use core::marker::PhantomData;
      5 use core::str::FromStr;
      6 use core::time::Duration;
      7 use std::error::Error;
      8 
      9 use serde::de::Error as _;
     10 use serde::{Deserialize, Deserializer, Serialize, Serializer};
     11 
     12 use crate::operations::{
     13     OperationsBindPolicy, OperationsConfigError, OperationsListenAddress, OperationsListenerConfig,
     14     OperationsTransportLimits,
     15 };
     16 
     17 const NANOSECONDS_PER_MICROSECOND: u64 = 1_000;
     18 const NANOSECONDS_PER_MILLISECOND: u64 = 1_000_000;
     19 const NANOSECONDS_PER_SECOND: u64 = 1_000_000_000;
     20 const NANOSECONDS_PER_MINUTE: u64 = 60 * NANOSECONDS_PER_SECOND;
     21 const NANOSECONDS_PER_HOUR: u64 = 60 * NANOSECONDS_PER_MINUTE;
     22 const NANOSECONDS_PER_DAY: u64 = 24 * NANOSECONDS_PER_HOUR;
     23 
     24 const BYTES_PER_KIBIBYTE: u64 = 1024;
     25 const BYTES_PER_MEBIBYTE: u64 = 1024 * BYTES_PER_KIBIBYTE;
     26 const BYTES_PER_GIBIBYTE: u64 = 1024 * BYTES_PER_MEBIBYTE;
     27 const BYTES_PER_TEBIBYTE: u64 = 1024 * BYTES_PER_GIBIBYTE;
     28 
     29 /// A positive duration represented by at most `u64::MAX` nanoseconds.
     30 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
     31 pub struct PositiveDuration(Duration);
     32 
     33 impl PositiveDuration {
     34     /// Validates a positive, canonically representable duration.
     35     pub fn new(value: Duration) -> Result<Self, PositiveDurationError> {
     36         if value.is_zero() {
     37             return Err(PositiveDurationError::Zero);
     38         }
     39         if value.as_nanos() > u128::from(u64::MAX) {
     40             return Err(PositiveDurationError::Overflow);
     41         }
     42         Ok(Self(value))
     43     }
     44 
     45     /// Returns the validated standard duration.
     46     #[must_use]
     47     pub const fn duration(self) -> Duration {
     48         self.0
     49     }
     50 
     51     /// Returns the exact total nanoseconds.
     52     #[must_use]
     53     pub fn nanoseconds(self) -> u64 {
     54         u64::try_from(self.0.as_nanos()).expect("validated positive duration fits u64 nanoseconds")
     55     }
     56 }
     57 
     58 impl FromStr for PositiveDuration {
     59     type Err = PositiveDurationError;
     60 
     61     fn from_str(value: &str) -> Result<Self, Self::Err> {
     62         let nanoseconds = parse_human_quantity(
     63             value,
     64             &[
     65                 ("ms", NANOSECONDS_PER_MILLISECOND),
     66                 ("us", NANOSECONDS_PER_MICROSECOND),
     67                 ("ns", 1),
     68                 ("d", NANOSECONDS_PER_DAY),
     69                 ("h", NANOSECONDS_PER_HOUR),
     70                 ("m", NANOSECONDS_PER_MINUTE),
     71                 ("s", NANOSECONDS_PER_SECOND),
     72             ],
     73         )
     74         .map_err(PositiveDurationError::from_quantity)?;
     75         Self::new(Duration::from_nanos(nanoseconds))
     76     }
     77 }
     78 
     79 impl fmt::Display for PositiveDuration {
     80     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
     81         format_human_quantity(
     82             formatter,
     83             self.nanoseconds(),
     84             &[
     85                 ("d", NANOSECONDS_PER_DAY),
     86                 ("h", NANOSECONDS_PER_HOUR),
     87                 ("m", NANOSECONDS_PER_MINUTE),
     88                 ("s", NANOSECONDS_PER_SECOND),
     89                 ("ms", NANOSECONDS_PER_MILLISECOND),
     90                 ("us", NANOSECONDS_PER_MICROSECOND),
     91                 ("ns", 1),
     92             ],
     93         )
     94     }
     95 }
     96 
     97 impl Serialize for PositiveDuration {
     98     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     99     where
    100         S: Serializer,
    101     {
    102         serializer.collect_str(self)
    103     }
    104 }
    105 
    106 impl<'de> Deserialize<'de> for PositiveDuration {
    107     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    108     where
    109         D: Deserializer<'de>,
    110     {
    111         deserialize_from_str(deserializer)
    112     }
    113 }
    114 
    115 /// Safe positive-duration parse failure.
    116 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    117 pub enum PositiveDurationError {
    118     Invalid,
    119     Zero,
    120     Overflow,
    121 }
    122 
    123 impl PositiveDurationError {
    124     const fn from_quantity(error: HumanQuantityError) -> Self {
    125         match error {
    126             HumanQuantityError::Invalid => Self::Invalid,
    127             HumanQuantityError::Zero => Self::Zero,
    128             HumanQuantityError::Overflow => Self::Overflow,
    129         }
    130     }
    131 }
    132 
    133 impl fmt::Display for PositiveDurationError {
    134     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    135         formatter.write_str("positive duration is invalid")
    136     }
    137 }
    138 
    139 impl Error for PositiveDurationError {}
    140 
    141 /// A positive exact byte quantity using canonical binary human units.
    142 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
    143 pub struct ByteLimit(u64);
    144 
    145 impl ByteLimit {
    146     /// Validates a positive byte quantity.
    147     pub const fn new(bytes: u64) -> Result<Self, ByteLimitError> {
    148         if bytes == 0 {
    149             Err(ByteLimitError::Zero)
    150         } else {
    151             Ok(Self(bytes))
    152         }
    153     }
    154 
    155     /// Returns the exact byte quantity.
    156     #[must_use]
    157     pub const fn bytes(self) -> u64 {
    158         self.0
    159     }
    160 }
    161 
    162 impl FromStr for ByteLimit {
    163     type Err = ByteLimitError;
    164 
    165     fn from_str(value: &str) -> Result<Self, Self::Err> {
    166         let bytes = parse_human_quantity(
    167             value,
    168             &[
    169                 ("KiB", BYTES_PER_KIBIBYTE),
    170                 ("MiB", BYTES_PER_MEBIBYTE),
    171                 ("GiB", BYTES_PER_GIBIBYTE),
    172                 ("TiB", BYTES_PER_TEBIBYTE),
    173                 ("B", 1),
    174             ],
    175         )
    176         .map_err(ByteLimitError::from_quantity)?;
    177         Self::new(bytes)
    178     }
    179 }
    180 
    181 impl fmt::Display for ByteLimit {
    182     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    183         format_human_quantity(
    184             formatter,
    185             self.bytes(),
    186             &[
    187                 ("TiB", BYTES_PER_TEBIBYTE),
    188                 ("GiB", BYTES_PER_GIBIBYTE),
    189                 ("MiB", BYTES_PER_MEBIBYTE),
    190                 ("KiB", BYTES_PER_KIBIBYTE),
    191                 ("B", 1),
    192             ],
    193         )
    194     }
    195 }
    196 
    197 impl Serialize for ByteLimit {
    198     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    199     where
    200         S: Serializer,
    201     {
    202         serializer.collect_str(self)
    203     }
    204 }
    205 
    206 impl<'de> Deserialize<'de> for ByteLimit {
    207     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    208     where
    209         D: Deserializer<'de>,
    210     {
    211         deserialize_from_str(deserializer)
    212     }
    213 }
    214 
    215 /// Safe byte-limit parse failure.
    216 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    217 pub enum ByteLimitError {
    218     Invalid,
    219     Zero,
    220     Overflow,
    221 }
    222 
    223 impl ByteLimitError {
    224     const fn from_quantity(error: HumanQuantityError) -> Self {
    225         match error {
    226             HumanQuantityError::Invalid => Self::Invalid,
    227             HumanQuantityError::Zero => Self::Zero,
    228             HumanQuantityError::Overflow => Self::Overflow,
    229         }
    230     }
    231 }
    232 
    233 impl fmt::Display for ByteLimitError {
    234     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    235         formatter.write_str("byte limit is invalid")
    236     }
    237 }
    238 
    239 impl Error for ByteLimitError {}
    240 
    241 /// A positive count bounded by its compile-time service-owned maximum.
    242 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
    243 pub struct BoundedCount<const MAXIMUM: u32>(u32);
    244 
    245 impl<const MAXIMUM: u32> BoundedCount<MAXIMUM> {
    246     /// Validates one positive count against the compile-time maximum.
    247     pub const fn new(value: u32) -> Result<Self, BoundedCountError> {
    248         if MAXIMUM == 0 {
    249             Err(BoundedCountError::InvalidMaximum)
    250         } else if value == 0 {
    251             Err(BoundedCountError::Zero)
    252         } else if value > MAXIMUM {
    253             Err(BoundedCountError::ExceedsMaximum)
    254         } else {
    255             Ok(Self(value))
    256         }
    257     }
    258 
    259     /// Returns the exact count.
    260     #[must_use]
    261     pub const fn value(self) -> u32 {
    262         self.0
    263     }
    264 
    265     /// Returns the compile-time maximum.
    266     #[must_use]
    267     pub const fn maximum() -> u32 {
    268         MAXIMUM
    269     }
    270 }
    271 
    272 impl<const MAXIMUM: u32> fmt::Display for BoundedCount<MAXIMUM> {
    273     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    274         self.0.fmt(formatter)
    275     }
    276 }
    277 
    278 impl<const MAXIMUM: u32> Serialize for BoundedCount<MAXIMUM> {
    279     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    280     where
    281         S: Serializer,
    282     {
    283         serializer.serialize_u32(self.0)
    284     }
    285 }
    286 
    287 impl<'de, const MAXIMUM: u32> Deserialize<'de> for BoundedCount<MAXIMUM> {
    288     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    289     where
    290         D: Deserializer<'de>,
    291     {
    292         let value = u64::deserialize(deserializer)?;
    293         let value = u32::try_from(value)
    294             .map_err(|_| D::Error::custom(BoundedCountError::ExceedsMaximum))?;
    295         Self::new(value).map_err(D::Error::custom)
    296     }
    297 }
    298 
    299 /// Safe bounded-count validation failure.
    300 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    301 pub enum BoundedCountError {
    302     InvalidMaximum,
    303     Zero,
    304     ExceedsMaximum,
    305 }
    306 
    307 impl fmt::Display for BoundedCountError {
    308     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    309         formatter.write_str("bounded count is outside its supported positive range")
    310     }
    311 }
    312 
    313 impl Error for BoundedCountError {}
    314 
    315 /// Closed v1 logging encoding for safe structured service logs.
    316 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
    317 pub enum LoggingFormat {
    318     #[default]
    319     Json,
    320 }
    321 
    322 impl FromStr for LoggingFormat {
    323     type Err = LoggingFormatError;
    324 
    325     fn from_str(value: &str) -> Result<Self, Self::Err> {
    326         match value {
    327             "json" => Ok(Self::Json),
    328             _ => Err(LoggingFormatError::Unsupported),
    329         }
    330     }
    331 }
    332 
    333 impl fmt::Display for LoggingFormat {
    334     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    335         formatter.write_str(match self {
    336             Self::Json => "json",
    337         })
    338     }
    339 }
    340 
    341 impl Serialize for LoggingFormat {
    342     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    343     where
    344         S: Serializer,
    345     {
    346         serializer.collect_str(self)
    347     }
    348 }
    349 
    350 impl<'de> Deserialize<'de> for LoggingFormat {
    351     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    352     where
    353         D: Deserializer<'de>,
    354     {
    355         deserialize_from_str(deserializer)
    356     }
    357 }
    358 
    359 /// Safe unsupported logging-format failure.
    360 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    361 pub enum LoggingFormatError {
    362     Unsupported,
    363 }
    364 
    365 impl fmt::Display for LoggingFormatError {
    366     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    367         formatter.write_str("logging format is unsupported")
    368     }
    369 }
    370 
    371 impl Error for LoggingFormatError {}
    372 
    373 /// Explicitly disabled or explicitly addressed operations binding.
    374 #[derive(Clone, Copy, PartialEq, Eq)]
    375 pub enum OptionalOperationsBind {
    376     Disabled,
    377     Listen(OperationsListenAddress),
    378 }
    379 
    380 impl OptionalOperationsBind {
    381     /// Returns a disabled operations binding.
    382     #[must_use]
    383     pub const fn disabled() -> Self {
    384         Self::Disabled
    385     }
    386 
    387     /// Returns an explicitly addressed operations binding.
    388     #[must_use]
    389     pub const fn listen(address: OperationsListenAddress) -> Self {
    390         Self::Listen(address)
    391     }
    392 
    393     /// Returns whether an operations binding is explicitly enabled.
    394     #[must_use]
    395     pub const fn is_enabled(self) -> bool {
    396         matches!(self, Self::Listen(_))
    397     }
    398 
    399     /// Returns the selected address when enabled.
    400     #[must_use]
    401     pub const fn address(self) -> Option<OperationsListenAddress> {
    402         match self {
    403             Self::Disabled => None,
    404             Self::Listen(address) => Some(address),
    405         }
    406     }
    407 
    408     /// Applies the existing network-scope and transport-limit authority.
    409     pub fn into_listener_config(
    410         self,
    411         bind_policy: OperationsBindPolicy,
    412         limits: OperationsTransportLimits,
    413     ) -> Result<OperationsListenerConfig, OperationsConfigError> {
    414         match self {
    415             Self::Disabled => Ok(OperationsListenerConfig::disabled()),
    416             Self::Listen(address) => {
    417                 OperationsListenerConfig::enabled(address, bind_policy, limits)
    418             }
    419         }
    420     }
    421 }
    422 
    423 impl Default for OptionalOperationsBind {
    424     fn default() -> Self {
    425         Self::disabled()
    426     }
    427 }
    428 
    429 impl FromStr for OptionalOperationsBind {
    430     type Err = OptionalOperationsBindError;
    431 
    432     fn from_str(value: &str) -> Result<Self, Self::Err> {
    433         if value == "disabled" {
    434             return Ok(Self::Disabled);
    435         }
    436         value
    437             .parse()
    438             .map(Self::Listen)
    439             .map_err(|_| OptionalOperationsBindError::Invalid)
    440     }
    441 }
    442 
    443 impl fmt::Debug for OptionalOperationsBind {
    444     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    445         formatter
    446             .debug_struct("OptionalOperationsBind")
    447             .field("enabled", &self.is_enabled())
    448             .field("listen", &self.address().map(|_| "[redacted]"))
    449             .finish()
    450     }
    451 }
    452 
    453 impl fmt::Display for OptionalOperationsBind {
    454     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    455         match self {
    456             Self::Disabled => formatter.write_str("disabled"),
    457             Self::Listen(address) => address.fmt(formatter),
    458         }
    459     }
    460 }
    461 
    462 impl Serialize for OptionalOperationsBind {
    463     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    464     where
    465         S: Serializer,
    466     {
    467         serializer.collect_str(self)
    468     }
    469 }
    470 
    471 impl<'de> Deserialize<'de> for OptionalOperationsBind {
    472     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    473     where
    474         D: Deserializer<'de>,
    475     {
    476         deserialize_from_str(deserializer)
    477     }
    478 }
    479 
    480 /// Safe optional operations-bind parse failure.
    481 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    482 pub enum OptionalOperationsBindError {
    483     Invalid,
    484 }
    485 
    486 impl fmt::Display for OptionalOperationsBindError {
    487     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    488         formatter.write_str("optional operations bind is invalid")
    489     }
    490 }
    491 
    492 impl Error for OptionalOperationsBindError {}
    493 
    494 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    495 enum HumanQuantityError {
    496     Invalid,
    497     Zero,
    498     Overflow,
    499 }
    500 
    501 fn deserialize_from_str<'de, D, T>(deserializer: D) -> Result<T, D::Error>
    502 where
    503     D: Deserializer<'de>,
    504     T: FromStr,
    505     T::Err: fmt::Display,
    506 {
    507     struct FromStrVisitor<T>(PhantomData<T>);
    508 
    509     impl<'de, T> serde::de::Visitor<'de> for FromStrVisitor<T>
    510     where
    511         T: FromStr,
    512         T::Err: fmt::Display,
    513     {
    514         type Value = T;
    515 
    516         fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    517             formatter.write_str("a canonical bounded configuration string")
    518         }
    519 
    520         fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
    521         where
    522             E: serde::de::Error,
    523         {
    524             value.parse().map_err(E::custom)
    525         }
    526 
    527         fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
    528         where
    529             E: serde::de::Error,
    530         {
    531             value.parse().map_err(E::custom)
    532         }
    533     }
    534 
    535     deserializer.deserialize_str(FromStrVisitor(PhantomData))
    536 }
    537 
    538 fn parse_human_quantity(value: &str, units: &[(&str, u64)]) -> Result<u64, HumanQuantityError> {
    539     let Some((number, multiplier)) = units.iter().find_map(|(unit, multiplier)| {
    540         value.strip_suffix(unit).map(|number| (number, *multiplier))
    541     }) else {
    542         return Err(HumanQuantityError::Invalid);
    543     };
    544     if number.is_empty()
    545         || !number.bytes().all(|byte| byte.is_ascii_digit())
    546         || (number.len() > 1 && number.starts_with('0'))
    547     {
    548         return Err(HumanQuantityError::Invalid);
    549     }
    550     let quantity = number
    551         .parse::<u64>()
    552         .map_err(|_| HumanQuantityError::Overflow)?;
    553     if quantity == 0 {
    554         return Err(HumanQuantityError::Zero);
    555     }
    556     quantity
    557         .checked_mul(multiplier)
    558         .ok_or(HumanQuantityError::Overflow)
    559 }
    560 
    561 fn format_human_quantity(
    562     formatter: &mut fmt::Formatter<'_>,
    563     value: u64,
    564     units: &[(&str, u64)],
    565 ) -> fmt::Result {
    566     let (unit, multiplier) = units
    567         .iter()
    568         .find(|(_, multiplier)| value.is_multiple_of(*multiplier))
    569         .expect("unit inventory ends in multiplier one");
    570     write!(formatter, "{}{unit}", value / multiplier)
    571 }
    572 
    573 #[cfg(test)]
    574 mod tests {
    575     use serde::{Deserialize, Serialize};
    576 
    577     use super::*;
    578 
    579     #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
    580     #[serde(deny_unknown_fields)]
    581     struct Values {
    582         duration: PositiveDuration,
    583         bytes: ByteLimit,
    584         count: BoundedCount<64>,
    585         logging: LoggingFormat,
    586         operations_bind: OptionalOperationsBind,
    587     }
    588 
    589     #[test]
    590     fn human_duration_units_parse_and_display_canonically() {
    591         for (source, nanoseconds, display) in [
    592             ("1ns", 1, "1ns"),
    593             ("1us", 1_000, "1us"),
    594             ("1ms", 1_000_000, "1ms"),
    595             ("1500ms", 1_500_000_000, "1500ms"),
    596             ("1s", 1_000_000_000, "1s"),
    597             ("60s", 60_000_000_000, "1m"),
    598             ("1m", 60_000_000_000, "1m"),
    599             ("1h", 3_600_000_000_000, "1h"),
    600             ("1d", 86_400_000_000_000, "1d"),
    601         ] {
    602             let value: PositiveDuration = source.parse().unwrap();
    603             assert_eq!(value.nanoseconds(), nanoseconds);
    604             assert_eq!(value.to_string(), display);
    605             assert_eq!(value.duration().as_nanos(), u128::from(nanoseconds));
    606         }
    607     }
    608 
    609     #[test]
    610     fn duration_zero_invalid_and_overflow_inputs_fail_closed() {
    611         for source in ["", "1", "s", "0s", "01s", "+1s", "-1s", "1.5s", "1S", " 1s"] {
    612             assert!(source.parse::<PositiveDuration>().is_err(), "{source}");
    613         }
    614         assert_eq!(
    615             PositiveDuration::new(Duration::ZERO),
    616             Err(PositiveDurationError::Zero)
    617         );
    618         assert_eq!(
    619             "18446744073709551616ns"
    620                 .parse::<PositiveDuration>()
    621                 .unwrap_err(),
    622             PositiveDurationError::Overflow
    623         );
    624         assert_eq!(
    625             "18446744073709551615d"
    626                 .parse::<PositiveDuration>()
    627                 .unwrap_err(),
    628             PositiveDurationError::Overflow
    629         );
    630         assert_eq!(
    631             PositiveDuration::new(Duration::MAX).unwrap_err(),
    632             PositiveDurationError::Overflow
    633         );
    634         let maximum = PositiveDuration::new(Duration::from_nanos(u64::MAX)).unwrap();
    635         assert_eq!(maximum.nanoseconds(), u64::MAX);
    636         assert_eq!(maximum.to_string(), format!("{}ns", u64::MAX));
    637     }
    638 
    639     #[test]
    640     fn binary_byte_units_parse_and_display_canonically() {
    641         for (source, bytes, display) in [
    642             ("1B", 1, "1B"),
    643             ("1KiB", 1024, "1KiB"),
    644             ("1536B", 1536, "1536B"),
    645             ("1MiB", 1_048_576, "1MiB"),
    646             ("1GiB", 1_073_741_824, "1GiB"),
    647             ("1TiB", 1_099_511_627_776, "1TiB"),
    648         ] {
    649             let value: ByteLimit = source.parse().unwrap();
    650             assert_eq!(value.bytes(), bytes);
    651             assert_eq!(value.to_string(), display);
    652         }
    653     }
    654 
    655     #[test]
    656     fn byte_zero_invalid_and_overflow_inputs_fail_closed() {
    657         for source in [
    658             "", "1", "B", "0B", "01B", "+1B", "-1B", "1.5KiB", "1KB", " 1B",
    659         ] {
    660             assert!(source.parse::<ByteLimit>().is_err(), "{source}");
    661         }
    662         assert_eq!(ByteLimit::new(0), Err(ByteLimitError::Zero));
    663         assert_eq!(
    664             "18446744073709551616B".parse::<ByteLimit>().unwrap_err(),
    665             ByteLimitError::Overflow
    666         );
    667         assert_eq!(
    668             "18446744073709551615KiB".parse::<ByteLimit>().unwrap_err(),
    669             ByteLimitError::Overflow
    670         );
    671         let maximum = ByteLimit::new(u64::MAX).unwrap();
    672         assert_eq!(maximum.bytes(), u64::MAX);
    673         assert_eq!(maximum.to_string(), format!("{}B", u64::MAX));
    674     }
    675 
    676     #[test]
    677     fn bounded_count_rejects_zero_invalid_maximum_overflow_and_just_over() {
    678         assert_eq!(BoundedCount::<64>::new(1).unwrap().value(), 1);
    679         assert_eq!(BoundedCount::<64>::new(64).unwrap().value(), 64);
    680         assert_eq!(BoundedCount::<64>::maximum(), 64);
    681         assert_eq!(
    682             BoundedCount::<64>::new(0).unwrap_err(),
    683             BoundedCountError::Zero
    684         );
    685         assert_eq!(
    686             BoundedCount::<64>::new(65).unwrap_err(),
    687             BoundedCountError::ExceedsMaximum
    688         );
    689         assert_eq!(
    690             BoundedCount::<0>::new(1).unwrap_err(),
    691             BoundedCountError::InvalidMaximum
    692         );
    693         assert!(toml::from_str::<Values>(
    694             "duration='1s'\nbytes='1KiB'\ncount=4294967296\nlogging='json'\noperations_bind='disabled'",
    695         )
    696         .is_err());
    697     }
    698 
    699     #[test]
    700     fn serde_and_display_are_exact_for_every_common_leaf() {
    701         let source = concat!(
    702             "duration = \"2m\"\n",
    703             "bytes = \"8MiB\"\n",
    704             "count = 32\n",
    705             "logging = \"json\"\n",
    706             "operations_bind = \"127.0.0.1:9100\"\n",
    707         );
    708         let values: Values = toml::from_str(source).unwrap();
    709         assert_eq!(values.duration.to_string(), "2m");
    710         assert_eq!(values.bytes.to_string(), "8MiB");
    711         assert_eq!(values.count.to_string(), "32");
    712         assert_eq!(values.logging.to_string(), "json");
    713         assert_eq!(values.operations_bind.to_string(), "127.0.0.1:9100");
    714         assert_eq!(toml::to_string(&values).unwrap(), source);
    715     }
    716 
    717     #[test]
    718     fn logging_format_is_closed_to_structured_json() {
    719         assert_eq!(LoggingFormat::default(), LoggingFormat::Json);
    720         assert_eq!("json".parse(), Ok(LoggingFormat::Json));
    721         for source in ["", "JSON", "text", "pretty", "compact"] {
    722             assert_eq!(
    723                 source.parse::<LoggingFormat>(),
    724                 Err(LoggingFormatError::Unsupported)
    725             );
    726         }
    727     }
    728 
    729     #[test]
    730     fn optional_operations_bind_is_explicit_redacted_and_policy_checked() {
    731         assert_eq!(
    732             OptionalOperationsBind::disabled(),
    733             OptionalOperationsBind::default()
    734         );
    735         let disabled: OptionalOperationsBind = "disabled".parse().unwrap();
    736         assert!(!disabled.is_enabled());
    737         assert_eq!(disabled.address(), None);
    738         assert_eq!(disabled.to_string(), "disabled");
    739         assert_eq!(
    740             disabled
    741                 .into_listener_config(
    742                     OperationsBindPolicy::LoopbackOnly,
    743                     OperationsTransportLimits::DEFAULT,
    744                 )
    745                 .unwrap(),
    746             OperationsListenerConfig::disabled()
    747         );
    748 
    749         let loopback: OptionalOperationsBind = "127.0.0.1:9100".parse().unwrap();
    750         assert_eq!(
    751             OptionalOperationsBind::listen(loopback.address().unwrap()),
    752             loopback
    753         );
    754         assert!(loopback.is_enabled());
    755         assert_eq!(loopback.to_string(), "127.0.0.1:9100");
    756         assert!(!format!("{loopback:?}").contains("127.0.0.1"));
    757         assert!(
    758             loopback
    759                 .into_listener_config(
    760                     OperationsBindPolicy::LoopbackOnly,
    761                     OperationsTransportLimits::DEFAULT,
    762                 )
    763                 .is_ok()
    764         );
    765 
    766         let public: OptionalOperationsBind = "0.0.0.0:9100".parse().unwrap();
    767         assert!(
    768             public
    769                 .into_listener_config(
    770                     OperationsBindPolicy::LoopbackOnly,
    771                     OperationsTransportLimits::DEFAULT,
    772                 )
    773                 .is_err()
    774         );
    775         assert!(
    776             public
    777                 .into_listener_config(
    778                     OperationsBindPolicy::Public,
    779                     OperationsTransportLimits::DEFAULT,
    780                 )
    781                 .is_ok()
    782         );
    783 
    784         for source in ["", "Disabled", "127.0.0.1:0", "localhost:9100"] {
    785             assert_eq!(
    786                 source.parse::<OptionalOperationsBind>(),
    787                 Err(OptionalOperationsBindError::Invalid)
    788             );
    789         }
    790     }
    791 }