1use std::fmt;
2use std::str::FromStr;
3
4#[derive(Clone, PartialEq, Eq)]
9pub enum StringFormat {
10 DateTime,
11 Date,
12 Time,
13 Duration,
14 Email,
15 IdnEmail,
16 Hostname,
17 IdnHostname,
18 Ipv4,
19 Ipv6,
20 Uri,
21 UriReference,
22 Iri,
23 IriReference,
24 Uuid,
25 UriTemplate,
26 JsonPointer,
27 RelativeJsonPointer,
28 Regex,
29 Unknown(String),
30}
31
32impl FromStr for StringFormat {
33 type Err = std::convert::Infallible;
34
35 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
36 Ok(match s {
37 "date-time" => StringFormat::DateTime,
38 "date" => StringFormat::Date,
39 "time" => StringFormat::Time,
40 "duration" => StringFormat::Duration,
41 "email" => StringFormat::Email,
42 "idn-email" => StringFormat::IdnEmail,
43 "hostname" => StringFormat::Hostname,
44 "idn-hostname" => StringFormat::IdnHostname,
45 "ipv4" => StringFormat::Ipv4,
46 "ipv6" => StringFormat::Ipv6,
47 "uri" => StringFormat::Uri,
48 "uri-reference" => StringFormat::UriReference,
49 "iri" => StringFormat::Iri,
50 "iri-reference" => StringFormat::IriReference,
51 "uuid" => StringFormat::Uuid,
52 "uri-template" => StringFormat::UriTemplate,
53 "json-pointer" => StringFormat::JsonPointer,
54 "relative-json-pointer" => StringFormat::RelativeJsonPointer,
55 "regex" => StringFormat::Regex,
56 other => StringFormat::Unknown(other.to_string()),
57 })
58 }
59}
60
61impl fmt::Display for StringFormat {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 StringFormat::DateTime => write!(f, "date-time"),
65 StringFormat::Date => write!(f, "date"),
66 StringFormat::Time => write!(f, "time"),
67 StringFormat::Duration => write!(f, "duration"),
68 StringFormat::Email => write!(f, "email"),
69 StringFormat::IdnEmail => write!(f, "idn-email"),
70 StringFormat::Hostname => write!(f, "hostname"),
71 StringFormat::IdnHostname => write!(f, "idn-hostname"),
72 StringFormat::Ipv4 => write!(f, "ipv4"),
73 StringFormat::Ipv6 => write!(f, "ipv6"),
74 StringFormat::Uri => write!(f, "uri"),
75 StringFormat::UriReference => write!(f, "uri-reference"),
76 StringFormat::Iri => write!(f, "iri"),
77 StringFormat::IriReference => write!(f, "iri-reference"),
78 StringFormat::Uuid => write!(f, "uuid"),
79 StringFormat::UriTemplate => write!(f, "uri-template"),
80 StringFormat::JsonPointer => write!(f, "json-pointer"),
81 StringFormat::RelativeJsonPointer => write!(f, "relative-json-pointer"),
82 StringFormat::Regex => write!(f, "regex"),
83 StringFormat::Unknown(s) => write!(f, "{s}"),
84 }
85 }
86}
87
88impl fmt::Debug for StringFormat {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 match self {
91 StringFormat::Unknown(s) => write!(f, "StringFormat::Unknown({s:?})"),
92 _ => write!(f, "StringFormat::{}", capitalize_variant(self)),
93 }
94 }
95}
96
97fn capitalize_variant(format: &StringFormat) -> &'static str {
98 match format {
99 StringFormat::DateTime => "DateTime",
100 StringFormat::Date => "Date",
101 StringFormat::Time => "Time",
102 StringFormat::Duration => "Duration",
103 StringFormat::Email => "Email",
104 StringFormat::IdnEmail => "IdnEmail",
105 StringFormat::Hostname => "Hostname",
106 StringFormat::IdnHostname => "IdnHostname",
107 StringFormat::Ipv4 => "Ipv4",
108 StringFormat::Ipv6 => "Ipv6",
109 StringFormat::Uri => "Uri",
110 StringFormat::UriReference => "UriReference",
111 StringFormat::Iri => "Iri",
112 StringFormat::IriReference => "IriReference",
113 StringFormat::Uuid => "Uuid",
114 StringFormat::UriTemplate => "UriTemplate",
115 StringFormat::JsonPointer => "JsonPointer",
116 StringFormat::RelativeJsonPointer => "RelativeJsonPointer",
117 StringFormat::Regex => "Regex",
118 StringFormat::Unknown(_) => unreachable!(),
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_from_str_known_formats() {
128 let cases = [
129 ("date-time", StringFormat::DateTime),
130 ("date", StringFormat::Date),
131 ("time", StringFormat::Time),
132 ("duration", StringFormat::Duration),
133 ("email", StringFormat::Email),
134 ("idn-email", StringFormat::IdnEmail),
135 ("hostname", StringFormat::Hostname),
136 ("idn-hostname", StringFormat::IdnHostname),
137 ("ipv4", StringFormat::Ipv4),
138 ("ipv6", StringFormat::Ipv6),
139 ("uri", StringFormat::Uri),
140 ("uri-reference", StringFormat::UriReference),
141 ("iri", StringFormat::Iri),
142 ("iri-reference", StringFormat::IriReference),
143 ("uuid", StringFormat::Uuid),
144 ("uri-template", StringFormat::UriTemplate),
145 ("json-pointer", StringFormat::JsonPointer),
146 ("relative-json-pointer", StringFormat::RelativeJsonPointer),
147 ("regex", StringFormat::Regex),
148 ];
149 for (input, expected) in cases {
150 let parsed: StringFormat = input.parse().unwrap();
151 assert_eq!(parsed, expected, "Failed for input: {input}");
152 }
153 }
154
155 #[test]
156 fn test_from_str_unknown_format() {
157 let parsed: StringFormat = "my-custom-format".parse().unwrap();
158 assert_eq!(
159 parsed,
160 StringFormat::Unknown("my-custom-format".to_string())
161 );
162 }
163
164 #[test]
165 fn test_display_roundtrip() {
166 let cases = [
167 "date-time",
168 "date",
169 "time",
170 "duration",
171 "email",
172 "idn-email",
173 "hostname",
174 "idn-hostname",
175 "ipv4",
176 "ipv6",
177 "uri",
178 "uri-reference",
179 "iri",
180 "iri-reference",
181 "uuid",
182 "uri-template",
183 "json-pointer",
184 "relative-json-pointer",
185 "regex",
186 ];
187 for input in cases {
188 let parsed: StringFormat = input.parse().unwrap();
189 assert_eq!(parsed.to_string(), input);
190 }
191 }
192
193 #[test]
194 fn test_debug_format() {
195 assert_eq!(
196 format!("{:?}", StringFormat::DateTime),
197 "StringFormat::DateTime"
198 );
199 assert_eq!(
200 format!("{:?}", StringFormat::Unknown("custom".to_string())),
201 r#"StringFormat::Unknown("custom")"#
202 );
203 }
204}