allsource_core/domain/value_objects/
transaction_id.rs1use crate::error::Result;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use uuid::Uuid;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
21pub struct TransactionId(Uuid);
22
23impl TransactionId {
24 pub fn new() -> Self {
34 Self(Uuid::new_v4())
35 }
36
37 pub fn from_uuid(uuid: Uuid) -> Self {
49 Self(uuid)
50 }
51
52 pub fn parse(value: &str) -> Result<Self> {
65 let uuid = Uuid::parse_str(value).map_err(|e| {
66 crate::error::AllSourceError::InvalidInput(format!(
67 "Invalid transaction ID '{}': {}",
68 value, e
69 ))
70 })?;
71 Ok(Self(uuid))
72 }
73
74 pub fn as_uuid(&self) -> Uuid {
76 self.0
77 }
78
79 pub fn is_nil(&self) -> bool {
81 self.0.is_nil()
82 }
83
84 pub fn nil() -> Self {
86 Self(Uuid::nil())
87 }
88}
89
90impl Default for TransactionId {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl fmt::Display for TransactionId {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 write!(f, "{}", self.0)
99 }
100}
101
102impl From<Uuid> for TransactionId {
103 fn from(uuid: Uuid) -> Self {
104 Self(uuid)
105 }
106}
107
108impl From<TransactionId> for Uuid {
109 fn from(transaction_id: TransactionId) -> Self {
110 transaction_id.0
111 }
112}
113
114impl TryFrom<&str> for TransactionId {
115 type Error = crate::error::AllSourceError;
116
117 fn try_from(value: &str) -> Result<Self> {
118 TransactionId::parse(value)
119 }
120}
121
122impl TryFrom<String> for TransactionId {
123 type Error = crate::error::AllSourceError;
124
125 fn try_from(value: String) -> Result<Self> {
126 TransactionId::parse(&value)
127 }
128}
129
130impl AsRef<Uuid> for TransactionId {
131 fn as_ref(&self) -> &Uuid {
132 &self.0
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_create_transaction_id() {
142 let transaction_id = TransactionId::new();
143 assert!(!transaction_id.is_nil());
144 }
145
146 #[test]
147 fn test_from_uuid() {
148 let uuid = Uuid::new_v4();
149 let transaction_id = TransactionId::from_uuid(uuid);
150 assert_eq!(transaction_id.as_uuid(), uuid);
151 }
152
153 #[test]
154 fn test_parse_valid_uuid() {
155 let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
156 let transaction_id = TransactionId::parse(uuid_str);
157 assert!(transaction_id.is_ok());
158 assert_eq!(transaction_id.unwrap().to_string(), uuid_str);
159 }
160
161 #[test]
162 fn test_parse_invalid_uuid() {
163 let invalid = "not-a-uuid";
164 let result = TransactionId::parse(invalid);
165 assert!(result.is_err());
166 }
167
168 #[test]
169 fn test_nil_transaction_id() {
170 let nil_id = TransactionId::nil();
171 assert!(nil_id.is_nil());
172 assert_eq!(nil_id.to_string(), "00000000-0000-0000-0000-000000000000");
173 }
174
175 #[test]
176 fn test_default_creates_new_uuid() {
177 let id1 = TransactionId::default();
178 let id2 = TransactionId::default();
179 assert_ne!(id1, id2);
180 assert!(!id1.is_nil());
181 }
182
183 #[test]
184 fn test_display_trait() {
185 let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
186 let transaction_id = TransactionId::from_uuid(uuid);
187 assert_eq!(
188 format!("{}", transaction_id),
189 "550e8400-e29b-41d4-a716-446655440000"
190 );
191 }
192
193 #[test]
194 fn test_from_uuid_trait() {
195 let uuid = Uuid::new_v4();
196 let transaction_id: TransactionId = uuid.into();
197 assert_eq!(transaction_id.as_uuid(), uuid);
198 }
199
200 #[test]
201 fn test_into_uuid_trait() {
202 let transaction_id = TransactionId::new();
203 let uuid: Uuid = transaction_id.into();
204 assert_eq!(uuid, transaction_id.as_uuid());
205 }
206
207 #[test]
208 fn test_try_from_str() {
209 let transaction_id: Result<TransactionId> =
210 "550e8400-e29b-41d4-a716-446655440000".try_into();
211 assert!(transaction_id.is_ok());
212
213 let invalid: Result<TransactionId> = "invalid".try_into();
214 assert!(invalid.is_err());
215 }
216
217 #[test]
218 fn test_try_from_string() {
219 let transaction_id: Result<TransactionId> = "550e8400-e29b-41d4-a716-446655440000"
220 .to_string()
221 .try_into();
222 assert!(transaction_id.is_ok());
223
224 let invalid: Result<TransactionId> = String::new().try_into();
225 assert!(invalid.is_err());
226 }
227
228 #[test]
229 fn test_equality() {
230 let uuid = Uuid::new_v4();
231 let id1 = TransactionId::from_uuid(uuid);
232 let id2 = TransactionId::from_uuid(uuid);
233 let id3 = TransactionId::new();
234
235 assert_eq!(id1, id2);
236 assert_ne!(id1, id3);
237 }
238
239 #[test]
240 fn test_cloning() {
241 let id1 = TransactionId::new();
242 let id2 = id1; assert_eq!(id1, id2);
244 }
245
246 #[test]
247 fn test_hash_consistency() {
248 use std::collections::HashSet;
249
250 let uuid = Uuid::new_v4();
251 let id1 = TransactionId::from_uuid(uuid);
252 let id2 = TransactionId::from_uuid(uuid);
253
254 let mut set = HashSet::new();
255 set.insert(id1);
256
257 assert!(set.contains(&id2));
258 }
259
260 #[test]
261 fn test_serde_serialization() {
262 let transaction_id = TransactionId::parse("550e8400-e29b-41d4-a716-446655440000").unwrap();
263
264 let json = serde_json::to_string(&transaction_id).unwrap();
266 assert_eq!(json, "\"550e8400-e29b-41d4-a716-446655440000\"");
267
268 let deserialized: TransactionId = serde_json::from_str(&json).unwrap();
270 assert_eq!(deserialized, transaction_id);
271 }
272
273 #[test]
274 fn test_as_ref() {
275 let transaction_id = TransactionId::new();
276 let uuid_ref: &Uuid = transaction_id.as_ref();
277 assert_eq!(*uuid_ref, transaction_id.as_uuid());
278 }
279}