1 //! Path validation for the purpose of the [`fs`] module. This is decoupled from
2 //! [`Path`] and [`PathBuf`], as the Rust standard library also does it this
3 //! way. Instead, the FS implementation is responsible for that.
4 //!
5 //! [`PathBuf`]: super::PathBuf
6 //! [`fs`]: crate::fs
7
8 use super::Path;
9 use crate::fs::CHARACTER_DENY_LIST;
10 use crate::Char16;
11 use core::fmt::{self, Display, Formatter};
12
13 /// Errors related to file paths.
14 #[derive(Debug, Clone, Eq, PartialEq)]
15 pub enum PathError {
16 /// The path is empty / points to nothing.
17 Empty,
18 /// A component of the path is empty, i.e., two separators without content
19 /// in between were found.
20 EmptyComponent,
21 /// There are illegal characters in the path.
22 IllegalChar(Char16),
23 }
24
25 impl Display for PathError {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result26 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
27 match self {
28 Self::Empty => write!(f, "path is empty"),
29 Self::EmptyComponent => write!(f, "path contains an empty component"),
30 Self::IllegalChar(c) => {
31 write!(
32 f,
33 "path contains an illegal character (value {})",
34 u16::from(*c)
35 )
36 }
37 }
38 }
39 }
40
41 #[cfg(feature = "unstable")]
42 impl core::error::Error for PathError {}
43
44 /// Validates a path for the needs of the [`fs`] module.
45 ///
46 /// [`fs`]: crate::fs
validate_path<P: AsRef<Path>>(path: P) -> Result<(), PathError>47 pub fn validate_path<P: AsRef<Path>>(path: P) -> Result<(), PathError> {
48 let path = path.as_ref();
49 if path.is_empty() {
50 return Err(PathError::Empty);
51 }
52 for component in path.components() {
53 if component.is_empty() {
54 return Err(PathError::EmptyComponent);
55 } else if let Some(char) = component
56 .as_slice()
57 .iter()
58 .find(|c| CHARACTER_DENY_LIST.contains(c))
59 {
60 return Err(PathError::IllegalChar(*char));
61 }
62 }
63 Ok(())
64 }
65
66 #[cfg(test)]
67 mod tests {
68 use super::*;
69 use crate::fs::PathBuf;
70 use crate::{cstr16, CString16};
71
72 #[test]
test_validate_path()73 fn test_validate_path() {
74 validate_path(cstr16!("hello\\foo\\bar")).unwrap();
75
76 let err = validate_path(cstr16!("hello\\f>oo\\bar")).unwrap_err();
77 assert_eq!(err, PathError::IllegalChar(CHARACTER_DENY_LIST[6]));
78
79 let err = validate_path(cstr16!("hello\\\\bar")).unwrap_err();
80 assert_eq!(err, PathError::EmptyComponent);
81
82 let empty_cstring16 = CString16::try_from("").unwrap();
83 let path = PathBuf::from(empty_cstring16);
84 let err = validate_path(path).unwrap_err();
85 assert_eq!(err, PathError::Empty)
86 }
87 }
88