northroot_proof_engine/
ids.rs1use thiserror::Error;
18
19#[derive(Debug, Error, PartialEq, Eq)]
21pub enum OtelIdError {
22 #[error("trace_id must be exactly 32 lowercase hexadecimal characters, got: {0}")]
24 InvalidTraceId(String),
25
26 #[error("span_id must be exactly 16 lowercase hexadecimal characters, got: {0}")]
28 InvalidSpanId(String),
29}
30
31pub fn validate_trace_id(trace_id: &str) -> Result<(), OtelIdError> {
45 if trace_id.len() != 32 {
46 return Err(OtelIdError::InvalidTraceId(trace_id.to_string()));
47 }
48
49 if !trace_id
53 .chars()
54 .all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
55 {
56 return Err(OtelIdError::InvalidTraceId(trace_id.to_string()));
57 }
58
59 Ok(())
60}
61
62pub fn validate_span_id(span_id: &str) -> Result<(), OtelIdError> {
76 if span_id.len() != 16 {
77 return Err(OtelIdError::InvalidSpanId(span_id.to_string()));
78 }
79
80 if !span_id
84 .chars()
85 .all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
86 {
87 return Err(OtelIdError::InvalidSpanId(span_id.to_string()));
88 }
89
90 Ok(())
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn test_validate_trace_id_valid() {
99 assert!(validate_trace_id("4f9b7a7a9b0a4a9e8b0a4a9e8b0a4a9e").is_ok());
100 assert!(validate_trace_id("00000000000000000000000000000000").is_ok());
101 assert!(validate_trace_id("ffffffffffffffffffffffffffffffff").is_ok());
102 }
103
104 #[test]
105 fn test_validate_trace_id_invalid_length() {
106 assert!(validate_trace_id("4f9b7a7a9b0a4a9e8b0a4a9e8b0a4a").is_err());
107 assert!(validate_trace_id("4f9b7a7a9b0a4a9e8b0a4a9e8b0a4a9e8").is_err());
108 }
109
110 #[test]
111 fn test_validate_trace_id_invalid_case() {
112 assert!(validate_trace_id("4f9b7a7a9b0a4a9e8b0a4a9e8b0a4a9E").is_err());
113 assert!(validate_trace_id("4F9B7A7A9B0A4A9E8B0A4A9E8B0A4A9E").is_err());
114 }
115
116 #[test]
117 fn test_validate_trace_id_invalid_chars() {
118 assert!(validate_trace_id("4f9b7a7a9b0a4a9e8b0a4a9e8b0a4a9g").is_err());
119 assert!(validate_trace_id("4f9b7a7a9b0a4a9e8b0a4a9e8b0a4a9-").is_err());
120 }
121
122 #[test]
123 fn test_validate_span_id_valid() {
124 assert!(validate_span_id("d2b8c1e4f7a12345").is_ok());
125 assert!(validate_span_id("0000000000000000").is_ok());
126 assert!(validate_span_id("ffffffffffffffff").is_ok());
127 }
128
129 #[test]
130 fn test_validate_span_id_invalid_length() {
131 assert!(validate_span_id("d2b8c1e4f7a1234").is_err());
132 assert!(validate_span_id("d2b8c1e4f7a123456").is_err());
133 }
134
135 #[test]
136 fn test_validate_span_id_invalid_case() {
137 assert!(validate_span_id("d2b8c1e4f7a1234F").is_err());
138 assert!(validate_span_id("D2B8C1E4F7A12345").is_err());
139 }
140
141 #[test]
142 fn test_validate_span_id_invalid_chars() {
143 assert!(validate_span_id("d2b8c1e4f7a1234g").is_err());
144 assert!(validate_span_id("d2b8c1e4f7a1234-").is_err());
145 }
146}