scolapasta_path/paths/
unix_wasi.rs

1use std::ffi::{OsStr, OsString};
2#[cfg(unix)]
3use std::os::unix::ffi::{OsStrExt, OsStringExt};
4#[cfg(target_os = "wasi")]
5use std::os::wasi::ffi::{OsStrExt, OsStringExt};
6use std::path::PathBuf;
7
8use super::is_explicit_relative_bytes;
9
10pub fn is_explicit_relative(path: &OsStr) -> bool {
11    let bytes = path.as_bytes();
12
13    is_explicit_relative_bytes(bytes)
14}
15
16#[expect(
17    clippy::unnecessary_wraps,
18    reason = "source compatibility with other platforms which may be fallible"
19)]
20pub fn normalize_slashes(path: PathBuf) -> Result<Vec<u8>, PathBuf> {
21    Ok(OsString::from(path).into_vec())
22}
23
24#[cfg(test)]
25mod tests {
26    use std::ffi::{OsStr, OsString};
27    #[cfg(unix)]
28    use std::os::unix::ffi::{OsStrExt, OsStringExt};
29    #[cfg(target_os = "wasi")]
30    use std::os::wasi::ffi::{OsStrExt, OsStringExt};
31    use std::path::PathBuf;
32
33    use super::{is_explicit_relative, normalize_slashes};
34
35    #[test]
36    fn empty() {
37        assert!(!is_explicit_relative(OsStr::new("")));
38    }
39
40    #[test]
41    fn single_char() {
42        assert!(!is_explicit_relative(OsStr::new("a")));
43    }
44
45    #[test]
46    fn single_dot() {
47        assert!(!is_explicit_relative(OsStr::new(".")));
48    }
49
50    #[test]
51    fn double_dot() {
52        assert!(!is_explicit_relative(OsStr::new("..")));
53    }
54
55    #[test]
56    fn triple_dot() {
57        assert!(!is_explicit_relative(OsStr::new("...")));
58    }
59
60    #[test]
61    fn single_dot_slash() {
62        assert!(is_explicit_relative(OsStr::new("./")));
63    }
64
65    #[test]
66    fn double_dot_slash() {
67        assert!(is_explicit_relative(OsStr::new("../")));
68    }
69
70    #[test]
71    fn absolute() {
72        let test_cases = [r"/bin", r"/home/artichoke"];
73        for path in test_cases {
74            assert!(
75                !is_explicit_relative(OsStr::new(path)),
76                "expected absolute path '{path}' to NOT be explicit relative path"
77            );
78        }
79    }
80
81    #[test]
82    fn relative() {
83        let test_cases = [r"temp", r"temp/../var"];
84        for path in test_cases {
85            assert!(
86                !is_explicit_relative(OsStr::new(path)),
87                "expected relative path '{path}' to NOT be explicit relative path"
88            );
89        }
90    }
91
92    #[test]
93    fn explicit_relative() {
94        let test_cases = [r"./cache", r"../cache", r"./.git", r"../.git"];
95        for path in test_cases {
96            assert!(
97                is_explicit_relative(OsStr::new(path)),
98                "expected relative path '{path}' to be explicit relative path"
99            );
100        }
101    }
102
103    #[test]
104    fn not_explicit_relative() {
105        let test_cases = [r"...\var", r".../var", r"\var", r"/var"];
106        for path in test_cases {
107            assert!(
108                !is_explicit_relative(OsStr::new(path)),
109                "expected path '{path}' to NOT be explicit relative path"
110            );
111        }
112    }
113
114    #[test]
115    fn forward_slash() {
116        let test_cases = [
117            r".\cache",
118            r"..\cache",
119            r".\.git",
120            r"..\.git",
121            r"...\var",
122            r".../var",
123            r"\var",
124            r"/var",
125        ];
126        for path in test_cases {
127            assert!(
128                !is_explicit_relative(OsStr::new(path)),
129                "expected relative path '{path}' to NOT be explicit relative path"
130            );
131        }
132    }
133
134    #[test]
135    fn invalid_utf8_dot_dot_slash() {
136        let path = OsStr::from_bytes(b"../\xFF\xFE");
137        assert!(is_explicit_relative(path));
138    }
139
140    #[test]
141    fn invalid_utf8_dot_slash() {
142        let path = OsStr::from_bytes(b"./\xFF\xFE");
143        assert!(is_explicit_relative(path));
144    }
145
146    #[test]
147    fn invalid_utf8_absolute() {
148        let path = OsStr::from_bytes(b"/\xFF\xFE");
149        assert!(!is_explicit_relative(path));
150    }
151
152    #[test]
153    fn invalid_utf8_relative() {
154        let path = OsStr::from_bytes(b"\xFF\xFE");
155        assert!(!is_explicit_relative(path));
156    }
157
158    #[test]
159    fn normalize_slashes_no_backslash() {
160        let path = PathBuf::from(r"abcxyz".to_string());
161        assert_eq!(normalize_slashes(path).unwrap(), b"abcxyz".to_vec());
162
163        let path = PathBuf::from(r"abc/xyz".to_string());
164        assert_eq!(normalize_slashes(path).unwrap(), b"abc/xyz".to_vec());
165    }
166
167    #[test]
168    fn normalize_slashes_backslash_noop() {
169        let path = PathBuf::from(r"abc\xyz".to_string());
170        assert_eq!(normalize_slashes(path).unwrap(), br"abc\xyz".to_vec());
171
172        let path = PathBuf::from(r"abc\xyz\123".to_string());
173        assert_eq!(normalize_slashes(path).unwrap(), br"abc\xyz\123".to_vec());
174    }
175
176    #[test]
177    fn normalize_slashes_invalid_utf8() {
178        let path = OsString::from_vec(b"abcxyz\xFF".to_vec());
179        assert_eq!(normalize_slashes(path.into()).unwrap(), b"abcxyz\xFF".to_vec());
180
181        let path = OsString::from_vec(b"abc/xyz\xFF".to_vec());
182        assert_eq!(normalize_slashes(path.into()).unwrap(), b"abc/xyz\xFF".to_vec());
183
184        let path = OsString::from_vec(b"abc\\xyz\xFF".to_vec());
185        assert_eq!(normalize_slashes(path.into()).unwrap(), b"abc\\xyz\xFF".to_vec());
186    }
187}