use_diagnostic_code/
lib.rs1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5
6#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct DiagnosticCode(String);
9
10impl DiagnosticCode {
11 pub fn new(value: impl AsRef<str>) -> Result<Self, DiagnosticCodeError> {
17 let trimmed = value.as_ref().trim();
18
19 if trimmed.is_empty() {
20 return Err(DiagnosticCodeError::Empty);
21 }
22
23 Ok(Self(trimmed.to_string()))
24 }
25
26 #[must_use]
28 pub fn as_str(&self) -> &str {
29 &self.0
30 }
31
32 #[must_use]
34 pub fn into_string(self) -> String {
35 self.0
36 }
37}
38
39impl AsRef<str> for DiagnosticCode {
40 fn as_ref(&self) -> &str {
41 self.as_str()
42 }
43}
44
45impl fmt::Display for DiagnosticCode {
46 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
47 formatter.write_str(self.as_str())
48 }
49}
50
51impl FromStr for DiagnosticCode {
52 type Err = DiagnosticCodeError;
53
54 fn from_str(value: &str) -> Result<Self, Self::Err> {
55 Self::new(value)
56 }
57}
58
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
61pub enum DiagnosticCodeError {
62 Empty,
64}
65
66impl fmt::Display for DiagnosticCodeError {
67 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match self {
69 Self::Empty => formatter.write_str("diagnostic code cannot be empty"),
70 }
71 }
72}
73
74impl std::error::Error for DiagnosticCodeError {}
75
76#[cfg(test)]
77mod tests {
78 use super::{DiagnosticCode, DiagnosticCodeError};
79
80 #[test]
81 fn accepts_valid_code() {
82 let code = DiagnosticCode::new("CONFIG001").expect("code should be valid");
83
84 assert_eq!(code.as_str(), "CONFIG001");
85 }
86
87 #[test]
88 fn rejects_empty_code() {
89 assert_eq!(DiagnosticCode::new(" "), Err(DiagnosticCodeError::Empty));
90 }
91
92 #[test]
93 fn trims_surrounding_whitespace() {
94 let code = DiagnosticCode::new(" DATA.INVALID_SHAPE ").expect("code should be valid");
95
96 assert_eq!(code.as_str(), "DATA.INVALID_SHAPE");
97 }
98
99 #[test]
100 fn display_round_trips_through_parse() {
101 let code = DiagnosticCode::new("VALIDATE_MISSING_FIELD").expect("code should be valid");
102 let parsed: DiagnosticCode = code
103 .to_string()
104 .parse()
105 .expect("displayed code should parse");
106
107 assert_eq!(parsed, code);
108 }
109
110 #[test]
111 fn ordering_is_deterministic() {
112 let mut codes = [
113 DiagnosticCode::new("DATA.INVALID_SHAPE").expect("code should be valid"),
114 DiagnosticCode::new("CONFIG001").expect("code should be valid"),
115 DiagnosticCode::new("VALIDATE_MISSING_FIELD").expect("code should be valid"),
116 ];
117
118 codes.sort();
119
120 assert_eq!(codes[0].as_str(), "CONFIG001");
121 assert_eq!(codes[1].as_str(), "DATA.INVALID_SHAPE");
122 assert_eq!(codes[2].as_str(), "VALIDATE_MISSING_FIELD");
123 }
124}