1use std::path::{Component, Path, PathBuf};
2
3#[must_use]
4pub fn absolutize_path(path: &Path, parent: &Path) -> PathBuf {
5 if path.is_relative() {
6 normalize_path(&parent.join(path))
7 } else {
8 PathBuf::from(path)
9 }
10}
11
12#[must_use]
18fn normalize_path(path: &Path) -> PathBuf {
19 let mut components = path.components().peekable();
20
21 let mut result = if let Some(component @ Component::Prefix(..)) = components.peek().copied() {
22 components.next();
23 PathBuf::from(component.as_os_str())
24 } else {
25 PathBuf::new()
26 };
27
28 for component in components {
29 match component {
30 Component::Prefix(..) => unreachable!(),
31 Component::RootDir => {
32 result.push(component.as_os_str());
33 }
34 Component::CurDir => {}
35 Component::ParentDir => {
36 result.pop();
37 }
38 Component::Normal(component) => {
39 result.push(component);
40 }
41 }
42 }
43
44 result
45}
46
47#[cfg(test)]
48mod test {
49 use super::normalize_path;
50 use std::path::PathBuf;
51
52 #[test]
53 fn test_normalize_path() {
54 assert_eq!(
55 normalize_path(&PathBuf::from("/foo/bar/baz")),
56 PathBuf::from("/foo/bar/baz")
57 );
58
59 assert_eq!(
60 normalize_path(&PathBuf::from("/foo/bar/../baz")),
61 PathBuf::from("/foo/baz")
62 );
63
64 assert_eq!(
65 normalize_path(&PathBuf::from("/foo/bar/./././././baz")),
66 PathBuf::from("/foo/bar/baz")
67 );
68
69 assert_eq!(
70 normalize_path(&PathBuf::from("/foo/bar/../../23/42/../.././hello.txt")),
71 PathBuf::from("/hello.txt")
72 );
73
74 assert_eq!(
75 normalize_path(&PathBuf::from("foo/bar/../../23/42/../.././hello.txt")),
76 PathBuf::from("hello.txt")
77 );
78 }
79}