1use std::{fmt, fmt::Write, str::FromStr};
2
3use arrayvec::ArrayString;
4use js_sys::JsString;
5use serde::{
6 de::{Error, Visitor},
7 Deserialize, Serialize,
8};
9
10use super::errors::RawObjectIdParseError;
11
12const MAX_PACKED_VAL: u128 = (1 << (32 * 3)) - 1;
13
14#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
33pub struct RawObjectId {
34 packed: u128,
35}
36
37impl fmt::Debug for RawObjectId {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 f.debug_struct("RawObjectId")
40 .field("packed", &self.packed)
41 .field("real", &self.to_string())
42 .finish()
43 }
44}
45
46impl fmt::Display for RawObjectId {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 write!(
49 f,
50 "{:0width$x}",
51 self.packed >> 32,
52 width = (self.packed as u32) as usize
53 )
54 }
55}
56
57impl Serialize for RawObjectId {
58 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59 where
60 S: serde::Serializer,
61 {
62 if serializer.is_human_readable() {
63 serializer.collect_str(&self.to_array_string())
64 } else {
65 serializer.serialize_bytes(&self.packed.to_be_bytes())
66 }
67 }
68}
69
70struct RawObjectIdVisitor;
71
72impl Visitor<'_> for RawObjectIdVisitor {
73 type Value = RawObjectId;
74
75 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
76 formatter.write_str("a string or bytes representing an object id")
77 }
78
79 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
80 where
81 E: Error,
82 {
83 RawObjectId::from_str(v).map_err(|e| E::custom(format!("Could not parse object id: {e}")))
84 }
85
86 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
87 where
88 E: Error,
89 {
90 v.try_into()
91 .map(u128::from_be_bytes)
92 .map(RawObjectId::from_packed)
93 .map_err(|e| E::custom(format!("Could not parse object id: {e}")))
94 }
95}
96
97impl<'de> Deserialize<'de> for RawObjectId {
98 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
99 where
100 D: serde::Deserializer<'de>,
101 {
102 if deserializer.is_human_readable() {
103 deserializer.deserialize_str(RawObjectIdVisitor)
104 } else {
105 deserializer.deserialize_bytes(RawObjectIdVisitor)
106 }
107 }
108}
109
110impl FromStr for RawObjectId {
111 type Err = RawObjectIdParseError;
112
113 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 let u128_id = u128::from_str_radix(s, 16)?;
117 let pad_length = s.len() as u128;
120
121 if u128_id > MAX_PACKED_VAL || pad_length > 24 {
122 return Err(RawObjectIdParseError::value_too_large(u128_id));
123 }
124
125 Ok(Self::from((u128_id << 32) | pad_length))
126 }
127}
128
129impl TryFrom<JsString> for RawObjectId {
130 type Error = RawObjectIdParseError;
131
132 fn try_from(js_id: JsString) -> Result<Self, Self::Error> {
133 let id: String = js_id.into();
134 RawObjectId::from_str(&id)
135 }
136}
137
138impl RawObjectId {
139 pub const fn from_packed(packed: u128) -> Self {
144 RawObjectId { packed }
145 }
146
147 pub fn to_array_string(&self) -> ArrayString<24> {
152 let mut res = ArrayString::new();
153 write!(res, "{self}").expect("expected formatting into a fixed-sized buffer to succeed");
154 res
155 }
156}
157
158impl From<RawObjectId> for ArrayString<24> {
159 fn from(id: RawObjectId) -> Self {
160 id.to_array_string()
161 }
162}
163
164impl From<RawObjectId> for String {
165 fn from(id: RawObjectId) -> Self {
166 id.to_string()
167 }
168}
169
170impl From<RawObjectId> for u128 {
171 fn from(id: RawObjectId) -> Self {
172 id.packed
173 }
174}
175
176impl From<u128> for RawObjectId {
177 fn from(packed: u128) -> Self {
178 Self::from_packed(packed)
179 }
180}
181
182#[cfg(test)]
183mod test {
184 use super::RawObjectId;
185
186 const TEST_IDS: &[&str] = &[
187 "0",
188 "1",
189 "ffffffffffffffffffffffff",
191 "06aebab343040c9baaa22322",
193 "000000000000000000000001",
194 "100000000000000000000000",
195 "5bbcab1d9099fc012e632dbc",
197 "5bbcab1d9099fc012e632dbd",
198 "10000000000000000",
199 "1000000000000000",
200 "bc03381d32f6790",
201 "0df4aea318bd552",
203 "000000000000f00",
204 "100000000",
205 "10000000",
206 ];
207
208 #[test]
209 fn rust_display_rust_fromstr_roundtrip() {
210 for id in TEST_IDS {
211 let parsed: RawObjectId = id.parse().unwrap();
212 assert_eq!(&*parsed.to_string(), *id);
213 }
214 }
215
216 #[test]
217 fn rust_to_array_string_rust_fromstr_roundtrip() {
218 for id in TEST_IDS {
219 let parsed: RawObjectId = id.parse().unwrap();
220 assert_eq!(&*parsed.to_array_string(), *id);
221 }
222 }
223
224 #[test]
225 fn rust_to_u128_from_u128_roundtrip() {
226 for id in TEST_IDS {
227 let parsed: RawObjectId = id.parse().unwrap();
228 let int = u128::from(parsed);
229 let reparsed: RawObjectId = int.into();
230 assert_eq!(parsed, reparsed);
231 assert_eq!(reparsed.to_string(), *id);
232 }
233 }
234
235 #[test]
236 fn rust_to_serde_json_from_serde_json_roundtrip() {
237 for id in TEST_IDS {
238 let parsed: RawObjectId = id.parse().unwrap();
239 let serialized = serde_json::to_string(&parsed).unwrap();
240 let reparsed: RawObjectId = serde_json::from_str(&serialized).unwrap();
241 assert_eq!(parsed, reparsed);
242 assert_eq!(reparsed.to_string(), *id);
243 }
244 }
245
246 #[test]
247 fn rust_vec_to_serde_json_from_serde_json_roundtrip() {
248 let mut ids_parsed = vec![];
249 for id in TEST_IDS {
250 let parsed: RawObjectId = id.parse().unwrap();
251 ids_parsed.push(parsed);
252 }
253 let serialized = serde_json::to_string(&ids_parsed).unwrap();
254 let ids_reparsed: Vec<RawObjectId> = serde_json::from_str(&serialized).unwrap();
255 assert_eq!(ids_parsed, ids_reparsed);
256 }
257
258 #[test]
259 fn rust_to_serde_bincode_from_serde_bincode_roundtrip() {
260 for id in TEST_IDS {
261 let parsed: RawObjectId = id.parse().unwrap();
262 let serialized = bincode::serialize(&parsed).unwrap();
263 let reparsed: RawObjectId = bincode::deserialize(&serialized).unwrap();
264 assert_eq!(parsed, reparsed);
265 assert_eq!(reparsed.to_string(), *id);
266 }
267 }
268
269 #[test]
270 fn rust_vec_to_serde_bincode_from_serde_bincode_roundtrip() {
271 let mut ids_parsed = vec![];
272 for id in TEST_IDS {
273 let parsed: RawObjectId = id.parse().unwrap();
274 ids_parsed.push(parsed);
275 }
276 let serialized = bincode::serialize(&ids_parsed).unwrap();
277 let ids_reparsed: Vec<RawObjectId> = bincode::deserialize(&serialized).unwrap();
278 assert_eq!(ids_parsed, ids_reparsed);
279 }
280
281 #[rustfmt::skip]
284 const SORTED_IDS: &[&str] = &[
285 "0",
286 "00",
287 "000",
288 "0000",
289 "1",
290 "01",
291 "001",
292 "0001",
293 "2",
294 "a",
295 "f",
296 "00f",
297 "10",
298 "010",
299 "0010",
300 "100",
301 "0100",
302 "f00",
303 "0f00",
304 "1000",
305 ];
306
307 #[test]
308 fn sort_order() {
309 let mut ids_parsed = vec![];
310 for id in SORTED_IDS {
311 let parsed: RawObjectId = id.parse().unwrap();
312 ids_parsed.push(parsed);
313 }
314 let mut ids_sort: Vec<_> = ids_parsed.clone();
315 ids_sort.sort();
316 assert_eq!(ids_sort, ids_parsed);
317 }
318
319 const INVALID_IDS: &[&str] = &[
320 "",
322 "-1",
324 "-0",
325 "1000000000000000000000000",
327 "000000000000000000000000f",
329 "340282366920938463463374607431768211455",
331 "000000000000000000000000000000000000000999000340282366920938463463374607431768211455",
333 "g",
335 "💣",
336 "\n",
337 ];
338
339 #[test]
340 fn invalid_values_do_not_parse() {
341 for id in INVALID_IDS {
342 let res: Result<RawObjectId, _> = id.parse();
343 assert!(res.is_err());
344 }
345 }
346}