1use std::path::{Component, Path};
4
5pub use path_clean::PathClean;
6
7pub fn unix_slash(root: &Path) -> String {
9 let mut res = String::with_capacity(root.as_os_str().len());
10 let mut parent_norm = false;
11 for comp in root.components() {
12 match comp {
13 Component::Prefix(p) => {
14 res.push_str(&p.as_os_str().to_string_lossy());
15 parent_norm = false;
16 }
17 Component::RootDir => {
18 res.push('/');
19 parent_norm = false;
20 }
21 Component::CurDir => {
22 parent_norm = false;
23 }
24 Component::ParentDir => {
25 if parent_norm {
26 res.push('/');
27 }
28 res.push_str("..");
29 parent_norm = true;
30 }
31 Component::Normal(p) => {
32 if parent_norm {
33 res.push('/');
34 }
35 res.push_str(&p.to_string_lossy());
36 parent_norm = true;
37 }
38 }
39 }
40
41 if res.is_empty() {
42 res.push('.');
43 }
44
45 res
46}
47
48pub use path_clean::clean;
50
51#[cfg(test)]
52mod test {
53 use std::path::{Path, PathBuf};
54
55 use super::{clean as inner_path_clean, unix_slash, PathClean};
56
57 pub fn clean<P: AsRef<Path>>(path: P) -> String {
58 unix_slash(&inner_path_clean(path))
59 }
60
61 #[test]
62 fn test_unix_slash() {
63 if cfg!(target_os = "windows") {
64 assert_eq!(
66 unix_slash(std::path::Path::new("C:\\Users\\a\\b\\c")),
67 "C:/Users/a/b/c"
68 );
69 assert_eq!(
70 unix_slash(std::path::Path::new("C:\\Users\\a\\b\\c\\")),
71 "C:/Users/a/b/c"
72 );
73 assert_eq!(unix_slash(std::path::Path::new("a\\b\\c")), "a/b/c");
74 assert_eq!(unix_slash(std::path::Path::new("C:\\")), "C:/");
75 assert_eq!(unix_slash(std::path::Path::new("C:\\\\")), "C:/");
76 assert_eq!(unix_slash(std::path::Path::new("C:")), "C:");
77 assert_eq!(unix_slash(std::path::Path::new("C:\\a")), "C:/a");
78 assert_eq!(unix_slash(std::path::Path::new("C:\\a\\")), "C:/a");
79 assert_eq!(unix_slash(std::path::Path::new("C:\\a\\b")), "C:/a/b");
80 assert_eq!(
81 unix_slash(std::path::Path::new("C:\\Users\\a\\..\\b\\c")),
82 "C:/Users/a/../b/c"
83 );
84 assert_eq!(
85 unix_slash(std::path::Path::new("C:\\Users\\a\\..\\b\\c\\")),
86 "C:/Users/a/../b/c"
87 );
88 assert_eq!(
89 unix_slash(std::path::Path::new("C:\\Users\\a\\..\\..")),
90 "C:/Users/a/../.."
91 );
92 assert_eq!(
93 unix_slash(std::path::Path::new("C:\\Users\\a\\..\\..\\")),
94 "C:/Users/a/../.."
95 );
96 }
97 assert_eq!(unix_slash(std::path::Path::new("/a/b/c")), "/a/b/c");
99 assert_eq!(unix_slash(std::path::Path::new("/a/b/c/")), "/a/b/c");
100 assert_eq!(unix_slash(std::path::Path::new("/")), "/");
101 assert_eq!(unix_slash(std::path::Path::new("//")), "/");
102 assert_eq!(unix_slash(std::path::Path::new("a")), "a");
103 assert_eq!(unix_slash(std::path::Path::new("a/")), "a");
104 assert_eq!(unix_slash(std::path::Path::new("a/b")), "a/b");
105 assert_eq!(unix_slash(std::path::Path::new("a/b/")), "a/b");
106 assert_eq!(unix_slash(std::path::Path::new("a/..")), "a/..");
107 assert_eq!(unix_slash(std::path::Path::new("a/../")), "a/..");
108 assert_eq!(unix_slash(std::path::Path::new("a/../..")), "a/../..");
109 assert_eq!(unix_slash(std::path::Path::new("a/../../")), "a/../..");
110 assert_eq!(unix_slash(std::path::Path::new("a/./b")), "a/b");
111 assert_eq!(unix_slash(std::path::Path::new("a/./b/")), "a/b");
112 assert_eq!(unix_slash(std::path::Path::new(".")), ".");
113 assert_eq!(unix_slash(std::path::Path::new("./")), ".");
114 assert_eq!(unix_slash(std::path::Path::new("./a")), "a");
115 assert_eq!(unix_slash(std::path::Path::new("./a/")), "a");
116 assert_eq!(unix_slash(std::path::Path::new("./a/b")), "a/b");
117 assert_eq!(unix_slash(std::path::Path::new("./a/b/")), "a/b");
118 assert_eq!(unix_slash(std::path::Path::new("./a/./b/")), "a/b");
119 }
120
121 #[test]
122 fn test_path_clean_empty_path_is_current_dir() {
123 assert_eq!(clean(""), ".");
124 }
125
126 #[test]
127 fn test_path_clean_clean_paths_dont_change() {
128 let tests = vec![(".", "."), ("..", ".."), ("/", "/")];
129
130 for test in tests {
131 assert_eq!(clean(test.0), test.1);
132 }
133 }
134
135 #[test]
136 fn test_path_clean_replace_multiple_slashes() {
137 let tests = vec![
138 ("/", "/"),
139 ("//", "/"),
140 ("///", "/"),
141 (".//", "."),
142 ("//..", "/"),
143 ("..//", ".."),
144 ("/..//", "/"),
145 ("/.//./", "/"),
146 ("././/./", "."),
147 ("path//to///thing", "path/to/thing"),
148 ("/path//to///thing", "/path/to/thing"),
149 ];
150
151 for test in tests {
152 assert_eq!(clean(test.0), test.1);
153 }
154 }
155
156 #[test]
157 fn test_path_clean_eliminate_current_dir() {
158 let tests = vec![
159 ("./", "."),
160 ("/./", "/"),
161 ("./test", "test"),
162 ("./test/./path", "test/path"),
163 ("/test/./path/", "/test/path"),
164 ("test/path/.", "test/path"),
165 ];
166
167 for test in tests {
168 assert_eq!(clean(test.0), test.1);
169 }
170 }
171
172 #[test]
173 fn test_path_clean_eliminate_parent_dir() {
174 let tests = vec![
175 ("/..", "/"),
176 ("/../test", "/test"),
177 ("test/..", "."),
178 ("test/path/..", "test"),
179 ("test/../path", "path"),
180 ("/test/../path", "/path"),
181 ("test/path/../../", "."),
182 ("test/path/../../..", ".."),
183 ("/test/path/../../..", "/"),
184 ("/test/path/../../../..", "/"),
185 ("test/path/../../../..", "../.."),
186 ("test/path/../../another/path", "another/path"),
187 ("test/path/../../another/path/..", "another"),
188 ("../test", "../test"),
189 ("../test/", "../test"),
190 ("../test/path", "../test/path"),
191 ("../test/..", ".."),
192 ];
193
194 for test in tests {
195 assert_eq!(clean(test.0), test.1);
196 }
197 }
198
199 #[test]
200 fn test_path_clean_pathbuf_trait() {
201 assert_eq!(
202 unix_slash(&PathBuf::from("/test/../path/").clean()),
203 "/path"
204 );
205 }
206
207 #[test]
208 fn test_path_clean_path_trait() {
209 assert_eq!(unix_slash(&Path::new("/test/../path/").clean()), "/path");
210 }
211
212 #[test]
213 #[cfg(target_os = "windows")]
214 fn test_path_clean_windows_paths() {
215 let tests = vec![
216 ("\\..", "/"),
217 ("\\..\\test", "/test"),
218 ("test\\..", "."),
219 ("test\\path\\..\\..\\..", ".."),
220 ("test\\path/..\\../another\\path", "another/path"), ("test\\path\\my/path", "test/path/my/path"), ("/dir\\../otherDir/test.json", "/otherDir/test.json"), ("c:\\test\\..", "c:/"), ("c:/test/..", "c:/"), ];
226
227 for test in tests {
228 assert_eq!(clean(test.0), test.1);
229 }
230 }
231}