src/basename.rs in faster_path-0.3.8 vs src/basename.rs in faster_path-0.3.9

- old
+ new

@@ -1,9 +1,11 @@ extern crate memchr; -use path_parsing::{find_last_sep_pos, find_last_non_sep_pos, find_last_dot_pos}; +use self::memchr::memrchr; +use path_parsing::{find_last_sep_pos, find_last_non_sep_pos}; + pub fn basename<'a>(path: &'a str, ext: &str) -> &'a str { let bytes: &[u8] = path.as_bytes(); let mut left: usize = 0; let mut right: usize = bytes.len(); if let Some(last_slash_pos) = find_last_sep_pos(bytes) { @@ -18,18 +20,90 @@ } } else { left = last_slash_pos + 1; } } + &path[left..left + ext_end(&bytes[left..right], ext)] +} + +fn ext_end(slice: &[u8], ext: &str) -> usize { + if ext.len() >= slice.len() || slice == b"." || slice == b".." { + return slice.len(); + } let ext_bytes = ext.as_bytes(); - if ext_bytes == b".*" { - if let Some(dot_pos) = find_last_dot_pos(&bytes[left..right]) { - right = left + dot_pos; - } - } else if bytes[left..right].ends_with(ext_bytes) { - right -= ext_bytes.len(); + if ext_bytes.len() == 2 && *ext_bytes.get(1).unwrap() == b'*' { + match memrchr(*ext_bytes.get(0).unwrap(), slice) { + Some(end) if end != 0 => return end, + _ => {} + }; + } else if slice.ends_with(ext_bytes) { + return slice.len() - ext_bytes.len(); } - &path[left..right] + slice.len() +} + +#[test] +fn non_dot_asterisk_ext() { + // This is undocumented Ruby functionality. We match it in case some code out there relies on it. + assert_eq!(basename("abc", "b*"), "a"); + assert_eq!(basename("abc", "abc"), "abc"); + assert_eq!(basename("abc", "a*"), "abc"); + assert_eq!(basename("playlist", "l*"), "play"); + // Treated as literal "*": + assert_eq!(basename("playlist", "yl*"), "playlist"); + assert_eq!(basename("playl*", "yl*"), "pla"); +} + +#[test] +fn empty() { + assert_eq!(basename("", ""), ""); + assert_eq!(basename("", ".*"), ""); + assert_eq!(basename("", ".a"), ""); +} + +#[test] +fn sep() { + assert_eq!(basename("/", ""), "/"); + assert_eq!(basename("//", ""), "/"); +} + +#[test] +fn trailing_dot() { + assert_eq!(basename("file.test.", ""), "file.test."); + assert_eq!(basename("file.test.", "."), "file.test"); + assert_eq!(basename("file.test.", ".*"), "file.test"); +} + +#[test] +fn trailing_dot_dot() { + assert_eq!(basename("a..", ".."), "a"); + assert_eq!(basename("a..", ".*"), "a."); +} + +#[test] +fn dot() { + assert_eq!(basename(".", ""), "."); + assert_eq!(basename(".", "."), "."); + assert_eq!(basename(".", ".*"), "."); +} + +#[test] +fn dot_dot() { + assert_eq!(basename("..", ""), ".."); + assert_eq!(basename("..", ".*"), ".."); + assert_eq!(basename("..", ".."), ".."); + assert_eq!(basename("..", "..."), ".."); +} + +#[test] +fn non_dot_ext() { + assert_eq!(basename("abc", "bc"), "a"); +} + +#[test] +fn basename_eq_ext() { + assert_eq!(basename(".x", ".x"), ".x"); + assert_eq!(basename(".x", ".*"), ".x"); } #[test] fn absolute() { assert_eq!(basename("/a/b///c", ""), "c");