diff --git a/library/std/src/path.rs b/library/std/src/path.rs index ed0987064e8f7..8a2450fcb887c 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 //////////////////////////////////////////////////////////////////////////////// @@ -2156,9 +2175,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. @@ -2182,7 +2241,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 896d6c2a64c60..e8b960e6d7370 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -3,7 +3,7 @@ use super::*; use crate::rc::Rc; use crate::sync::Arc; -macro_rules! t( +macro_rules! t ( ($path:expr, iter: $iter:expr) => ( { let path = Path::new($path); @@ -73,15 +73,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); } ); ); @@ -116,7 +134,8 @@ pub fn test_decompositions_unix() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo", @@ -126,7 +145,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!("/", @@ -136,7 +156,8 @@ pub fn test_decompositions_unix() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/foo", @@ -146,7 +167,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/", @@ -156,7 +178,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/", @@ -166,7 +189,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", @@ -176,7 +200,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", @@ -186,7 +211,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///", @@ -196,7 +222,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", @@ -206,7 +233,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!("./.", @@ -216,7 +244,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/..", @@ -226,7 +255,8 @@ pub fn test_decompositions_unix() { parent: Some("/"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("../", @@ -236,7 +266,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/.", @@ -246,7 +277,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/..", @@ -256,7 +288,8 @@ pub fn test_decompositions_unix() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/./", @@ -266,7 +299,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", @@ -276,7 +310,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/../", @@ -286,7 +321,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", @@ -296,7 +332,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", @@ -306,7 +343,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!(".", @@ -316,7 +354,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("./", @@ -326,7 +365,8 @@ pub fn test_decompositions_unix() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("a/b", @@ -336,7 +376,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", @@ -346,7 +387,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", @@ -356,7 +398,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", @@ -366,7 +409,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", @@ -376,7 +420,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") ); } @@ -390,7 +468,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo", @@ -400,7 +479,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!("/", @@ -410,7 +490,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\", @@ -420,7 +501,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:", @@ -430,7 +512,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:\\", @@ -440,7 +523,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("c:/", @@ -450,7 +534,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/foo", @@ -460,7 +545,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/", @@ -470,7 +556,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/", @@ -480,7 +567,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", @@ -490,7 +578,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", @@ -500,7 +589,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///", @@ -510,7 +600,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", @@ -520,7 +611,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!("./.", @@ -530,7 +622,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("/..", @@ -540,7 +633,8 @@ pub fn test_decompositions_windows() { parent: Some("/"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("../", @@ -550,7 +644,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/.", @@ -560,7 +655,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/..", @@ -570,7 +666,8 @@ pub fn test_decompositions_windows() { parent: Some("foo"), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("foo/./", @@ -580,7 +677,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", @@ -590,7 +688,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/../", @@ -600,7 +699,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", @@ -610,7 +710,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", @@ -620,7 +721,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!(".", @@ -630,7 +732,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("./", @@ -640,7 +743,8 @@ pub fn test_decompositions_windows() { parent: Some(""), file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("a/b", @@ -650,7 +754,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", @@ -660,7 +765,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", @@ -670,7 +776,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", @@ -680,7 +787,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"], @@ -689,7 +798,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", @@ -699,7 +809,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", @@ -709,7 +820,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", @@ -719,7 +831,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", @@ -729,7 +842,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\server", @@ -739,7 +853,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", @@ -749,7 +864,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", @@ -759,7 +875,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\", @@ -769,7 +886,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", @@ -779,7 +897,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", @@ -789,7 +908,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\UNC\\", @@ -799,7 +919,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", @@ -809,7 +930,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:\\", @@ -819,7 +941,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:", @@ -829,7 +952,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\foo/bar", @@ -839,7 +963,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\C:/foo", @@ -849,7 +974,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\.\\foo\\bar", @@ -859,7 +985,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", @@ -869,7 +996,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\.\\foo/bar", @@ -879,7 +1007,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", @@ -889,7 +1018,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!("\\\\.\\", @@ -899,7 +1029,8 @@ pub fn test_decompositions_windows() { parent: None, file_name: None, file_stem: None, - extension: None + extension: None, + file_prefix: None ); t!("\\\\?\\a\\b\\", @@ -909,7 +1040,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") ); } @@ -949,12 +1125,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); @@ -1042,7 +1265,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(); @@ -1096,7 +1319,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); @@ -1130,7 +1353,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); @@ -1192,7 +1415,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) => ({