ts_path/
display.rs

1//! An opinionated way to display a path
2
3use alloc::borrow::Cow;
4use std::path::{Component, Path};
5
6use crate::NormalizePath;
7
8/// Extension trait to display a path.
9pub trait DisplayPath {
10    /// Opinionated display for a path    
11    fn opinionated_display(&self) -> String;
12}
13
14impl<P: AsRef<Path>> DisplayPath for P {
15    fn opinionated_display(&self) -> String {
16        display_path(self.as_ref())
17    }
18}
19
20/// Opinionated display for a path:
21/// * Normalises the path.
22/// * Prefixed paths use the `\` separator, all other paths use the `/` separator.
23pub fn display_path(path: &Path) -> String {
24    let path = path.normalized();
25
26    if path.as_path() == Path::new("") {
27        return ".".to_string();
28    }
29
30    let has_prefix = path
31        .components()
32        .next()
33        .is_some_and(|component| matches!(component, Component::Prefix(_)));
34
35    let separator = if has_prefix { r"\" } else { "/" };
36
37    path.components()
38        .filter_map(|component| {
39            if has_prefix && matches!(component, Component::RootDir) {
40                None
41            } else if matches!(component, Component::RootDir) {
42                Some(Cow::Borrowed(""))
43            } else {
44                Some(component.as_os_str().to_string_lossy())
45            }
46        })
47        .collect::<Vec<_>>()
48        .join(separator)
49}
50
51#[cfg(test)]
52mod test {
53    use std::path::Path;
54
55    use crate::display::DisplayPath;
56
57    #[test]
58    fn handles_relative() {
59        let expected = "some/relative/path/1";
60        let data = Path::new(r"some\relative\path\1");
61        assert_eq!(expected, data.opinionated_display());
62
63        let expected = "some/relative/path/2";
64        let data = Path::new(r".\some\relative\path\2");
65        assert_eq!(expected, data.opinionated_display());
66    }
67
68    #[test]
69    fn handles_windows_style() {
70        let expected = r"C:\some\absolute\path\1";
71        let data = Path::new(r"C:\some\absolute\path\1");
72        assert_eq!(expected, data.opinionated_display());
73
74        let expected = r"C:\some\absolute\path\2";
75        let data = Path::new(r"C:/some/absolute/path/2");
76        assert_eq!(expected, data.opinionated_display());
77    }
78
79    #[test]
80    fn handles_unix_style() {
81        let expected = r"/some/absolute/path/1";
82        let data = Path::new(r"/some/absolute/path/1");
83        assert_eq!(expected, data.opinionated_display());
84
85        let expected = r"/some/absolute/path/2";
86        let data = Path::new(r"\some\absolute\path\2");
87        assert_eq!(expected, data.opinionated_display());
88    }
89}