diff options
Diffstat (limited to 'vendor/globset/src/pathutil.rs')
-rw-r--r-- | vendor/globset/src/pathutil.rs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/vendor/globset/src/pathutil.rs b/vendor/globset/src/pathutil.rs new file mode 100644 index 000000000..2bd34e1dd --- /dev/null +++ b/vendor/globset/src/pathutil.rs @@ -0,0 +1,129 @@ +use std::borrow::Cow; + +use bstr::{ByteSlice, ByteVec}; + +/// The final component of the path, if it is a normal file. +/// +/// If the path terminates in ., .., or consists solely of a root of prefix, +/// file_name will return None. +pub fn file_name<'a>(path: &Cow<'a, [u8]>) -> Option<Cow<'a, [u8]>> { + if path.is_empty() { + return None; + } else if path.last_byte() == Some(b'.') { + return None; + } + let last_slash = path.rfind_byte(b'/').map(|i| i + 1).unwrap_or(0); + Some(match *path { + Cow::Borrowed(path) => Cow::Borrowed(&path[last_slash..]), + Cow::Owned(ref path) => { + let mut path = path.clone(); + path.drain_bytes(..last_slash); + Cow::Owned(path) + } + }) +} + +/// Return a file extension given a path's file name. +/// +/// Note that this does NOT match the semantics of std::path::Path::extension. +/// Namely, the extension includes the `.` and matching is otherwise more +/// liberal. Specifically, the extenion is: +/// +/// * None, if the file name given is empty; +/// * None, if there is no embedded `.`; +/// * Otherwise, the portion of the file name starting with the final `.`. +/// +/// e.g., A file name of `.rs` has an extension `.rs`. +/// +/// N.B. This is done to make certain glob match optimizations easier. Namely, +/// a pattern like `*.rs` is obviously trying to match files with a `rs` +/// extension, but it also matches files like `.rs`, which doesn't have an +/// extension according to std::path::Path::extension. +pub fn file_name_ext<'a>(name: &Cow<'a, [u8]>) -> Option<Cow<'a, [u8]>> { + if name.is_empty() { + return None; + } + let last_dot_at = match name.rfind_byte(b'.') { + None => return None, + Some(i) => i, + }; + Some(match *name { + Cow::Borrowed(name) => Cow::Borrowed(&name[last_dot_at..]), + Cow::Owned(ref name) => { + let mut name = name.clone(); + name.drain_bytes(..last_dot_at); + Cow::Owned(name) + } + }) +} + +/// Normalizes a path to use `/` as a separator everywhere, even on platforms +/// that recognize other characters as separators. +#[cfg(unix)] +pub fn normalize_path(path: Cow<'_, [u8]>) -> Cow<'_, [u8]> { + // UNIX only uses /, so we're good. + path +} + +/// Normalizes a path to use `/` as a separator everywhere, even on platforms +/// that recognize other characters as separators. +#[cfg(not(unix))] +pub fn normalize_path(mut path: Cow<[u8]>) -> Cow<[u8]> { + use std::path::is_separator; + + for i in 0..path.len() { + if path[i] == b'/' || !is_separator(path[i] as char) { + continue; + } + path.to_mut()[i] = b'/'; + } + path +} + +#[cfg(test)] +mod tests { + use std::borrow::Cow; + + use bstr::{ByteVec, B}; + + use super::{file_name_ext, normalize_path}; + + macro_rules! ext { + ($name:ident, $file_name:expr, $ext:expr) => { + #[test] + fn $name() { + let bs = Vec::from($file_name); + let got = file_name_ext(&Cow::Owned(bs)); + assert_eq!($ext.map(|s| Cow::Borrowed(B(s))), got); + } + }; + } + + ext!(ext1, "foo.rs", Some(".rs")); + ext!(ext2, ".rs", Some(".rs")); + ext!(ext3, "..rs", Some(".rs")); + ext!(ext4, "", None::<&str>); + ext!(ext5, "foo", None::<&str>); + + macro_rules! normalize { + ($name:ident, $path:expr, $expected:expr) => { + #[test] + fn $name() { + let bs = Vec::from_slice($path); + let got = normalize_path(Cow::Owned(bs)); + assert_eq!($expected.to_vec(), got.into_owned()); + } + }; + } + + normalize!(normal1, b"foo", b"foo"); + normalize!(normal2, b"foo/bar", b"foo/bar"); + #[cfg(unix)] + normalize!(normal3, b"foo\\bar", b"foo\\bar"); + #[cfg(not(unix))] + normalize!(normal3, b"foo\\bar", b"foo/bar"); + #[cfg(unix)] + normalize!(normal4, b"foo\\bar/baz", b"foo\\bar/baz"); + #[cfg(not(unix))] + normalize!(normal4, b"foo\\bar/baz", b"foo/bar/baz"); +} |