pub fn truncate_str(s: &str, max_bytes: usize) -> &str {
if s.len() <= max_bytes {
return s;
}
let mut end = max_bytes;
while end > 0 && !s.is_char_boundary(end) {
end -= 1;
}
&s[..end]
}
pub fn looks_like_file_path(s: &str) -> bool {
if !s.starts_with('/') {
return false;
}
if s[1..].contains('/') {
return true;
}
let first_word = s.split_whitespace().next().unwrap_or(s);
if let Some(ext) = std::path::Path::new(first_word).extension()
&& !ext.is_empty()
{
return true;
}
false
}
pub fn tilde_home(s: &str) -> String {
let home = match dirs::home_dir() {
Some(h) => h,
None => return s.to_string(),
};
let home_str = home.to_string_lossy();
if home_str.is_empty() {
return s.to_string();
}
s.replace(home_str.as_ref(), "~")
}
pub fn truncate_middle(s: &str, max_bytes: usize) -> String {
if s.len() <= max_bytes {
return s.to_string();
}
const ELLIPSIS: &str = "…"; if max_bytes <= ELLIPSIS.len() + 2 {
return truncate_str(s, max_bytes).to_string();
}
let budget = max_bytes - ELLIPSIS.len();
let tail_bytes = budget.div_ceil(2);
let head_bytes = budget - tail_bytes;
let mut head_end = head_bytes;
while head_end > 0 && !s.is_char_boundary(head_end) {
head_end -= 1;
}
let mut tail_start = s.len() - tail_bytes;
while tail_start < s.len() && !s.is_char_boundary(tail_start) {
tail_start += 1;
}
if tail_start <= head_end {
return truncate_str(s, max_bytes).to_string();
}
format!("{}{}{}", &s[..head_end], ELLIPSIS, &s[tail_start..])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_truncate_str_ascii() {
assert_eq!(truncate_str("hello world", 5), "hello");
assert_eq!(truncate_str("hello", 10), "hello");
assert_eq!(truncate_str("hello", 5), "hello");
}
#[test]
fn test_truncate_str_multibyte_boundary() {
let s = "abc█def";
assert_eq!(truncate_str(s, 3), "abc"); assert_eq!(truncate_str(s, 4), "abc"); assert_eq!(truncate_str(s, 5), "abc"); assert_eq!(truncate_str(s, 6), "abc█"); }
#[test]
fn test_truncate_str_emoji() {
let s = "hi🦀bye";
assert_eq!(truncate_str(s, 2), "hi");
assert_eq!(truncate_str(s, 3), "hi"); assert_eq!(truncate_str(s, 5), "hi"); assert_eq!(truncate_str(s, 6), "hi🦀");
}
#[test]
fn test_truncate_str_zero() {
assert_eq!(truncate_str("hello", 0), "");
assert_eq!(truncate_str("🦀", 0), "");
}
#[test]
fn test_truncate_str_empty() {
assert_eq!(truncate_str("", 5), "");
assert_eq!(truncate_str("", 0), "");
}
#[test]
fn test_truncate_str_all_multibyte() {
let s = "███"; assert_eq!(truncate_str(s, 1), ""); assert_eq!(truncate_str(s, 3), "█");
assert_eq!(truncate_str(s, 7), "██"); assert_eq!(truncate_str(s, 9), "███");
}
#[test]
fn test_truncate_middle_preserves_tail() {
let s = "/Users/alice/srv/dart/heyiolo/lib/presentation/pages/some_really_long_widget_name.dart";
let out = truncate_middle(s, 40);
assert!(
out.ends_with("some_really_long_widget_name.dart")
|| out.ends_with("_name.dart")
|| out.ends_with(".dart"),
"expected filename tail preserved, got: {}",
out
);
assert!(out.contains('…'), "expected ellipsis marker in middle");
assert!(out.len() <= 40, "expected <= 40 bytes, got {}", out.len());
}
#[test]
fn test_truncate_middle_short_string_unchanged() {
assert_eq!(truncate_middle("short.rs", 80), "short.rs");
}
#[test]
fn test_truncate_middle_tiny_budget_falls_back() {
let out = truncate_middle("hello world", 4);
assert!(out.len() <= 4);
}
#[test]
fn test_tilde_home_collapses_prefix() {
if let Some(h) = dirs::home_dir() {
let home_str = h.to_string_lossy();
let full = format!("{}/srv/project/file.rs", home_str);
assert_eq!(tilde_home(&full), "~/srv/project/file.rs");
assert_eq!(tilde_home("/tmp/foo"), "/tmp/foo");
let cmd = format!("cp {}/a {}/b", home_str, home_str);
assert_eq!(tilde_home(&cmd), "cp ~/a ~/b");
}
}
#[test]
fn test_looks_like_file_path_absolute_with_segments() {
assert!(looks_like_file_path("/Users/alice/Downloads/report.pdf"));
assert!(looks_like_file_path("/tmp/foo.txt"));
assert!(looks_like_file_path("/etc/hosts"));
}
#[test]
fn test_looks_like_file_path_with_extension_and_text() {
assert!(looks_like_file_path("/report.pdf check this"));
assert!(looks_like_file_path("/data.csv analyze"));
}
#[test]
fn test_looks_like_file_path_slash_commands_are_not_paths() {
assert!(!looks_like_file_path("/help"));
assert!(!looks_like_file_path("/models"));
assert!(!looks_like_file_path("/deploy staging"));
assert!(!looks_like_file_path("/credits"));
}
#[test]
fn test_looks_like_file_path_no_slash_prefix() {
assert!(!looks_like_file_path("hello world"));
assert!(!looks_like_file_path("report.pdf"));
}
}