src/plus.rs in faster_path-0.3.9 vs src/plus.rs in faster_path-0.3.10

- old
+ new

@@ -1,85 +1,82 @@ -extern crate array_tool; -use std::path::Path; +use std::borrow::Cow; use std::str; +use std::path::MAIN_SEPARATOR; + use chop_basename::chop_basename; -use basename::basename; -use dirname::dirname; -use self::array_tool::vec::Shift; -use std::ops::Index; +use path_parsing::SEP; -pub fn plus_paths(path1: &str, path2: &str) -> String { - let mut prefix2 = path2.to_string(); +pub fn plus_paths<'a>(path1: &'a str, path2: &str) -> Cow<'a, str> { + let mut prefix2 = path2; let mut index_list2: Vec<usize> = vec![]; - let mut basename_list2: Vec<String> = vec![]; - + let mut basename_list2: Vec<&str> = vec![]; loop { - match chop_basename(&prefix2.clone()[..]) { - None => { break }, + match chop_basename(prefix2) { + None => { break; } Some((pfx2, basename2)) => { - prefix2 = pfx2.to_string(); - index_list2.unshift(pfx2.len()); - basename_list2.unshift(basename2.to_owned()); - }, + prefix2 = pfx2; + index_list2.push(pfx2.len()); + basename_list2.push(basename2); + } } } if !prefix2.is_empty() { - return path2.to_string() + return path2.to_string().into(); }; - let mut prefix1 = path1.to_string(); - + let result_prefix: Cow<str>; + let mut prefix1 = path1; loop { - while !basename_list2.is_empty() && basename_list2.first().unwrap() == "." { - index_list2.shift(); - basename_list2.shift(); - } - match chop_basename(&prefix1.clone()[..]) { - None => { break }, + let mut new_len = basename_list2.len() - count_trailing(".", &basename_list2); + index_list2.truncate(new_len); + basename_list2.truncate(new_len); + match chop_basename(prefix1) { + None => { + result_prefix = prefix1.into(); + break; + } Some((pfx1, basename1)) => { - prefix1 = pfx1.to_string(); - if basename1 == "." { continue }; - if basename1 == ".." || basename_list2.is_empty() || basename_list2.first().unwrap() != ".." { - prefix1.push_str(&basename1); - break - } + prefix1 = pfx1; + if basename1 == "." { continue; }; + if basename1 == ".." || basename_list2.last() != Some(&"..") { + result_prefix = [prefix1, basename1].concat().into(); + break; } + } } - index_list2.shift(); - basename_list2.shift(); + if new_len > 0 { + new_len -= 1; + index_list2.truncate(new_len); + basename_list2.truncate(new_len); + } } - let result: String; - - let mut r1 = if let Some((_,_)) = chop_basename(&prefix1[..]) {true} else {false}; - - if !r1 { - r1 = basename(&prefix1[..], "").contains("/"); - if r1 { - while !basename_list2.is_empty() && basename_list2.first().unwrap() == ".." { - index_list2.shift(); - basename_list2.shift(); - } - } + if !result_prefix.is_empty() && result_prefix.as_bytes().iter().cloned().all(|b| b == SEP) { + let new_len = basename_list2.len() - count_trailing("..", &basename_list2); + index_list2.truncate(new_len); + basename_list2.truncate(new_len); } - if !basename_list2.is_empty() { - let suffix2 = path2.index(index_list2.first().unwrap().to_owned()..); - if r1 { - result = Path::new(&prefix1).join(Path::new(&suffix2)).to_str().unwrap().to_string(); - } else { - prefix1.push_str(&suffix2); - result = prefix1.to_string(); + if let Some(last_index2) = index_list2.last() { + let suffix = &path2[*last_index2..]; + match (result_prefix.as_bytes().last(), suffix.as_bytes().first()) { + (Some(&SEP), Some(&SEP)) => [&result_prefix, &suffix[1..]].concat().into(), + (Some(&SEP), Some(_)) | (Some(_), Some(&SEP)) => [&result_prefix, suffix].concat().into(), + (None, Some(_)) => suffix.to_string().into(), + _ => format!("{}{}{}", result_prefix.as_ref(), MAIN_SEPARATOR, suffix).into(), } } else { - if r1 { - result = prefix1.to_string(); + if result_prefix.is_empty() { + ".".into() } else { - result = dirname(&prefix1[..]).to_string(); + result_prefix } } +} - String::from(result) +#[inline(always)] +fn count_trailing(x: &str, xs: &Vec<&str>) -> usize { + xs.iter().rev().take_while(|&c| c == &x).count() } #[test] fn it_will_plus_same_as_ruby() { assert_eq!("/" , plus_paths("/" , "/")); @@ -88,9 +85,10 @@ assert_eq!("b" , plus_paths("." , "b")); assert_eq!("." , plus_paths("." , ".")); assert_eq!("/b" , plus_paths("a" , "/b")); assert_eq!("/" , plus_paths("/" , "..")); + assert_eq!("////" , plus_paths("////", "")); assert_eq!("." , plus_paths("a" , "..")); assert_eq!("a" , plus_paths("a/b", "..")); assert_eq!("../.." , plus_paths(".." , "..")); assert_eq!("/c" , plus_paths("/" , "../c")); assert_eq!("c" , plus_paths("a" , "../c"));