How would you efficiently detect a trailing '/' in a PathBuf or in a AsRef<Path> ?(i.e. the difference between PathBuf::from("/home") and PathBuf::from("/home/") ?
And how would you add a trailing '/' to a PathBuf not having one ?
In both cases I see no other way than using to_str.
Unfortunately I don't think there's a good way. using PathBuf or Path. This is (hopefully) going to be improved in the future but for now here are some ideas I came up with:
#[cfg(windows)]
use std::os::windows::ffi::OsStrExt;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
#[cfg(windows)]
fn has_trailing_slash(p: &Path) -> bool {
let last = p.as_os_str().encode_wide().last();
last == Some(b'\\' as u16) || last == Some(b'/' as u16)
}
#[cfg(unix)]
fn has_trailing_slash(p: &Path) -> bool {
p.as_os_str().as_bytes().last() == Some(&b'/')
}
fn add_trailing_slash(p: &mut PathBuf) {
let fname = p.file_name();
let dirname = if let Some(fname) = fname {
let mut s = OsString::with_capacity(fname.len() + 1);
s.push(fname);
if cfg!(windows) {
s.push("\\");
} else {
s.push("/");
}
s
} else {
OsString::new()
};
if p.pop() {
p.push(dirname);
}
}
While there probably is some unsafe and incorrect way to peek at the last byte of the contained OsStr, that solution not going to be portable anyway. I suspect your challenge is bigger than just I get PathBufs and have to efficiently add a slash if there isn't already one. Note internally that might not even be a slash, can be any valid path separator on that platform, or some other way of encoding it. If you share a bigger picture, I'd be glad to give suggestions.
let mut pb = PathBuf::from("/home");
if !pb.as_os_str().is_empty() {
let mut name = pb.file_name().map(ToOwned::to_owned).unwrap_or_default();
name.push(std::path::MAIN_SEPARATOR.to_string());
pb.set_file_name(name);
}
let s = pb.as_os_str().to_string_lossy();
assert!(s.ends_with(&std::path::MAIN_SEPARATOR.to_string()));
They are different by practice. For example in Linux, if the path points to a symlink targetting another directory, the / at the end signals to follow the symlink.
Yes, ends_with can't work because /, when not at the end, isn't a Component.
As for why make the difference, there are several reasons. My precise use case is related to auto-completion (a/b can be completed in a/ba and a/b/ while a/b/ should be completed with the childs of b). It's really possible to only deal with strings, that's what I do, but in a complex application this means keeping a lot of strings just for that while paths are supposed to wrap them and shouldn't lose information.
That does indeed work. Using push with an empty string is not at all obvious or documented. But the behaviour does make some sense once you realise it's implemented in terms of components and not strings.