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
65
66
67
68
69
70
71
72
73
74
75
use std::path::{Component, Path, PathBuf};
pub fn normalize_path(p: &Path) -> PathBuf {
let mut stack: Vec<Component> = vec![];
// We assume .components() removes redundant consecutive path separators.
// Note that .components() also does some normalization of '.' on its own anyways.
// This '.' normalization happens to be compatible with the approach below.
for component in p.components() {
match component {
// Drop CurDir components, do not even push onto the stack.
Component::CurDir => {}
// For ParentDir components, we need to use the contents of the stack.
Component::ParentDir => {
// Look at the top element of stack, if any.
let top = stack.last().cloned();
match top {
// A component is on the stack, need more pattern matching.
Some(c) => {
match c {
// Push the ParentDir on the stack.
Component::Prefix(_) => {
stack.push(component);
}
// The parent of a RootDir is itself, so drop the ParentDir (no-op).
Component::RootDir => {}
// A CurDir should never be found on the stack, since they are dropped when seen.
Component::CurDir => {
unreachable!();
}
// If a ParentDir is found, it must be due to it piling up at the start of a path.
// Push the new ParentDir onto the stack.
Component::ParentDir => {
stack.push(component);
}
// If a Normal is found, pop it off.
Component::Normal(_) => {
let _ = stack.pop();
}
}
}
// Stack is empty, so path is empty, just push.
None => {
stack.push(component);
}
}
}
// All others, simply push onto the stack.
_ => {
stack.push(component);
}
}
}
// If an empty PathBuf would be return, instead return CurDir ('.').
if stack.is_empty() {
return PathBuf::from(Component::CurDir.as_os_str());
}
let mut norm_path = PathBuf::new();
for item in &stack {
norm_path.push(item.as_os_str());
}
norm_path
}