1use alloc::string::String;
2
3#[derive(Debug)]
7#[non_exhaustive]
8pub enum ErrorKind {
9 Empty,
11
12 CurrentDirectoryMarker,
14
15 ParentDirectoryMarker,
17
18 ContainsForwardSlash,
20
21 ContainsNullByte,
24
25 #[cfg(target_vendor = "apple")]
27 GetFileSystemRepresentationError,
28}
29
30impl ErrorKind {
31 pub(crate) fn into_error(self, original: impl Into<String>) -> Error {
33 Error {
34 original: original.into(),
35 kind: self,
36 }
37 }
38}
39
40impl core::fmt::Display for ErrorKind {
41 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42 match self {
43 Self::Empty => f.write_str("empty path element"),
44 Self::CurrentDirectoryMarker => f.write_str("current directory marker"),
45 Self::ParentDirectoryMarker => f.write_str("parent directory marker"),
46 Self::ContainsForwardSlash => f.write_str("contains forward slash"),
47 Self::ContainsNullByte => f.write_str("contains null byte"),
48 #[cfg(target_vendor = "apple")]
49 Self::GetFileSystemRepresentationError => {
50 f.write_str("CFStringGetFileSystemRepresentation failed")
51 }
52 }
53 }
54}
55
56#[derive(Debug)]
69pub struct Error {
70 original: String,
71 kind: ErrorKind,
72}
73
74impl Error {
75 #[must_use]
77 pub fn kind(&self) -> &ErrorKind {
78 &self.kind
79 }
80
81 #[must_use]
83 pub fn into_kind(self) -> ErrorKind {
84 self.kind
85 }
86
87 #[must_use]
89 pub fn original(&self) -> &str {
90 &self.original
91 }
92}
93
94impl core::fmt::Display for Error {
95 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 if self.original.is_empty() {
97 write!(f, "{}", self.kind)
98 } else {
99 write!(f, "{}: {:?}", self.kind, self.original)
100 }
101 }
102}
103
104impl core::error::Error for Error {}
105
106pub type Result<T> = core::result::Result<T, Error>;
108
109pub type ResultKind<T> = core::result::Result<T, ErrorKind>;
115
116#[cfg(test)]
117mod tests {
118 use alloc::format;
119 use alloc::string::{String, ToString};
120
121 #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
122 use wasm_bindgen_test::wasm_bindgen_test as test;
123
124 use super::ErrorKind;
125
126 #[test]
127 fn error_kind_display() {
128 assert_eq!(ErrorKind::Empty.to_string(), "empty path element");
129 assert_eq!(
130 ErrorKind::CurrentDirectoryMarker.to_string(),
131 "current directory marker"
132 );
133 assert_eq!(
134 ErrorKind::ParentDirectoryMarker.to_string(),
135 "parent directory marker"
136 );
137 assert_eq!(
138 ErrorKind::ContainsForwardSlash.to_string(),
139 "contains forward slash"
140 );
141 assert_eq!(
142 ErrorKind::ContainsNullByte.to_string(),
143 "contains null byte"
144 );
145 }
146
147 #[test]
148 fn into_error_stores_original() {
149 let err = ErrorKind::Empty.into_error(String::from(" "));
150 assert_eq!(err.original(), " ");
151 assert!(matches!(err.kind(), ErrorKind::Empty));
152 }
153
154 #[test]
155 fn into_error_empty_original() {
156 let err = ErrorKind::Empty.into_error(String::new());
157 assert_eq!(err.original(), "");
158 assert!(matches!(err.kind(), ErrorKind::Empty));
159 }
160
161 #[test]
162 fn into_kind_roundtrip() {
163 let err = ErrorKind::ContainsNullByte.into_error(String::from("a\0b"));
164 assert!(matches!(err.into_kind(), ErrorKind::ContainsNullByte));
165 }
166
167 #[test]
168 fn error_display_with_original() {
169 let err = ErrorKind::ContainsForwardSlash.into_error(String::from("a/b"));
170 assert_eq!(format!("{err}"), "contains forward slash: \"a/b\"");
171 }
172
173 #[test]
174 fn error_display_empty_original() {
175 let err = ErrorKind::Empty.into_error(String::new());
176 assert_eq!(format!("{err}"), "empty path element");
177 }
178
179 #[test]
180 fn error_debug() {
181 let err = ErrorKind::Empty.into_error(String::from("."));
182 let debug = format!("{err:?}");
183 assert!(debug.contains("Empty"));
184 assert!(debug.contains('.'));
185 }
186
187 #[test]
188 fn path_element_error_has_original() {
189 let err = crate::PathElementCS::new("a/b").unwrap_err();
190 assert!(matches!(err.kind(), ErrorKind::ContainsForwardSlash));
191 assert_eq!(err.original(), "a/b");
192 }
193
194 #[test]
195 fn path_element_error_empty() {
196 let err = crate::PathElementCS::new("").unwrap_err();
197 assert!(matches!(err.kind(), ErrorKind::Empty));
198 assert_eq!(err.original(), "");
199 }
200
201 #[test]
202 fn path_element_error_dot() {
203 let err = crate::PathElementCI::new(".").unwrap_err();
204 assert!(matches!(err.kind(), ErrorKind::CurrentDirectoryMarker));
205 assert_eq!(err.original(), ".");
206 }
207
208 #[test]
209 fn path_element_error_dotdot() {
210 let err = crate::PathElementCS::new("..").unwrap_err();
211 assert!(matches!(err.kind(), ErrorKind::ParentDirectoryMarker));
212 assert_eq!(err.original(), "..");
213 }
214
215 #[test]
216 fn path_element_error_null_byte() {
217 let err = crate::PathElementCS::new("a\0b").unwrap_err();
218 assert!(matches!(err.kind(), ErrorKind::ContainsNullByte));
219 assert_eq!(err.original(), "a\0b");
220 }
221
222 #[test]
223 fn path_element_error_whitespace_trimmed_to_empty() {
224 let err = crate::PathElementCS::new(" ").unwrap_err();
225 assert!(matches!(err.kind(), ErrorKind::Empty));
226 assert_eq!(err.original(), " ");
227 }
228
229 #[test]
230 fn path_element_error_bom_trimmed_to_dot() {
231 let err = crate::PathElementCS::new("\u{FEFF}.").unwrap_err();
232 assert!(matches!(err.kind(), ErrorKind::CurrentDirectoryMarker));
233 assert_eq!(err.original(), "\u{FEFF}.");
234 }
235}