diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 654049ba458f2..2a9c361c18afc 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -315,7 +315,7 @@ fn has_physical_root(s: &[u8], prefix: Option>) -> bool { } // basic workhorse for splitting stem and extension -fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { +fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { if os_str_as_u8_slice(file) == b".." { return (Some(file), None); } @@ -334,6 +334,25 @@ fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { } } +fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { + let slice = os_str_as_u8_slice(file); + if slice == b".." { + return (file, None); + } + + // The unsafety here stems from converting between &OsStr and &[u8] + // and back. This is safe to do because (1) we only look at ASCII + // contents of the encoding and (2) new &OsStr values are produced + // only from ASCII-bounded slices of existing &OsStr values. + let i = match slice[1..].iter().position(|b| *b == b'.') { + Some(i) => i + 1, + None => return (file, None), + }; + let before = &slice[..i]; + let after = &slice[i + 1..]; + unsafe { (u8_slice_as_os_str(before), Some(u8_slice_as_os_str(after))) } +} + //////////////////////////////////////////////////////////////////////////////// // The core iterators //////////////////////////////////////////////////////////////////////////////// @@ -2213,9 +2232,49 @@ impl Path { /// assert_eq!("foo", Path::new("foo.rs").file_stem().unwrap()); /// assert_eq!("foo.tar", Path::new("foo.tar.gz").file_stem().unwrap()); /// ``` + /// + /// # See Also + /// This method is similar to [`Path::file_prefix`], which extracts the portion of the file name + /// before the *first* `.` + /// + /// [`Path::file_prefix`]: Path::file_prefix + /// #[stable(feature = "rust1", since = "1.0.0")] pub fn file_stem(&self) -> Option<&OsStr> { - self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.or(after)) + self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.or(after)) + } + + /// Extracts the prefix of [`self.file_name`]. + /// + /// The prefix is: + /// + /// * [`None`], if there is no file name; + /// * The entire file name if there is no embedded `.`; + /// * The portion of the file name before the first non-beginning `.`; + /// * The entire file name if the file name begins with `.` and has no other `.`s within; + /// * The portion of the file name before the second `.` if the file name begins with `.` + /// + /// [`self.file_name`]: Path::file_name + /// + /// # Examples + /// + /// ``` + /// # #![feature(path_file_prefix)] + /// use std::path::Path; + /// + /// assert_eq!("foo", Path::new("foo.rs").file_prefix().unwrap()); + /// assert_eq!("foo", Path::new("foo.tar.gz").file_prefix().unwrap()); + /// ``` + /// + /// # See Also + /// This method is similar to [`Path::file_stem`], which extracts the portion of the file name + /// before the *last* `.` + /// + /// [`Path::file_stem`]: Path::file_stem + /// + #[unstable(feature = "path_file_prefix", issue = "86319")] + pub fn file_prefix(&self) -> Option<&OsStr> { + self.file_name().map(split_file_at_dot).and_then(|(before, _after)| Some(before)) } /// Extracts the extension of [`self.file_name`], if possible. @@ -2239,7 +2298,7 @@ impl Path { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn extension(&self) -> Option<&OsStr> { - self.file_name().map(split_file_at_dot).and_then(|(before, after)| before.and(after)) + self.file_name().map(rsplit_file_at_dot).and_then(|(before, after)| before.and(after)) } /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index c10ded08f2ce4..ce23cf6cd21b9 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -5,7 +5,7 @@ use crate::rc::Rc; use crate::sync::Arc; use core::hint::black_box; -macro_rules! t( +macro_rules! t ( ($path:expr, iter: $iter:expr) => ( { let path = Path::new($path); @@ -75,15 +75,33 @@ macro_rules! t( } ); + ($path:expr, file_prefix: $file_prefix:expr, extension: $extension:expr) => ( + { + let path = Path::new($path); + + let prefix = path.file_prefix().map(|p| p.to_str().unwrap()); + let exp_prefix: Option<&str> = $file_prefix; + assert!(prefix == exp_prefix, "file_prefix: Expected {:?}, found {:?}", + exp_prefix, prefix); + + let ext = path.extension().map(|p| p.to_str().unwrap()); + let exp_ext: Option<&str> = $extension; + assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", + exp_ext, ext); + } + ); + ($path:expr, iter: $iter:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr, parent: $parent:expr, file_name: $file:expr, - file_stem: $file_stem:expr, extension: $extension:expr) => ( + file_stem: $file_stem:expr, extension: $extension:expr, + file_prefix: $file_prefix:expr) => ( { t!($path, iter: $iter); t!($path, has_root: $has_root, is_absolute: $is_absolute); t!($path, parent: $parent, file_name: $file); t!($path, file_stem: $file_stem, extension: $extension); + t!($path, file_prefix: $file_prefix, extension: $extension); } ); ); @@ -118,7 +136,8 @@ pub fn test_decompositions_unix() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo", @@ -128,7 +147,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/", @@ -138,7 +158,8 @@ pub fn test_decompositions_unix() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/foo", @@ -148,7 +169,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/", @@ -158,7 +180,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/foo/", @@ -168,7 +191,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/bar", @@ -178,7 +202,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("/foo/bar", @@ -188,7 +213,8 @@ pub fn test_decompositions_unix() { parent: Some("/foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("///foo///", @@ -198,7 +224,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("///foo///bar", @@ -208,7 +235,8 @@ pub fn test_decompositions_unix() { parent: Some("///foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./.", @@ -218,7 +246,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/..", @@ -228,7 +257,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("../", @@ -238,7 +268,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/.", @@ -248,7 +279,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/..", @@ -258,7 +290,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/./", @@ -268,7 +301,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/./bar", @@ -278,7 +312,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("foo/../", @@ -288,7 +323,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/../bar", @@ -298,7 +334,8 @@ pub fn test_decompositions_unix() { parent: Some("foo/.."), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./a", @@ -308,7 +345,8 @@ pub fn test_decompositions_unix() { parent: Some("."), file_name: Some("a"), file_stem: Some("a"), - extension: None + extension: None, + file_prefix: Some("a") ); t!(".", @@ -318,7 +356,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("./", @@ -328,7 +367,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("a/b", @@ -338,7 +378,8 @@ pub fn test_decompositions_unix() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a//b", @@ -348,7 +389,8 @@ pub fn test_decompositions_unix() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/./b", @@ -358,7 +400,8 @@ pub fn test_decompositions_unix() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/b/c", @@ -368,7 +411,8 @@ pub fn test_decompositions_unix() { parent: Some("a/b"), file_name: Some("c"), file_stem: Some("c"), - extension: None + extension: None, + file_prefix: Some("c") ); t!(".foo", @@ -378,7 +422,41 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: Some(".foo"), file_stem: Some(".foo"), - extension: None + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.foo", + iter: ["a", ".foo"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.rustfmt.toml", + iter: ["a", ".rustfmt.toml"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".rustfmt.toml"), + file_stem: Some(".rustfmt"), + extension: Some("toml"), + file_prefix: Some(".rustfmt") + ); + + t!("a/.x.y.z", + iter: ["a", ".x.y.z"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".x.y.z"), + file_stem: Some(".x.y"), + extension: Some("z"), + file_prefix: Some(".x") ); } @@ -392,7 +470,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo", @@ -402,7 +481,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/", @@ -412,7 +492,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\", @@ -422,7 +503,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:", @@ -432,7 +514,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:\\", @@ -442,7 +525,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:/", @@ -452,7 +536,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/foo", @@ -462,7 +547,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/", @@ -472,7 +558,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("/foo/", @@ -482,7 +569,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/bar", @@ -492,7 +580,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("/foo/bar", @@ -502,7 +591,8 @@ pub fn test_decompositions_windows() { parent: Some("/foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("///foo///", @@ -512,7 +602,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("///foo///bar", @@ -522,7 +613,8 @@ pub fn test_decompositions_windows() { parent: Some("///foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./.", @@ -532,7 +624,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/..", @@ -542,7 +635,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("../", @@ -552,7 +646,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/.", @@ -562,7 +657,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/..", @@ -572,7 +668,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/./", @@ -582,7 +679,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: Some("foo"), file_stem: Some("foo"), - extension: None + extension: None, + file_prefix: Some("foo") ); t!("foo/./bar", @@ -592,7 +690,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("foo/../", @@ -602,7 +701,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/../bar", @@ -612,7 +712,8 @@ pub fn test_decompositions_windows() { parent: Some("foo/.."), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("./a", @@ -622,7 +723,8 @@ pub fn test_decompositions_windows() { parent: Some("."), file_name: Some("a"), file_stem: Some("a"), - extension: None + extension: None, + file_prefix: Some("a") ); t!(".", @@ -632,7 +734,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("./", @@ -642,7 +745,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("a/b", @@ -652,7 +756,8 @@ pub fn test_decompositions_windows() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a//b", @@ -662,7 +767,8 @@ pub fn test_decompositions_windows() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/./b", @@ -672,7 +778,8 @@ pub fn test_decompositions_windows() { parent: Some("a"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") ); t!("a/b/c", @@ -682,7 +789,9 @@ pub fn test_decompositions_windows() { parent: Some("a/b"), file_name: Some("c"), file_stem: Some("c"), - extension: None); + extension: None, + file_prefix: Some("c") + ); t!("a\\b\\c", iter: ["a", "b", "c"], @@ -691,7 +800,8 @@ pub fn test_decompositions_windows() { parent: Some("a\\b"), file_name: Some("c"), file_stem: Some("c"), - extension: None + extension: None, + file_prefix: Some("c") ); t!("\\a", @@ -701,7 +811,8 @@ pub fn test_decompositions_windows() { parent: Some("\\"), file_name: Some("a"), file_stem: Some("a"), - extension: None + extension: None, + file_prefix: Some("a") ); t!("c:\\foo.txt", @@ -711,7 +822,8 @@ pub fn test_decompositions_windows() { parent: Some("c:\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\server\\share\\foo.txt", @@ -721,7 +833,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\server\\share\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\server\\share", @@ -731,7 +844,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\server", @@ -741,7 +855,8 @@ pub fn test_decompositions_windows() { parent: Some("\\"), file_name: Some("server"), file_stem: Some("server"), - extension: None + extension: None, + file_prefix: Some("server") ); t!("\\\\?\\bar\\foo.txt", @@ -751,7 +866,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\bar\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\?\\bar", @@ -761,7 +877,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\", @@ -771,7 +888,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\UNC\\server\\share\\foo.txt", @@ -781,7 +899,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\UNC\\server\\share\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\?\\UNC\\server", @@ -791,7 +910,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\UNC\\", @@ -801,7 +921,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:\\foo.txt", @@ -811,7 +932,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\C:\\"), file_name: Some("foo.txt"), file_stem: Some("foo"), - extension: Some("txt") + extension: Some("txt"), + file_prefix: Some("foo") ); t!("\\\\?\\C:\\", @@ -821,7 +943,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:", @@ -831,7 +954,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\foo/bar", @@ -841,7 +965,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:/foo", @@ -851,7 +976,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\.\\foo\\bar", @@ -861,7 +987,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\.\\foo\\"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("\\\\.\\foo", @@ -871,7 +998,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\.\\foo/bar", @@ -881,7 +1009,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\.\\foo/"), file_name: Some("bar"), file_stem: Some("bar"), - extension: None + extension: None, + file_prefix: Some("bar") ); t!("\\\\.\\foo\\bar/baz", @@ -891,7 +1020,8 @@ pub fn test_decompositions_windows() { parent: Some("\\\\.\\foo\\bar"), file_name: Some("baz"), file_stem: Some("baz"), - extension: None + extension: None, + file_prefix: Some("baz") ); t!("\\\\.\\", @@ -901,7 +1031,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\a\\b\\", @@ -911,7 +1042,52 @@ pub fn test_decompositions_windows() { parent: Some("\\\\?\\a\\"), file_name: Some("b"), file_stem: Some("b"), - extension: None + extension: None, + file_prefix: Some("b") + ); + + t!("\\\\?\\C:\\foo.txt.zip", + iter: ["\\\\?\\C:", "\\", "foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some("foo.txt.zip"), + file_stem: Some("foo.txt"), + extension: Some("zip"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\C:\\.foo.txt.zip", + iter: ["\\\\?\\C:", "\\", ".foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo.txt.zip"), + file_stem: Some(".foo.txt"), + extension: Some("zip"), + file_prefix: Some(".foo") + ); + + t!("\\\\?\\C:\\.foo", + iter: ["\\\\?\\C:", "\\", ".foo"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.x.y.z", + iter: ["a", ".x.y.z"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".x.y.z"), + file_stem: Some(".x.y"), + extension: Some("z"), + file_prefix: Some(".x") ); } @@ -951,12 +1127,59 @@ pub fn test_stem_ext() { t!("..", file_stem: None, extension: None); + t!(".x.y.z", file_stem: Some(".x.y"), extension: Some("z")); + + t!("..x.y.z", file_stem: Some("..x.y"), extension: Some("z")); + t!("", file_stem: None, extension: None); } +#[test] +pub fn test_prefix_ext() { + t!("foo", + file_prefix: Some("foo"), + extension: None + ); + + t!("foo.", + file_prefix: Some("foo"), + extension: Some("") + ); + + t!(".foo", + file_prefix: Some(".foo"), + extension: None + ); + + t!("foo.txt", + file_prefix: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.txt", + file_prefix: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.", + file_prefix: Some("foo"), + extension: Some("") + ); + + t!(".", file_prefix: None, extension: None); + + t!("..", file_prefix: None, extension: None); + + t!(".x.y.z", file_prefix: Some(".x"), extension: Some("z")); + + t!("..x.y.z", file_prefix: Some("."), extension: Some("z")); + + t!("", file_prefix: None, extension: None); +} + #[test] pub fn test_push() { - macro_rules! tp( + macro_rules! tp ( ($path:expr, $push:expr, $expected:expr) => ( { let mut actual = PathBuf::from($path); actual.push($push); @@ -1044,7 +1267,7 @@ pub fn test_push() { #[test] pub fn test_pop() { - macro_rules! tp( + macro_rules! tp ( ($path:expr, $expected:expr, $output:expr) => ( { let mut actual = PathBuf::from($path); let output = actual.pop(); @@ -1098,7 +1321,7 @@ pub fn test_pop() { #[test] pub fn test_set_file_name() { - macro_rules! tfn( + macro_rules! tfn ( ($path:expr, $file:expr, $expected:expr) => ( { let mut p = PathBuf::from($path); p.set_file_name($file); @@ -1132,7 +1355,7 @@ pub fn test_set_file_name() { #[test] pub fn test_set_extension() { - macro_rules! tfe( + macro_rules! tfe ( ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( { let mut p = PathBuf::from($path); let output = p.set_extension($ext); @@ -1194,7 +1417,7 @@ pub fn test_compare() { s.finish() } - macro_rules! tc( + macro_rules! tc ( ($path1:expr, $path2:expr, eq: $eq:expr, starts_with: $starts_with:expr, ends_with: $ends_with:expr, relative_from: $relative_from:expr) => ({