@@ -125,6 +125,19 @@ pub fn convert_temperature(celsius: f32, unit: TemperatureUnit) -> f32 {
125125 }
126126}
127127
128+ pub fn truncate_str ( s : & str , max_len : usize ) -> String {
129+ let char_count = s. chars ( ) . count ( ) ;
130+ if char_count <= max_len {
131+ s. to_string ( )
132+ } else if max_len <= 3 {
133+ s. chars ( ) . take ( max_len) . collect ( )
134+ } else {
135+ let visible_len = max_len - 3 ;
136+ let result: String = s. chars ( ) . take ( visible_len) . collect ( ) ;
137+ result + "..."
138+ }
139+ }
140+
128141#[ cfg( test) ]
129142mod tests {
130143 use super :: * ;
@@ -241,4 +254,61 @@ mod tests {
241254 "1.0 GiB"
242255 ) ;
243256 }
257+
258+ #[ test]
259+ fn test_truncate_str_no_underflow_when_max_len_small ( ) {
260+ assert_eq ! ( truncate_str( "Terminal" , 0 ) , "" ) ;
261+ assert_eq ! ( truncate_str( "Terminal" , 1 ) , "T" ) ;
262+ assert_eq ! ( truncate_str( "Terminal" , 2 ) , "Te" ) ;
263+ assert_eq ! ( truncate_str( "Terminal" , 3 ) , "Ter" ) ;
264+ }
265+
266+ #[ test]
267+ fn test_truncate_str_adds_ellipsis ( ) {
268+ assert_eq ! ( truncate_str( "Terminal" , 7 ) , "Term..." ) ;
269+ assert_eq ! ( truncate_str( "Terminal" , 4 ) , "T..." ) ;
270+ }
271+
272+ #[ test]
273+ fn test_truncate_str_unchanged_when_fits ( ) {
274+ assert_eq ! ( truncate_str( "Terminal" , 8 ) , "Terminal" ) ;
275+ assert_eq ! ( truncate_str( "Terminal" , 10 ) , "Terminal" ) ;
276+ }
277+
278+ #[ test]
279+ fn test_truncate_str_utf8_multibyte_chars ( ) {
280+ // Emoji (4 bytes each in UTF-8)
281+ assert_eq ! ( truncate_str( "🚀🔥💻" , 3 ) , "🚀🔥💻" ) ; // Fits exactly
282+ assert_eq ! ( truncate_str( "🚀🔥💻" , 2 ) , "🚀🔥" ) ; // max_len <= 3, no ellipsis
283+ assert_eq ! ( truncate_str( "🚀🔥💻🎉" , 4 ) , "🚀🔥💻🎉" ) ; // Fits exactly (4 chars)
284+ assert_eq ! ( truncate_str( "🚀🔥💻🎉🌟" , 4 ) , "🚀..." ) ; // Truncates with ellipsis
285+
286+ // Mixed ASCII and emoji
287+ assert_eq ! ( truncate_str( "Terminal🚀" , 9 ) , "Terminal🚀" ) ; // Fits exactly
288+ assert_eq ! ( truncate_str( "Terminal🚀" , 8 ) , "Termi..." ) ; // Truncates
289+
290+ // CJK characters (3 bytes each in UTF-8)
291+ assert_eq ! ( truncate_str( "文字列" , 3 ) , "文字列" ) ; // Fits exactly
292+ assert_eq ! ( truncate_str( "文字列テスト" , 5 ) , "文字..." ) ; // Truncates with ellipsis
293+
294+ // Accented characters
295+ assert_eq ! ( truncate_str( "café" , 4 ) , "café" ) ; // Fits exactly
296+ assert_eq ! ( truncate_str( "naïve" , 4 ) , "n..." ) ; // Truncates with ellipsis
297+ }
298+
299+ #[ test]
300+ fn test_truncate_str_utf8_edge_cases ( ) {
301+ // Empty string
302+ assert_eq ! ( truncate_str( "" , 0 ) , "" ) ;
303+ assert_eq ! ( truncate_str( "" , 5 ) , "" ) ;
304+
305+ // Single multi-byte char with small max_len
306+ assert_eq ! ( truncate_str( "🚀" , 1 ) , "🚀" ) ; // Fits (1 char)
307+ assert_eq ! ( truncate_str( "🚀" , 0 ) , "" ) ; // Zero length
308+
309+ // Ensure no panic on boundary conditions
310+ assert_eq ! ( truncate_str( "🚀hello" , 1 ) , "🚀" ) ;
311+ assert_eq ! ( truncate_str( "🚀hello" , 4 ) , "🚀..." ) ;
312+ assert_eq ! ( truncate_str( "🚀hello" , 6 ) , "🚀hello" ) ;
313+ }
244314}
0 commit comments