1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
use {
std::path::{Component, Path, PathBuf},
};
/// Improve the path to try remove and solve .. token.
///
/// This assumes that `a/b/../c` is `a/c` which might be different from
/// what the OS would have chosen when b is a link. This is OK
/// for broot verb arguments but can't be generally used elsewhere
/// (a more general solution would probably query the FS and just
/// resolve b in case of links).
///
/// This function ensures a given path ending with '/' still
/// ends with '/' after normalization.
pub fn normalize_path<P: AsRef<Path>>(path: P) -> PathBuf {
let ends_with_slash = path.as_ref().to_str().map_or(false, |s| s.ends_with('/'));
let mut normalized = PathBuf::new();
for component in path.as_ref().components() {
match &component {
Component::ParentDir => {
if !normalized.pop() {
normalized.push(component);
}
}
_ => {
normalized.push(component);
}
}
}
if ends_with_slash {
normalized.push("");
}
normalized
}
#[cfg(test)]
mod path_normalize_tests {
use super::normalize_path;
fn check(before: &str, after: &str) {
println!("-----------------\nnormalizing {:?}", before);
assert_eq!(normalize_path(before.to_string()).to_string_lossy(), after);
}
#[test]
fn test_path_normalization() {
check("/abc/test/../thing.png", "/abc/thing.png");
check("/abc/def/../../thing.png", "/thing.png");
check("/home/dys/test", "/home/dys/test");
check("/home/dys", "/home/dys");
check("/home/dys/", "/home/dys/");
check("/home/dys/..", "/home");
check("/home/dys/../", "/home/");
check("/..", "/..");
check("../test", "../test");
check("/home/dys/../../../test", "/../test");
check("π/2", "π/2");
check(
"/home/dys/dev/broot/../../../canop/test",
"/home/canop/test",
);
}
}
|