use std::path::Path;
use crate::utils::path::util::normalize_separators;
pub fn shorten_path(path: impl AsRef<Path>, max_width: usize) -> String {
let path = path.as_ref();
let full = normalize_separators(&path.display().to_string());
if full.len() <= max_width {
return full;
}
let components: Vec<&str> = full.split('/').filter(|s| !s.is_empty()).collect();
if components.is_empty() {
return full;
}
let filename = match components.last() {
Some(f) => f,
None => return full,
};
if filename.len() + 4 > max_width {
let max_bytes = max_width.saturating_sub(3);
let mut truncate_pos = 0;
for (byte_start, ch) in filename.char_indices() {
let byte_end = byte_start + ch.len_utf8();
if byte_end > max_bytes {
break;
}
truncate_pos = byte_end;
}
return format!("...{}", &filename[..truncate_pos]);
}
let mut result = String::new();
let mut included = 0;
for (i, component) in components.iter().rev().enumerate() {
let needed = if i == 0 {
component.len()
} else {
component.len() + 1 };
if result.len() + needed + 4 <= max_width {
if i > 0 {
result = format!("/{}{}", component, result);
} else {
result = component.to_string();
}
included += 1;
} else {
break;
}
}
if included < components.len() {
if result.starts_with('/') {
format!("...{}", result)
} else {
format!(".../{}", result)
}
} else {
format!("/{}", result)
}
}
pub fn abbreviate_path(path: impl AsRef<Path>) -> String {
abbreviate_path_keep(path, 2)
}
pub fn abbreviate_path_keep(path: impl AsRef<Path>, keep_last: usize) -> String {
let path = path.as_ref();
let path_str = path.display().to_string();
let starts_with_slash = path_str.starts_with('/');
let components: Vec<&str> = path_str.split('/').filter(|s| !s.is_empty()).collect();
if components.len() <= keep_last {
return path_str;
}
let abbrev_count = components.len() - keep_last;
let mut result = String::new();
if starts_with_slash {
result.push('/');
}
for (i, component) in components.iter().enumerate() {
if i > 0 {
result.push('/');
}
if i < abbrev_count {
if let Some(first) = component.chars().next() {
result.push(first);
}
} else {
result.push_str(component);
}
}
result
}
pub fn relative_to(path: impl AsRef<Path>, base: impl AsRef<Path>) -> String {
let path = path.as_ref();
let base = base.as_ref();
if let Ok(rel) = path.strip_prefix(base) {
rel.display().to_string()
} else {
path.display().to_string()
}
}