1const SHOWN: usize = 4;
12
13const SHOWABLE: usize = 12;
16
17#[derive(Clone, PartialEq, Eq)]
19pub struct Key(String);
20
21impl Key {
22 #[must_use]
26 pub fn new(text: &str) -> Option<Self> {
27 let trimmed = text.trim();
28 if trimmed.is_empty() || trimmed.chars().any(char::is_control) {
31 return None;
32 }
33 Some(Self(trimmed.to_owned()))
34 }
35
36 #[must_use]
39 pub fn last_four(&self) -> Option<String> {
40 let characters: Vec<char> = self.0.chars().collect();
41 (characters.len() >= SHOWABLE).then(|| characters[characters.len() - SHOWN..].iter().collect())
42 }
43
44 pub(crate) fn expose(&self) -> &str {
46 &self.0
47 }
48}
49
50impl std::fmt::Debug for Key {
51 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self.last_four() {
55 Some(tail) => write!(formatter, "Key(…{tail})"),
56 None => formatter.write_str("Key(…)"),
57 }
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 const MADE_UP: &str = "not-a-real-key-0000-wxyz";
67
68 #[test]
69 fn only_the_last_four_characters_are_ever_shown() {
70 let key = Key::new(MADE_UP).expect("a key");
71 assert_eq!(key.last_four().as_deref(), Some("wxyz"));
72 assert_eq!(format!("{key:?}"), "Key(…wxyz)");
73 assert!(!format!("{key:?}").contains("not-a-real"), "the front of the key is not printed");
74 }
75
76 #[test]
77 fn a_short_key_shows_nothing_at_all_rather_than_most_of_itself() {
78 let key = Key::new("abcdefgh").expect("a key");
79 assert_eq!(key.last_four(), None, "four of eight characters is most of the key");
80 assert_eq!(format!("{key:?}"), "Key(…)");
81 assert!(!format!("{key:?}").contains("efgh"));
82 }
83
84 #[test]
85 fn what_the_clipboard_brings_along_is_dropped_and_a_paragraph_is_refused() {
86 let key = Key::new(&format!(" {MADE_UP}\n")).expect("a key");
87 assert_eq!(key.expose(), MADE_UP, "the key itself is what a header gets");
88 assert_eq!(Key::new(""), None);
89 assert_eq!(Key::new(" \n "), None);
90 assert_eq!(Key::new("first line\nsecond line"), None, "no header holds two lines");
91 assert_eq!(Key::new("has\ttab"), None);
92 }
93}