1#![forbid(unsafe_code)]
4
5use super::error::DomainError;
6use crate::validation::{MAX_CHAR_LIMIT, MAX_TIMEOUT_MS};
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use std::fmt;
9use std::num::NonZeroUsize;
10
11#[repr(transparent)]
13#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct TimeoutMs(u64);
15
16impl TimeoutMs {
17 pub fn try_new(ms: u64) -> Result<Self, DomainError> {
19 if ms == 0 || ms > MAX_TIMEOUT_MS {
20 return Err(DomainError::new(
21 "timeout_ms",
22 format!("timeout_ms must be 1..={MAX_TIMEOUT_MS}"),
23 ));
24 }
25 Ok(Self(ms))
26 }
27
28 #[must_use]
30 pub const fn get(self) -> u64 {
31 self.0
32 }
33
34 #[must_use]
36 pub const fn as_u64(self) -> u64 {
37 self.0
38 }
39}
40
41impl fmt::Display for TimeoutMs {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 write!(f, "{}", self.0)
44 }
45}
46
47impl Serialize for TimeoutMs {
48 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
49 s.serialize_u64(self.0)
50 }
51}
52
53impl<'de> Deserialize<'de> for TimeoutMs {
54 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
55 let v = u64::deserialize(d)?;
56 Self::try_new(v).map_err(serde::de::Error::custom)
57 }
58}
59
60#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
62pub enum CharLimit {
63 Unlimited,
65 Limited(NonZeroUsize),
67}
68
69impl CharLimit {
70 pub fn try_new(n: usize) -> Result<Self, DomainError> {
72 if n == 0 {
73 return Ok(Self::Unlimited);
74 }
75 if n > MAX_CHAR_LIMIT {
76 return Err(DomainError::new(
77 "char_limit",
78 format!("char limit must be 0..={MAX_CHAR_LIMIT}"),
79 ));
80 }
81 Ok(Self::Limited(NonZeroUsize::new(n).expect("n > 0")))
82 }
83
84 #[must_use]
86 pub fn effective(self) -> usize {
87 match self {
88 Self::Unlimited => usize::MAX,
89 Self::Limited(n) => n.get(),
90 }
91 }
92
93 #[must_use]
95 pub fn wire(self) -> usize {
96 match self {
97 Self::Unlimited => 0,
98 Self::Limited(n) => n.get(),
99 }
100 }
101}
102
103impl Serialize for CharLimit {
104 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
105 s.serialize_u64(self.wire() as u64)
106 }
107}
108
109impl<'de> Deserialize<'de> for CharLimit {
110 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
111 let v = u64::deserialize(d)?;
112 let n = usize::try_from(v).map_err(serde::de::Error::custom)?;
113 Self::try_new(n).map_err(serde::de::Error::custom)
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use crate::validation::{MAX_CHAR_LIMIT, MAX_TIMEOUT_MS};
121
122 #[test]
123 fn timeout_range() {
124 assert!(TimeoutMs::try_new(0).is_err());
125 assert!(TimeoutMs::try_new(MAX_TIMEOUT_MS + 1).is_err());
126 assert_eq!(TimeoutMs::try_new(1000).unwrap().get(), 1000);
127 }
128
129 #[test]
130 fn char_limit() {
131 assert_eq!(CharLimit::try_new(0).unwrap().effective(), usize::MAX);
132 assert_eq!(CharLimit::try_new(10).unwrap().wire(), 10);
133 assert!(CharLimit::try_new(MAX_CHAR_LIMIT + 1).is_err());
134 }
135}