use crate::db::CommandRecord;
use crate::tui::app::last_segment;
const ELLIPSIS: char = '…';
pub fn short_time(created_at: &str) -> &str {
created_at.get(11..16).unwrap_or(created_at)
}
pub fn truncate_end(s: &str, max: usize) -> String {
if max == 0 {
return String::new();
}
let count = s.chars().count();
if count <= max {
return s.to_string();
}
if max == 1 {
return ELLIPSIS.to_string();
}
let mut out: String = s.chars().take(max - 1).collect();
out.push(ELLIPSIS);
out
}
pub fn truncate_middle(s: &str, max: usize) -> String {
let count = s.chars().count();
if count <= max {
return s.to_string();
}
if max < 5 {
return truncate_end(s, max);
}
let keep = max - 1;
let head = keep.div_ceil(2);
let tail = keep - head;
let chars: Vec<char> = s.chars().collect();
let mut out: String = chars[..head].iter().collect();
out.push(ELLIPSIS);
out.extend(chars[count - tail..].iter());
out
}
pub fn context_label(record: &CommandRecord) -> String {
if let Some(root) = &record.git_root {
if !root.is_empty() {
return last_segment(root).to_string();
}
}
if let Some(cwd) = &record.cwd {
if !cwd.is_empty() {
return last_segment(cwd).to_string();
}
}
"-".to_string()
}
pub fn status_text(exit_code: Option<i64>) -> &'static str {
match exit_code {
Some(0) => "SUCCESS",
Some(_) => "FAILED",
None => "-",
}
}
pub fn status_symbol(exit_code: Option<i64>) -> &'static str {
match exit_code {
Some(0) => "✓",
Some(_) => "✗",
None => "·",
}
}
#[cfg(test)]
mod tests {
use super::*;
fn rec(command: &str) -> CommandRecord {
CommandRecord {
id: 1,
command: command.to_string(),
cwd: None,
shell: None,
hostname: None,
exit_code: None,
created_at: "2026-06-14 17:29:11".to_string(),
git_root: None,
git_branch: None,
git_remote: None,
session_id: None,
}
}
#[test]
fn short_time_extrait_hh_mm() {
assert_eq!(short_time("2026-06-14 17:29:11"), "17:29");
}
#[test]
fn short_time_tolere_format_inattendu() {
assert_eq!(short_time("court"), "court");
assert_eq!(short_time(""), "");
}
#[test]
fn truncate_end_court_inchange() {
assert_eq!(truncate_end("git push", 20), "git push");
}
#[test]
fn truncate_end_ajoute_ellipse() {
let out = truncate_end("git push origin main", 8);
assert_eq!(out.chars().count(), 8);
assert!(out.ends_with('…'));
}
#[test]
fn truncate_end_cas_limites() {
assert_eq!(truncate_end("abc", 0), "");
assert_eq!(truncate_end("abc", 1), "…");
}
#[test]
fn truncate_end_ne_panique_pas_sur_utf8() {
let s = "éàçü emojis 🚀🚀🚀 accentués";
let out = truncate_end(s, 5);
assert_eq!(out.chars().count(), 5);
}
#[test]
fn truncate_middle_conserve_tete_et_queue() {
let path = "/home/killian/projects/mnemo/backups";
let out = truncate_middle(path, 20);
assert_eq!(out.chars().count(), 20);
assert!(out.contains('…'));
assert!(out.starts_with('/'));
assert!(out.ends_with('s'));
}
#[test]
fn truncate_middle_court_inchange() {
assert_eq!(truncate_middle("/tmp", 20), "/tmp");
}
#[test]
fn context_label_prefere_projet_git() {
let mut r = rec("cargo build");
r.git_root = Some("/home/killian/mnemo".to_string());
assert_eq!(context_label(&r), "mnemo");
}
#[test]
fn context_label_retombe_sur_dossier() {
let mut r = rec("ls");
r.cwd = Some("/var/log".to_string());
assert_eq!(context_label(&r), "log");
}
#[test]
fn context_label_sans_contexte() {
assert_eq!(context_label(&rec("ls")), "-");
}
#[test]
fn status_text_et_symbole() {
assert_eq!(status_text(Some(0)), "SUCCESS");
assert_eq!(status_text(Some(1)), "FAILED");
assert_eq!(status_text(None), "-");
assert_eq!(status_symbol(Some(0)), "✓");
assert_eq!(status_symbol(Some(2)), "✗");
}
}