keyarray/
lib.rs

1/// KeyArray is like a row of buttons; exactly one button (the current key)
2/// is “pressed” at any time.
3///
4/// To create (defaults to first key):
5///     let mut mykeys = KeyArray::new(["On", "Off", "Auto"]);
6///
7/// To create with an explicit start:
8///     let mut mykeys = KeyArray::new_with(["On", "Off"], 1);
9///
10/// To change the status (current key by index):
11///     mykeys.change(2);
12///
13/// To inspect:
14///     let idx = mykeys.current_index();
15///     let key = mykeys.current();
16///     let all = mykeys.keys();
17///
18/// To edit the key list:
19///     mykeys.push("New");
20///     mykeys.insert(1, "Inserted");
21///     let removed = mykeys.remove(0);
22///
23
24use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
25
26pub struct KeyArray<K> {
27    keys: Vec<K>,
28    idx: usize,
29}
30
31impl<K> KeyArray<K>
32where
33    K: Clone + PartialEq + Debug + Display,
34{
35    /// Create from any iterable of keys. Panics if empty.
36    pub fn new(keys: impl IntoIterator<Item = K>) -> Self {
37        let keys: Vec<K> = keys.into_iter().collect();
38        assert!(
39            !keys.is_empty(),
40            "KeyArray::new: must supply at least one key"
41        );
42        KeyArray { keys, idx: 0 }
43    }
44
45    /// Same as `new`, but start at `start_idx`. Panics if out of bounds.
46    pub fn new_with(keys: impl IntoIterator<Item = K>, start_idx: usize) -> Self {
47        let keys: Vec<K> = keys.into_iter().collect();
48        assert!(
49            !keys.is_empty(),
50            "KeyArray::new_with: must supply keys"
51        );
52        assert!(
53            start_idx < keys.len(),
54            "KeyArray::new_with: start_idx {} out of bounds",
55            start_idx
56        );
57        KeyArray {
58            keys,
59            idx: start_idx,
60        }
61    }
62
63    /// Change the current key by zero‐based index.
64    /// Panics if `i` is out of bounds.
65    pub fn change(&mut self, i: usize) {
66        assert!(
67            i < self.keys.len(),
68            "KeyArray::change: index {} out of bounds",
69            i
70        );
71        self.idx = i;
72    }
73
74    /// Get a reference to the current key.
75    pub fn current(&self) -> &K {
76        &self.keys[self.idx]
77    }
78
79    /// Get the index of the current key.
80    pub fn current_index(&self) -> usize {
81        self.idx
82    }
83
84    /// Get a slice of all keys.
85    pub fn keys(&self) -> &[K] {
86        &self.keys
87    }
88
89    /// Number of keys.
90    pub fn len(&self) -> usize {
91        self.keys.len()
92    }
93
94    /// Returns true if there are no keys.
95    pub fn is_empty(&self) -> bool {
96        self.len() == 0
97    }
98
99    /// Append a new key after the last.
100    pub fn push(&mut self, key: K) {
101        self.keys.push(key);
102    }
103
104    /// Insert a key at position `i`. Panics if `i > len`.
105    pub fn insert(&mut self, i: usize, key: K) {
106        assert!(
107            i <= self.keys.len(),
108            "KeyArray::insert: index {} out of bounds",
109            i
110        );
111        self.keys.insert(i, key);
112        // if you inserted before current idx, bump it forward
113        if i <= self.idx {
114            self.idx += 1;
115        }
116    }
117
118    /// Remove and return the key at `i`. Panics if out of bounds.
119    pub fn remove(&mut self, i: usize) -> K {
120        assert!(
121            i < self.keys.len(),
122            "KeyArray::remove: index {} out of bounds",
123            i
124        );
125        let removed = self.keys.remove(i);
126        // adjust current index
127        if self.idx >= self.keys.len() {
128            // if we removed the last element, clamp idx
129            self.idx = self.keys.len().saturating_sub(1);
130        }
131        removed
132    }
133}
134
135impl<K> Display for KeyArray<K>
136where
137    K: Clone + PartialEq + Debug + Display,
138{
139    fn fmt(&self, f: &mut Formatter) -> FmtResult {
140        write!(
141            f,
142            "keys={:?}, current_idx={}, current={}",
143            self.keys,
144            self.idx,
145            self.current()
146        )
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn basic_flow() {
156        let mut ka = KeyArray::new(["A", "B", "C"]);
157        assert_eq!(ka.current(), &"A");
158        assert_eq!(ka.current_index(), 0);
159
160        ka.change(2);
161        assert_eq!(ka.current(), &"C");
162
163        // push a new key
164        ka.push("D");
165        assert_eq!(ka.len(), 4);
166        assert_eq!(ka.keys(), &["A", "B", "C", "D"]);
167
168        // insert at front
169        ka.insert(0, "X");
170        assert_eq!(ka.keys()[0], "X");
171        // current was 2→ now is 3
172        assert_eq!(ka.current(), &"C");
173
174        // remove the first
175        let removed = ka.remove(0);
176        assert_eq!(removed, "X");
177        assert_eq!(ka.keys()[0], "A");
178    }
179
180    #[test]
181    #[should_panic(expected = "out of bounds")]
182    fn change_oob_panics() {
183        let mut ka = KeyArray::new(["Only"]);
184        ka.change(5);
185    }
186
187    #[test]
188    fn display_format() {
189        let ka = KeyArray::new(["Up", "Down"]);
190        let s = format!("{}", ka);
191        assert!(s.contains(r#"["Up", "Down"]"#) && s.contains("current_idx=0"));
192    }
193
194    #[test]
195    fn empty_and_len() {
196        let ka = KeyArray::new_with(["One"], 0);
197        assert!(!ka.is_empty());
198        assert_eq!(ka.len(), 1);
199
200        let mut empty = KeyArray::new(["X"]);
201        empty.remove(0);
202        assert!(empty.is_empty());
203        assert_eq!(empty.len(), 0);
204    }
205}