use alloc::string::String;
#[derive(Debug)]
#[non_exhaustive]
pub enum ErrorKind {
Empty,
CurrentDirectoryMarker,
ParentDirectoryMarker,
ContainsForwardSlash,
ContainsNullByte,
#[cfg(target_vendor = "apple")]
GetFileSystemRepresentationError,
}
impl ErrorKind {
pub(crate) fn into_error(self, original: impl Into<String>) -> Error {
Error {
original: original.into(),
kind: self,
}
}
}
impl core::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Empty => f.write_str("empty path element"),
Self::CurrentDirectoryMarker => f.write_str("current directory marker"),
Self::ParentDirectoryMarker => f.write_str("parent directory marker"),
Self::ContainsForwardSlash => f.write_str("contains forward slash"),
Self::ContainsNullByte => f.write_str("contains null byte"),
#[cfg(target_vendor = "apple")]
Self::GetFileSystemRepresentationError => {
f.write_str("CFStringGetFileSystemRepresentation failed")
}
}
}
}
#[derive(Debug)]
pub struct Error {
original: String,
kind: ErrorKind,
}
impl Error {
#[must_use]
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
#[must_use]
pub fn into_kind(self) -> ErrorKind {
self.kind
}
#[must_use]
pub fn original(&self) -> &str {
&self.original
}
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.original.is_empty() {
write!(f, "{}", self.kind)
} else {
write!(f, "{}: {:?}", self.kind, self.original)
}
}
}
impl core::error::Error for Error {}
pub type Result<T> = core::result::Result<T, Error>;
pub type ResultKind<T> = core::result::Result<T, ErrorKind>;
#[cfg(test)]
mod tests {
use alloc::format;
use alloc::string::{String, ToString};
#[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
use wasm_bindgen_test::wasm_bindgen_test as test;
use super::ErrorKind;
#[test]
fn error_kind_display() {
assert_eq!(ErrorKind::Empty.to_string(), "empty path element");
assert_eq!(
ErrorKind::CurrentDirectoryMarker.to_string(),
"current directory marker"
);
assert_eq!(
ErrorKind::ParentDirectoryMarker.to_string(),
"parent directory marker"
);
assert_eq!(
ErrorKind::ContainsForwardSlash.to_string(),
"contains forward slash"
);
assert_eq!(
ErrorKind::ContainsNullByte.to_string(),
"contains null byte"
);
}
#[test]
fn into_error_stores_original() {
let err = ErrorKind::Empty.into_error(String::from(" "));
assert_eq!(err.original(), " ");
assert!(matches!(err.kind(), ErrorKind::Empty));
}
#[test]
fn into_error_empty_original() {
let err = ErrorKind::Empty.into_error(String::new());
assert_eq!(err.original(), "");
assert!(matches!(err.kind(), ErrorKind::Empty));
}
#[test]
fn into_kind_roundtrip() {
let err = ErrorKind::ContainsNullByte.into_error(String::from("a\0b"));
assert!(matches!(err.into_kind(), ErrorKind::ContainsNullByte));
}
#[test]
fn error_display_with_original() {
let err = ErrorKind::ContainsForwardSlash.into_error(String::from("a/b"));
assert_eq!(format!("{err}"), "contains forward slash: \"a/b\"");
}
#[test]
fn error_display_empty_original() {
let err = ErrorKind::Empty.into_error(String::new());
assert_eq!(format!("{err}"), "empty path element");
}
#[test]
fn error_debug() {
let err = ErrorKind::Empty.into_error(String::from("."));
let debug = format!("{err:?}");
assert!(debug.contains("Empty"));
assert!(debug.contains('.'));
}
#[test]
fn path_element_error_has_original() {
let err = crate::PathElementCS::new("a/b").unwrap_err();
assert!(matches!(err.kind(), ErrorKind::ContainsForwardSlash));
assert_eq!(err.original(), "a/b");
}
#[test]
fn path_element_error_empty() {
let err = crate::PathElementCS::new("").unwrap_err();
assert!(matches!(err.kind(), ErrorKind::Empty));
assert_eq!(err.original(), "");
}
#[test]
fn path_element_error_dot() {
let err = crate::PathElementCI::new(".").unwrap_err();
assert!(matches!(err.kind(), ErrorKind::CurrentDirectoryMarker));
assert_eq!(err.original(), ".");
}
#[test]
fn path_element_error_dotdot() {
let err = crate::PathElementCS::new("..").unwrap_err();
assert!(matches!(err.kind(), ErrorKind::ParentDirectoryMarker));
assert_eq!(err.original(), "..");
}
#[test]
fn path_element_error_null_byte() {
let err = crate::PathElementCS::new("a\0b").unwrap_err();
assert!(matches!(err.kind(), ErrorKind::ContainsNullByte));
assert_eq!(err.original(), "a\0b");
}
#[test]
fn path_element_error_whitespace_trimmed_to_empty() {
let err = crate::PathElementCS::new(" ").unwrap_err();
assert!(matches!(err.kind(), ErrorKind::Empty));
assert_eq!(err.original(), " ");
}
#[test]
fn path_element_error_bom_trimmed_to_dot() {
let err = crate::PathElementCS::new("\u{FEFF}.").unwrap_err();
assert!(matches!(err.kind(), ErrorKind::CurrentDirectoryMarker));
assert_eq!(err.original(), "\u{FEFF}.");
}
}