I have a function that accepts a string which will be used to create a file with that name (e.g. f("foo") will create a /some/fixed/path/foo.txt file). I'd like to prevent users from mistakenly passing strings with / separators that would introduce additional sub-directories. Since PathBuf::push() accepts strings with multiple components (and, confusingly, so does PathBuf::set_file_name()) it doesn't seem possible to prevent pushing multiple components onto a PathBuf without a separate check first.
Naively, I could do a .contains() check:
assert!(!name.contains("/"), "name should be a single path element");
But obviously that's not cross-platform. There is path::is_separator() so I could do:
name.chars().any(std::path::is_separator)
Alternatively I looked at Path for any sort of is_single_component() check or similar, I could check the file_name() equals the whole path:
let name = Path::new(name);
assert_eq!(Some(name.as_os_str()), name.file_name(),
"name should be a single path element");
or that iterating over the path yields one element:
assert_eq!(Path::new(name).iter().count(), 1,
"name should be a single path element");
I'm leaning towards this last approach, but I'm just curious if there's a more idiomatic way to ensure pushing a string onto a PathBuf will just add one path component.
If you are fine with limiting yourself to path names that are valid UTF-8, I suggest this succinct implementation:
fn has_single_component(path: &str) -> bool {
!path.contains(std::path::is_separator)
}
In contrast to your Path-based approaches, it will stop at the first separator found, and it's easy to read.
Note that testing whether a path only consists of a single component is a rather uncommon thing to do, so there isn't a standard way of doing it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With