use std::borrow::Cow;
fn is_absolute_windows_path(s: &str) -> bool {
if s.len() > 2 && &s[..2] == "\\\\" {
return true;
}
let mut char_iter = s.chars();
let (fc, sc, tc) = (char_iter.next(), char_iter.next(), char_iter.next());
match fc.unwrap_or_default() {
'A'..='Z' | 'a'..='z' => {
if sc == Some(':') && tc.map_or(false, |tc| tc == '\\' || tc == '/') {
return true;
}
}
_ => (),
}
false
}
fn is_absolute_unix_path(s: &str) -> bool {
s.starts_with('/')
}
pub fn join_path(base: &str, other: &str) -> String {
if base == "" || is_absolute_windows_path(other) || is_absolute_unix_path(other) {
return other.into();
}
if other == "" {
return base.into();
}
let win_abs = is_absolute_windows_path(base);
let unix_abs = is_absolute_unix_path(base);
let win_style = win_abs || (!unix_abs && base.contains('\\'));
if win_style {
format!(
"{}\\{}",
base.trim_end_matches(&['\\', '/'][..]),
other.trim_start_matches(&['\\', '/'][..])
)
} else {
format!(
"{}/{}",
base.trim_end_matches('/'),
other.trim_start_matches('/')
)
}
}
pub fn split_path(path: &str) -> (Option<&str>, &str) {
let path = path.trim_end_matches(&['\\', '/'][..]);
let split_char = if !path.starts_with('/') && path.contains('\\') {
'\\' } else {
'/' };
let mut iter = path.rsplitn(2, split_char);
let basename = iter.next().unwrap();
let basepath = iter.next();
(basepath, basename)
}
pub fn shorten_path(path: &str, length: usize) -> Cow<'_, str> {
if path.len() <= length {
return Cow::Borrowed(path);
} else if length <= 10 {
if length > 3 {
return Cow::Owned(format!("{}...", &path[..length - 3]));
}
return Cow::Borrowed(&path[..length]);
}
let mut rv = String::new();
let mut last_idx = 0;
let mut piece_iter = path.match_indices(&['\\', '/'][..]);
let mut final_sep = "/";
let max_len = length - 4;
while let Some((idx, sep)) = piece_iter.next() {
let slice = &path[last_idx..idx + sep.len()];
rv.push_str(slice);
let done = last_idx > 0;
last_idx = idx + sep.len();
final_sep = sep;
if done {
break;
}
}
let mut final_length = rv.len() as i64;
let mut rest = vec![];
let mut next_idx = path.len();
while let Some((idx, _)) = piece_iter.next_back() {
if idx <= last_idx {
break;
}
let slice = &path[idx + 1..next_idx];
if final_length + (slice.len() as i64) > max_len as i64 {
break;
}
rest.push(slice);
next_idx = idx + 1;
final_length += slice.len() as i64;
}
if rv.len() > max_len || rest.is_empty() {
let basename = path.rsplit(&['\\', '/'][..]).next().unwrap();
if basename.len() > max_len {
return Cow::Owned(format!("...{}", &basename[basename.len() - max_len + 1..]));
} else {
return Cow::Owned(format!("...{}{}", final_sep, basename));
}
}
rest.reverse();
rv.push_str("...");
rv.push_str(final_sep);
for item in rest {
rv.push_str(&item);
}
Cow::Owned(rv)
}
#[test]
fn test_join_path() {
assert_eq!(join_path("C:\\test", "other"), "C:\\test\\other");
assert_eq!(join_path("C:/test", "other"), "C:/test\\other");
assert_eq!(
join_path("C:\\test", "other\\stuff"),
"C:\\test\\other\\stuff"
);
assert_eq!(join_path("C:/test", "C:\\other"), "C:\\other");
assert_eq!(
join_path("foo\\bar\\baz", "blub\\blah"),
"foo\\bar\\baz\\blub\\blah"
);
assert_eq!(join_path("/usr/bin", "bash"), "/usr/bin/bash");
assert_eq!(join_path("/usr/local", "bin/bash"), "/usr/local/bin/bash");
assert_eq!(join_path("/usr/bin", "/usr/local/bin"), "/usr/local/bin");
assert_eq!(join_path("foo/bar/", "blah"), "foo/bar/blah");
}
#[test]
fn test_shorten_path() {
assert_eq!(&shorten_path("/foo/bar/baz/blah/blafasel", 6), "/fo...");
assert_eq!(&shorten_path("/foo/bar/baz/blah/blafasel", 2), "/f");
assert_eq!(
&shorten_path("/foo/bar/baz/blah/blafasel", 21),
"/foo/.../blafasel"
);
assert_eq!(
&shorten_path("/foo/bar/baz/blah/blafasel", 22),
"/foo/.../blah/blafasel"
);
assert_eq!(
&shorten_path("C:\\bar\\baz\\blah\\blafasel", 20),
"C:\\bar\\...\\blafasel"
);
assert_eq!(
&shorten_path("/foo/blar/baz/blah/blafasel", 27),
"/foo/blar/baz/blah/blafasel"
);
assert_eq!(
&shorten_path("/foo/blar/baz/blah/blafasel", 26),
"/foo/.../baz/blah/blafasel"
);
assert_eq!(
&shorten_path("/foo/b/baz/blah/blafasel", 23),
"/foo/.../blah/blafasel"
);
assert_eq!(&shorten_path("/foobarbaz/blahblah", 16), ".../blahblah");
assert_eq!(&shorten_path("/foobarbazblahblah", 12), "...lahblah");
}