http_smtp_rele/
request_id.rs1use std::{fmt, str::FromStr};
10
11use serde::{Deserialize, Serialize};
12use ulid::Ulid;
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
19#[serde(transparent)]
20pub struct RequestId(String);
21
22impl RequestId {
23 pub fn new() -> Self {
25 RequestId(format!("req_{}", Ulid::new()))
26 }
27
28 pub fn as_str(&self) -> &str {
30 &self.0
31 }
32}
33
34impl Default for RequestId {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl fmt::Display for RequestId {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 f.write_str(&self.0)
43 }
44}
45
46#[derive(Debug, PartialEq, Eq)]
47pub struct RequestIdParseError;
48
49impl fmt::Display for RequestIdParseError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 f.write_str("invalid request_id: must be req_<ULID>")
52 }
53}
54
55impl FromStr for RequestId {
56 type Err = RequestIdParseError;
57
58 fn from_str(s: &str) -> Result<Self, Self::Err> {
59 let ulid_part = s.strip_prefix("req_").ok_or(RequestIdParseError)?;
60 Ulid::from_string(ulid_part).map_err(|_| RequestIdParseError)?;
61 Ok(RequestId(s.to_string()))
62 }
63}
64
65impl AsRef<str> for RequestId {
67 fn as_ref(&self) -> &str {
68 &self.0
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn new_id_has_req_prefix() {
78 let id = RequestId::new();
79 assert!(id.as_str().starts_with("req_"), "got: {id}");
80 }
81
82 #[test]
83 fn display_roundtrips_via_fromstr() {
84 let id = RequestId::new();
85 let s = id.to_string();
86 let parsed: RequestId = s.parse().unwrap();
87 assert_eq!(id, parsed);
88 }
89
90 #[test]
91 fn plain_uuid_rejected() {
92 let result = "550e8400-e29b-41d4-a716-446655440000".parse::<RequestId>();
93 assert!(result.is_err());
94 }
95
96 #[test]
97 fn wrong_prefix_rejected() {
98 let result = "id_01HX7Q9V6R6W9V8Y5E3E6E7M9A".parse::<RequestId>();
99 assert!(result.is_err());
100 }
101
102 #[test]
103 fn two_ids_are_unique() {
104 assert_ne!(RequestId::new(), RequestId::new());
105 }
106}