1use crate::db::CommandRecord;
9use crate::tui::app::last_segment;
10
11const ELLIPSIS: char = '…';
13
14pub fn short_time(created_at: &str) -> &str {
18 created_at.get(11..16).unwrap_or(created_at)
20}
21
22pub fn truncate_end(s: &str, max: usize) -> String {
27 if max == 0 {
28 return String::new();
29 }
30 let count = s.chars().count();
31 if count <= max {
32 return s.to_string();
33 }
34 if max == 1 {
35 return ELLIPSIS.to_string();
36 }
37 let mut out: String = s.chars().take(max - 1).collect();
38 out.push(ELLIPSIS);
39 out
40}
41
42pub fn truncate_middle(s: &str, max: usize) -> String {
47 let count = s.chars().count();
48 if count <= max {
49 return s.to_string();
50 }
51 if max < 5 {
52 return truncate_end(s, max);
53 }
54 let keep = max - 1;
56 let head = keep.div_ceil(2);
57 let tail = keep - head;
58 let chars: Vec<char> = s.chars().collect();
59 let mut out: String = chars[..head].iter().collect();
60 out.push(ELLIPSIS);
61 out.extend(chars[count - tail..].iter());
62 out
63}
64
65pub fn context_label(record: &CommandRecord) -> String {
68 if let Some(root) = &record.git_root {
69 if !root.is_empty() {
70 return last_segment(root).to_string();
71 }
72 }
73 if let Some(cwd) = &record.cwd {
74 if !cwd.is_empty() {
75 return last_segment(cwd).to_string();
76 }
77 }
78 "-".to_string()
79}
80
81pub fn status_text(exit_code: Option<i64>) -> &'static str {
83 match exit_code {
84 Some(0) => "SUCCESS",
85 Some(_) => "FAILED",
86 None => "-",
87 }
88}
89
90pub fn status_symbol(exit_code: Option<i64>) -> &'static str {
92 match exit_code {
93 Some(0) => "✓",
94 Some(_) => "✗",
95 None => "·",
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 fn rec(command: &str) -> CommandRecord {
104 CommandRecord {
105 id: 1,
106 command: command.to_string(),
107 cwd: None,
108 shell: None,
109 hostname: None,
110 exit_code: None,
111 created_at: "2026-06-14 17:29:11".to_string(),
112 git_root: None,
113 git_branch: None,
114 git_remote: None,
115 session_id: None,
116 }
117 }
118
119 #[test]
120 fn short_time_extrait_hh_mm() {
121 assert_eq!(short_time("2026-06-14 17:29:11"), "17:29");
122 }
123
124 #[test]
125 fn short_time_tolere_format_inattendu() {
126 assert_eq!(short_time("court"), "court");
127 assert_eq!(short_time(""), "");
128 }
129
130 #[test]
131 fn truncate_end_court_inchange() {
132 assert_eq!(truncate_end("git push", 20), "git push");
133 }
134
135 #[test]
136 fn truncate_end_ajoute_ellipse() {
137 let out = truncate_end("git push origin main", 8);
138 assert_eq!(out.chars().count(), 8);
139 assert!(out.ends_with('…'));
140 }
141
142 #[test]
143 fn truncate_end_cas_limites() {
144 assert_eq!(truncate_end("abc", 0), "");
145 assert_eq!(truncate_end("abc", 1), "…");
146 }
147
148 #[test]
149 fn truncate_end_ne_panique_pas_sur_utf8() {
150 let s = "éàçü emojis 🚀🚀🚀 accentués";
151 let out = truncate_end(s, 5);
152 assert_eq!(out.chars().count(), 5);
153 }
154
155 #[test]
156 fn truncate_middle_conserve_tete_et_queue() {
157 let path = "/home/killian/projects/mnemo/backups";
158 let out = truncate_middle(path, 20);
159 assert_eq!(out.chars().count(), 20);
160 assert!(out.contains('…'));
161 assert!(out.starts_with('/'));
162 assert!(out.ends_with('s'));
163 }
164
165 #[test]
166 fn truncate_middle_court_inchange() {
167 assert_eq!(truncate_middle("/tmp", 20), "/tmp");
168 }
169
170 #[test]
171 fn context_label_prefere_projet_git() {
172 let mut r = rec("cargo build");
173 r.git_root = Some("/home/killian/mnemo".to_string());
174 assert_eq!(context_label(&r), "mnemo");
175 }
176
177 #[test]
178 fn context_label_retombe_sur_dossier() {
179 let mut r = rec("ls");
180 r.cwd = Some("/var/log".to_string());
181 assert_eq!(context_label(&r), "log");
182 }
183
184 #[test]
185 fn context_label_sans_contexte() {
186 assert_eq!(context_label(&rec("ls")), "-");
187 }
188
189 #[test]
190 fn status_text_et_symbole() {
191 assert_eq!(status_text(Some(0)), "SUCCESS");
192 assert_eq!(status_text(Some(1)), "FAILED");
193 assert_eq!(status_text(None), "-");
194 assert_eq!(status_symbol(Some(0)), "✓");
195 assert_eq!(status_symbol(Some(2)), "✗");
196 }
197}