1use serde::{Deserialize, Serialize};
4
5macro_rules! define_id {
6 ($(#[$meta:meta])* $name:ident) => {
7 $(#[$meta])*
8 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
9 #[serde(transparent)]
10 pub struct $name(String);
11
12 impl $name {
13 pub fn new(id: impl Into<String>) -> Self {
15 Self(id.into())
16 }
17
18 pub fn as_str(&self) -> &str {
20 &self.0
21 }
22
23 pub fn into_inner(self) -> String {
25 self.0
26 }
27 }
28
29 impl std::fmt::Display for $name {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 f.write_str(&self.0)
32 }
33 }
34
35 impl From<String> for $name {
36 fn from(s: String) -> Self {
37 Self(s)
38 }
39 }
40
41 impl From<&str> for $name {
42 fn from(s: &str) -> Self {
43 Self(s.to_owned())
44 }
45 }
46 };
47}
48
49define_id!(
50 ReservationId
52);
53
54define_id!(
55 IdempotencyKey
57);
58
59define_id!(
60 EventId
62);
63
64impl IdempotencyKey {
65 pub fn random() -> Self {
67 Self(uuid::Uuid::new_v4().to_string())
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn newtype_display_and_serde() {
77 let id = ReservationId::new("rsv_123");
78 assert_eq!(id.as_str(), "rsv_123");
79 assert_eq!(id.to_string(), "rsv_123");
80
81 let json = serde_json::to_string(&id).unwrap();
82 assert_eq!(json, "\"rsv_123\"");
83
84 let deserialized: ReservationId = serde_json::from_str(&json).unwrap();
85 assert_eq!(deserialized, id);
86 }
87
88 #[test]
89 fn idempotency_key_random() {
90 let k1 = IdempotencyKey::random();
91 let k2 = IdempotencyKey::random();
92 assert_ne!(k1, k2);
93 assert_eq!(k1.as_str().len(), 36); }
95
96 #[test]
97 fn from_conversions() {
98 let id: ReservationId = "rsv_abc".into();
99 assert_eq!(id.as_str(), "rsv_abc");
100
101 let id2: ReservationId = String::from("rsv_def").into();
102 assert_eq!(id2.as_str(), "rsv_def");
103 }
104
105 #[test]
106 fn into_inner() {
107 let id = ReservationId::new("rsv_xyz");
108 let inner: String = id.into_inner();
109 assert_eq!(inner, "rsv_xyz");
110
111 let ek = EventId::new("evt_123");
112 let inner2: String = ek.into_inner();
113 assert_eq!(inner2, "evt_123");
114 }
115}