objects/object/
operation_id.rs1use std::{fmt, str::FromStr};
11
12use serde::{Deserialize, Serialize};
13use uuid::Uuid;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
16#[serde(transparent)]
17pub struct OperationId(pub Uuid);
18
19impl OperationId {
20 pub fn new() -> Self {
21 Self(Uuid::now_v7())
25 }
26
27 pub fn from_uuid(uuid: Uuid) -> Self {
28 Self(uuid)
29 }
30
31 pub fn as_uuid(&self) -> Uuid {
32 self.0
33 }
34
35 pub fn as_bytes(&self) -> &[u8; 16] {
36 self.0.as_bytes()
37 }
38}
39
40impl Default for OperationId {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl fmt::Display for OperationId {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 self.0.fmt(f)
49 }
50}
51
52#[derive(Debug, thiserror::Error)]
53pub enum OperationIdParseError {
54 #[error("invalid operation id: {0}")]
55 InvalidUuid(#[from] uuid::Error),
56}
57
58impl FromStr for OperationId {
59 type Err = OperationIdParseError;
60
61 fn from_str(s: &str) -> Result<Self, Self::Err> {
62 Ok(Self(Uuid::parse_str(s)?))
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn new_generates_distinct_ids() {
72 let a = OperationId::new();
73 let b = OperationId::new();
74 assert_ne!(a, b);
75 }
76
77 #[test]
78 fn display_round_trips_through_from_str() {
79 let id = OperationId::new();
80 let parsed: OperationId = id.to_string().parse().unwrap();
81 assert_eq!(id, parsed);
82 }
83
84 #[test]
85 fn rejects_garbage() {
86 assert!("not-a-uuid".parse::<OperationId>().is_err());
87 }
88
89 #[test]
90 fn serde_roundtrip() {
91 let id = OperationId::new();
92 let json = serde_json::to_string(&id).unwrap();
93 let back: OperationId = serde_json::from_str(&json).unwrap();
94 assert_eq!(id, back);
95 }
96}