1use std::fmt::Formatter;
6
7use serde::{Deserialize, Serialize, de::Error};
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize)]
13pub struct StickyDurationMs(u32);
14
15#[derive(Clone, Debug, Serialize, Deserialize)]
17#[non_exhaustive]
18pub struct StickyObject {
19 pub duration_ms: StickyDurationMs,
21}
22
23impl StickyDurationMs {
24 pub const MAX: u32 = 3_600_000;
26
27 pub fn new_clamped<T: Into<u64>>(v: T) -> Self {
29 let v = v.into();
30 let clamped = v.min(Self::MAX as u64) as u32;
31 Self(clamped)
32 }
33
34 pub fn get(self) -> u32 {
36 self.into()
37 }
38}
39
40impl From<StickyDurationMs> for u32 {
41 fn from(d: StickyDurationMs) -> Self {
42 d.0
43 }
44}
45
46impl<'de> Deserialize<'de> for StickyDurationMs {
47 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
48 where
49 D: serde::Deserializer<'de>,
50 {
51 struct StickyDurationMsVisitor;
52
53 impl<'de> serde::de::Visitor<'de> for StickyDurationMsVisitor {
54 type Value = StickyDurationMs;
55
56 fn expecting(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
57 write!(f, "an integer in the range 0-3600000")
58 }
59
60 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
61 where
62 E: Error,
63 {
64 if v < 0 {
65 Err(E::invalid_value(serde::de::Unexpected::Signed(v), &self))
66 } else {
67 self.visit_u64(v as u64)
68 }
69 }
70
71 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
73 where
74 E: Error,
75 {
76 if v > StickyDurationMs::MAX as u64 {
77 Err(E::invalid_value(serde::de::Unexpected::Unsigned(v), &self))
78 } else {
79 Ok(StickyDurationMs(v as u32))
80 }
81 }
82 }
83
84 deserializer.deserialize_any(StickyDurationMsVisitor)
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use crate::sticky::{StickyDurationMs, StickyObject};
91
92 #[test]
93 fn deserialize_sticky_ok() {
94 let raw = "78000";
95 let sticky_duration: StickyDurationMs = serde_json::from_str(raw).unwrap();
96 assert_eq!(sticky_duration.get(), 78_000_u32);
97 }
98
99 #[test]
100 fn serialize_sticky() {
101 let sticky = StickyDurationMs::new_clamped(60_000_u32);
102 let ser = serde_json::to_string(&sticky).unwrap();
103 assert_eq!(ser, "60000");
104 }
105
106 #[test]
107 fn deserialize_sticky_oob() {
108 let raw = "3600001";
109 let res: Result<StickyDurationMs, _> = serde_json::from_str(raw);
110 assert!(res.is_err());
111
112 let err = res.unwrap_err();
113 let err_msg = err.to_string();
114 assert!(err_msg.contains(
115 "invalid value: integer `3600001`, expected an integer in the range 0-3600000"
116 ));
117 }
118
119 #[test]
120 fn deserialize_sticky_float_err() {
121 let raw = "3000.0";
122 let res: Result<StickyDurationMs, _> = serde_json::from_str(raw);
123 assert!(res.is_err());
124
125 let err = res.unwrap_err();
126 let err_msg = err.to_string();
127 assert!(err_msg.contains(
128 "invalid type: floating point `3000.0`, expected an integer in the range 0-3600000"
129 ));
130 }
131
132 #[test]
133 fn deserialize_sticky_neg_err() {
134 let raw = "-1";
135 let res: Result<StickyDurationMs, _> = serde_json::from_str(raw);
136 assert!(res.is_err());
137
138 let err = res.unwrap_err();
139 let err_msg = err.to_string();
140 println!("{}", err_msg);
141 assert!(
142 err_msg.contains(
143 "invalid value: integer `-1`, expected an integer in the range 0-3600000"
144 )
145 );
146 }
147
148 #[test]
149 fn deserialize_sticky_object_ok() {
150 let ser = r#"{"duration_ms":78000}"#;
151 let sticky_object: StickyObject = serde_json::from_str(ser).unwrap();
152 assert_eq!(sticky_object.duration_ms.get(), 78_000_u32);
153 }
154
155 #[test]
156 fn deserialize_sticky_object_err() {
157 let raw = r#"{"duration_ms":3600001}"#;
158 let res: Result<StickyObject, _> = serde_json::from_str(raw);
159 assert!(res.is_err());
160 }
161}