libcnb_package/
util.rs

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/// Normalizes a path without it needing to exist on the file system.
13///
14/// Works similarly to [`std::fs::canonicalize`] but without using the file system. This means that
15/// symbolic links will not be resolved. In return, it can be used before creating a path on the
16/// file system.
17#[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}