1use alloc::string::String;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum ErrorKind {
8 Empty,
10
11 CurrentDirectoryMarker,
13
14 ParentDirectoryMarker,
16
17 ContainsForwardSlash,
19
20 ContainsNullByte,
23
24 ContainsControlCharacter,
27
28 ContainsBom,
30
31 InvalidUtf8,
33
34 ContainsUnassignedChar,
37
38 #[cfg(any(target_vendor = "apple", docsrs))]
41 #[cfg_attr(docsrs, doc(cfg(target_vendor = "apple")))]
42 GetFileSystemRepresentationError,
43}
44
45impl core::fmt::Display for ErrorKind {
46 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
47 match self {
48 Self::Empty => f.write_str("empty path element"),
49 Self::CurrentDirectoryMarker => f.write_str("current directory marker"),
50 Self::ParentDirectoryMarker => f.write_str("parent directory marker"),
51 Self::ContainsForwardSlash => f.write_str("contains forward slash"),
52 Self::ContainsNullByte => f.write_str("contains null byte"),
53 Self::ContainsControlCharacter => f.write_str("contains control character"),
54 Self::ContainsBom => f.write_str("contains BOM"),
55 Self::InvalidUtf8 => f.write_str("invalid UTF-8"),
56 Self::ContainsUnassignedChar => f.write_str("contains unassigned character"),
57 #[cfg(any(target_vendor = "apple", docsrs))]
58 Self::GetFileSystemRepresentationError => {
59 f.write_str("CFStringGetFileSystemRepresentation failed")
60 }
61 }
62 }
63}
64
65#[derive(Debug, Clone)]
87pub struct Error {
88 pub kind: ErrorKind,
90 pub original: String,
92}
93
94impl Error {
95 #[must_use]
97 pub fn new(kind: ErrorKind, original: impl Into<String>) -> Self {
98 Self {
99 kind,
100 original: original.into(),
101 }
102 }
103}
104
105impl core::fmt::Display for Error {
106 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
107 if self.original.is_empty() {
108 write!(f, "{}", self.kind)
109 } else {
110 write!(f, "{}: {:?}", self.kind, self.original)
111 }
112 }
113}
114
115impl core::error::Error for Error {}
116
117pub type Result<T> = core::result::Result<T, Error>;
119
120pub type ResultKind<T> = core::result::Result<T, ErrorKind>;
126
127#[cfg(test)]
128mod tests {
129 use alloc::format;
130 use alloc::string::ToString;
131
132 #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
133 use wasm_bindgen_test::wasm_bindgen_test as test;
134
135 use super::{Error, ErrorKind};
136
137 #[test]
138 fn error_kind_display() {
139 assert_eq!(ErrorKind::Empty.to_string(), "empty path element");
140 assert_eq!(
141 ErrorKind::CurrentDirectoryMarker.to_string(),
142 "current directory marker"
143 );
144 assert_eq!(
145 ErrorKind::ParentDirectoryMarker.to_string(),
146 "parent directory marker"
147 );
148 assert_eq!(
149 ErrorKind::ContainsForwardSlash.to_string(),
150 "contains forward slash"
151 );
152 assert_eq!(
153 ErrorKind::ContainsNullByte.to_string(),
154 "contains null byte"
155 );
156 assert_eq!(
157 ErrorKind::ContainsControlCharacter.to_string(),
158 "contains control character"
159 );
160 assert_eq!(ErrorKind::ContainsBom.to_string(), "contains BOM");
161 assert_eq!(ErrorKind::InvalidUtf8.to_string(), "invalid UTF-8");
162 assert_eq!(
163 ErrorKind::ContainsUnassignedChar.to_string(),
164 "contains unassigned character"
165 );
166 }
167
168 #[test]
169 fn error_display_with_original() {
170 let err = Error::new(ErrorKind::ContainsForwardSlash, "a/b");
171 assert_eq!(format!("{err}"), "contains forward slash: \"a/b\"");
172 }
173
174 #[test]
175 fn error_display_empty_original() {
176 let err = Error::new(ErrorKind::Empty, "");
177 assert_eq!(format!("{err}"), "empty path element");
178 }
179
180 #[test]
181 fn error_debug() {
182 let err = Error::new(ErrorKind::Empty, ".");
183 let debug = format!("{err:?}");
184 assert!(debug.contains("Empty"));
185 assert!(debug.contains('.'));
186 }
187
188 #[test]
189 fn path_element_error_has_original() {
190 let err = crate::PathElementCS::new("a/b").unwrap_err();
191 assert_eq!(err.kind, ErrorKind::ContainsForwardSlash);
192 assert_eq!(err.original, "a/b");
193 }
194
195 #[test]
196 fn path_element_error_empty() {
197 let err = crate::PathElementCS::new("").unwrap_err();
198 assert_eq!(err.kind, ErrorKind::Empty);
199 assert_eq!(err.original, "");
200 }
201
202 #[test]
203 fn path_element_error_dot() {
204 let err = crate::PathElementCI::new(".").unwrap_err();
205 assert_eq!(err.kind, ErrorKind::CurrentDirectoryMarker);
206 assert_eq!(err.original, ".");
207 }
208
209 #[test]
210 fn path_element_error_dotdot() {
211 let err = crate::PathElementCS::new("..").unwrap_err();
212 assert_eq!(err.kind, ErrorKind::ParentDirectoryMarker);
213 assert_eq!(err.original, "..");
214 }
215
216 #[test]
217 fn path_element_error_null_byte() {
218 let err = crate::PathElementCS::new("a\0b").unwrap_err();
219 assert_eq!(err.kind, ErrorKind::ContainsNullByte);
220 assert_eq!(err.original, "a\0b");
221 }
222
223 #[test]
224 fn path_element_error_control_character() {
225 let err = crate::PathElementCS::new("a\x01b").unwrap_err();
226 assert_eq!(err.kind, ErrorKind::ContainsControlCharacter);
227 assert_eq!(err.original, "a\x01b");
228 }
229
230 #[test]
231 fn path_element_error_bom() {
232 let err = crate::PathElementCS::new("\u{FEFF}hello").unwrap_err();
233 assert_eq!(err.kind, ErrorKind::ContainsBom);
234 assert_eq!(err.original, "\u{FEFF}hello");
235 }
236
237 #[test]
238 fn path_element_error_whitespace_trimmed_to_empty() {
239 let err = crate::PathElementCS::new(" ").unwrap_err();
240 assert_eq!(err.kind, ErrorKind::Empty);
241 assert_eq!(err.original, " ");
242 }
243}