Skip to main content

scope/display/
text.rs

1//! # Short text
2//!
3//! The one way to shorten an identifier (address, hash, mint) for a
4//! narrow column, and the one ellipsis every surface uses.
5
6/// Marks text that was shortened or cut.
7pub const ELLIPSIS: &str = "…";
8
9/// How many characters of an identifier stay before the ellipsis.
10const HEAD_CHARS: usize = 6;
11
12/// How many characters of an identifier stay after the ellipsis.
13const TAIL_CHARS: usize = 4;
14
15/// `id` shortened to its start, [`ELLIPSIS`] and its end
16/// (`0x742d…3c2b`), or `id` itself when that would not be shorter. It
17/// counts characters, so any text is safe.
18pub fn abbreviate(id: &str) -> String {
19    let chars: Vec<char> = id.chars().collect();
20    if chars.len() <= HEAD_CHARS + TAIL_CHARS + 1 {
21        return id.to_string();
22    }
23    let head: String = chars[..HEAD_CHARS].iter().collect();
24    let tail: String = chars[chars.len() - TAIL_CHARS..].iter().collect();
25    format!("{head}{ELLIPSIS}{tail}")
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31
32    #[test]
33    fn test_abbreviate_keeps_head_and_tail() {
34        assert_eq!(
35            abbreviate("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
36            "0xA0b8…eB48"
37        );
38    }
39
40    #[test]
41    fn test_short_ids_stay_whole() {
42        assert_eq!(abbreviate(""), "");
43        assert_eq!(abbreviate("0x12345678a"), "0x12345678a");
44        assert_eq!(abbreviate("0x12345678ab"), "0x1234…78ab");
45    }
46
47    #[test]
48    fn test_non_ascii_ids_do_not_panic() {
49        // Byte slicing cut inside a character here.
50        assert_eq!(abbreviate("ünïcødé-ädrèss-ñame"), "ünïcød…ñame");
51        assert_eq!(
52            abbreviate("日本語のアドレス文字列です"),
53            "日本語のアド…字列です"
54        );
55    }
56}