axfs_vfs/
path.rs

1//! Utilities for path manipulation.
2
3use alloc::string::String;
4
5/// Returns the canonical form of the path with all intermediate components
6/// normalized.
7///
8/// It won't force convert the path to an absolute form.
9///
10/// # Examples
11///
12/// ```
13/// use axfs_vfs::path::canonicalize;
14///
15/// assert_eq!(canonicalize("/path/./to//foo"), "/path/to/foo");
16/// assert_eq!(canonicalize("/./path/to/../bar.rs"), "/path/bar.rs");
17/// assert_eq!(canonicalize("./foo/./bar"), "foo/bar");
18/// ```
19pub fn canonicalize(path: &str) -> String {
20    let mut buf = String::new();
21    let is_absolute = path.starts_with('/');
22    for part in path.split('/') {
23        match part {
24            "" | "." => continue,
25            ".." => {
26                while !buf.is_empty() {
27                    if buf == "/" {
28                        break;
29                    }
30                    let c = buf.pop().unwrap();
31                    if c == '/' {
32                        break;
33                    }
34                }
35            }
36            _ => {
37                if buf.is_empty() {
38                    if is_absolute {
39                        buf.push('/');
40                    }
41                } else if &buf[buf.len() - 1..] != "/" {
42                    buf.push('/');
43                }
44                buf.push_str(part);
45            }
46        }
47    }
48    if is_absolute && buf.is_empty() {
49        buf.push('/');
50    }
51    buf
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn test_path_canonicalize() {
60        assert_eq!(canonicalize(""), "");
61        assert_eq!(canonicalize("///"), "/");
62        assert_eq!(canonicalize("//a//.//b///c//"), "/a/b/c");
63        assert_eq!(canonicalize("/a/../"), "/");
64        assert_eq!(canonicalize("/a/../..///"), "/");
65        assert_eq!(canonicalize("a/../"), "");
66        assert_eq!(canonicalize("a/..//.."), "");
67        assert_eq!(canonicalize("././a"), "a");
68        assert_eq!(canonicalize(".././a"), "a");
69        assert_eq!(canonicalize("/././a"), "/a");
70        assert_eq!(canonicalize("/abc/../abc"), "/abc");
71        assert_eq!(canonicalize("/test"), "/test");
72        assert_eq!(canonicalize("/test/"), "/test");
73        assert_eq!(canonicalize("test/"), "test");
74        assert_eq!(canonicalize("test"), "test");
75        assert_eq!(canonicalize("/test//"), "/test");
76        assert_eq!(canonicalize("/test/foo"), "/test/foo");
77        assert_eq!(canonicalize("/test/foo/"), "/test/foo");
78        assert_eq!(canonicalize("/test/foo/bar"), "/test/foo/bar");
79        assert_eq!(canonicalize("/test/foo/bar//"), "/test/foo/bar");
80        assert_eq!(canonicalize("/test//foo/bar//"), "/test/foo/bar");
81        assert_eq!(canonicalize("/test//./foo/bar//"), "/test/foo/bar");
82        assert_eq!(canonicalize("/test//./.foo/bar//"), "/test/.foo/bar");
83        assert_eq!(canonicalize("/test//./..foo/bar//"), "/test/..foo/bar");
84        assert_eq!(canonicalize("/test//./../foo/bar//"), "/foo/bar");
85        assert_eq!(canonicalize("/test/../foo"), "/foo");
86        assert_eq!(canonicalize("/test/bar/../foo"), "/test/foo");
87        assert_eq!(canonicalize("../foo"), "foo");
88        assert_eq!(canonicalize("../foo/"), "foo");
89        assert_eq!(canonicalize("/../foo"), "/foo");
90        assert_eq!(canonicalize("/../foo/"), "/foo");
91        assert_eq!(canonicalize("/../../foo"), "/foo");
92        assert_eq!(canonicalize("/bleh/../../foo"), "/foo");
93        assert_eq!(canonicalize("/bleh/bar/../../foo"), "/foo");
94        assert_eq!(canonicalize("/bleh/bar/../../foo/.."), "/");
95        assert_eq!(canonicalize("/bleh/bar/../../foo/../meh"), "/meh");
96    }
97}