Skip to main content

gitkraft_core/utils/
text.rs

1//! Text manipulation utilities shared across the crate.
2
3/// Truncate `s` to at most `max_chars` Unicode characters, appending "…" if shortened.
4///
5/// This is a framework-agnostic utility shared by both the GUI and TUI.
6pub fn truncate_str(s: &str, max_chars: usize) -> String {
7    let char_count = s.chars().count();
8    if char_count <= max_chars {
9        s.to_string()
10    } else if max_chars <= 1 {
11        "…".to_string()
12    } else {
13        let mut out: String = s.chars().take(max_chars - 1).collect();
14        out.push('…');
15        out
16    }
17}
18
19/// Extract the filename (last component) from a `/`-separated path string.
20///
21/// Returns the entire input when there is no `/` separator.
22///
23/// # Examples
24/// ```
25/// assert_eq!(gitkraft_core::path_basename("src/main.rs"), "main.rs");
26/// assert_eq!(gitkraft_core::path_basename("hello.txt"), "hello.txt");
27/// assert_eq!(gitkraft_core::path_basename(""), "");
28/// ```
29pub fn path_basename(path: &str) -> &str {
30    path.rsplit('/').next().unwrap_or(path)
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn no_truncation_when_short() {
39        assert_eq!(truncate_str("hello", 10), "hello");
40    }
41
42    #[test]
43    fn exact_length_no_truncation() {
44        assert_eq!(truncate_str("hello", 5), "hello");
45    }
46
47    #[test]
48    fn truncation_adds_ellipsis() {
49        assert_eq!(truncate_str("hello world", 5), "hell…");
50    }
51
52    #[test]
53    fn max_one_gives_ellipsis() {
54        assert_eq!(truncate_str("hello", 1), "…");
55    }
56
57    #[test]
58    fn max_zero_gives_ellipsis() {
59        assert_eq!(truncate_str("hello", 0), "…");
60    }
61
62    #[test]
63    fn unicode_chars_counted_correctly() {
64        // 4 chars: each emoji is 1 char
65        assert_eq!(truncate_str("😀😁😂😃", 3), "😀😁…");
66    }
67
68    #[test]
69    fn empty_string() {
70        assert_eq!(truncate_str("", 5), "");
71    }
72
73    #[test]
74    fn path_basename_with_slashes() {
75        assert_eq!(path_basename("src/main.rs"), "main.rs");
76    }
77
78    #[test]
79    fn path_basename_no_slash() {
80        assert_eq!(path_basename("hello.txt"), "hello.txt");
81    }
82
83    #[test]
84    fn path_basename_trailing_slash() {
85        assert_eq!(path_basename("dir/"), "");
86    }
87
88    #[test]
89    fn path_basename_empty() {
90        assert_eq!(path_basename(""), "");
91    }
92
93    #[test]
94    fn path_basename_deep_path() {
95        assert_eq!(path_basename("a/b/c/d/file.rs"), "file.rs");
96    }
97}