nu_path/
trailing_slash.rs1use std::{
2 borrow::Cow,
3 path::{Path, PathBuf},
4};
5
6pub fn strip_trailing_slash(path: &Path) -> Cow<'_, Path> {
9 if has_trailing_slash(path) {
10 let mut out = PathBuf::with_capacity(path.as_os_str().len());
13 out.extend(path.components());
14 Cow::Owned(out)
15 } else {
16 Cow::Borrowed(path)
18 }
19}
20
21#[cfg(windows)]
23pub fn has_trailing_slash(path: &Path) -> bool {
24 use std::os::windows::ffi::OsStrExt;
25
26 let last = path.as_os_str().encode_wide().last();
27 last == Some(b'\\' as u16) || last == Some(b'/' as u16)
28}
29
30#[cfg(unix)]
32pub fn has_trailing_slash(path: &Path) -> bool {
33 use std::os::unix::ffi::OsStrExt;
34
35 let last = path.as_os_str().as_bytes().last();
36 last == Some(&b'/')
37}
38
39#[cfg(target_arch = "wasm32")]
41pub fn has_trailing_slash(path: &Path) -> bool {
42 path.to_str().is_some_and(|s| s.ends_with('/'))
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[cfg_attr(not(unix), ignore = "only for Unix")]
51 #[test]
52 fn strip_root_unix() {
53 assert_eq!(Path::new("/"), strip_trailing_slash(Path::new("/")));
54 }
55
56 #[cfg_attr(not(unix), ignore = "only for Unix")]
57 #[test]
58 fn strip_non_trailing_unix() {
59 assert_eq!(
60 Path::new("/foo/bar"),
61 strip_trailing_slash(Path::new("/foo/bar"))
62 );
63 }
64
65 #[cfg_attr(not(unix), ignore = "only for Unix")]
66 #[test]
67 fn strip_trailing_unix() {
68 assert_eq!(
69 Path::new("/foo/bar"),
70 strip_trailing_slash(Path::new("/foo/bar/"))
71 );
72 }
73
74 #[cfg_attr(not(windows), ignore = "only for Windows")]
75 #[test]
76 fn strip_root_windows() {
77 assert_eq!(Path::new(r"C:\"), strip_trailing_slash(Path::new(r"C:\")));
78 }
79
80 #[cfg_attr(not(windows), ignore = "only for Windows")]
81 #[test]
82 fn strip_non_trailing_windows() {
83 assert_eq!(
84 Path::new(r"C:\foo\bar"),
85 strip_trailing_slash(Path::new(r"C:\foo\bar"))
86 );
87 }
88
89 #[cfg_attr(not(windows), ignore = "only for Windows")]
90 #[test]
91 fn strip_non_trailing_windows_unc() {
92 assert_eq!(
93 Path::new(r"\\foo\bar"),
94 strip_trailing_slash(Path::new(r"\\foo\bar"))
95 );
96 }
97
98 #[cfg_attr(not(windows), ignore = "only for Windows")]
99 #[test]
100 fn strip_trailing_windows() {
101 assert_eq!(
102 Path::new(r"C:\foo\bar"),
103 strip_trailing_slash(Path::new(r"C:\foo\bar\"))
104 );
105 }
106
107 #[cfg_attr(not(windows), ignore = "only for Windows")]
108 #[test]
109 fn strip_trailing_windows_unc() {
110 assert_eq!(
111 Path::new(r"\\foo\bar"),
112 strip_trailing_slash(Path::new(r"\\foo\bar\"))
113 );
114 }
115
116 #[cfg_attr(not(windows), ignore = "only for Windows")]
117 #[test]
118 fn strip_trailing_windows_device() {
119 assert_eq!(
120 Path::new(r"\\.\foo"),
121 strip_trailing_slash(Path::new(r"\\.\foo\"))
122 );
123 }
124}