embedded_cli/
autocomplete.rs1use crate::utils;
2
3#[derive(Clone, Debug)]
4#[non_exhaustive]
5pub enum Request<'a> {
6 CommandName(&'a str),
8}
9
10impl<'a> Request<'a> {
11 pub fn from_input(input: &'a str) -> Option<Self> {
12 let input = utils::trim_start(input);
13
14 if input.is_empty() {
15 return None;
16 }
17
18 if !input.contains(' ') {
20 Some(Request::CommandName(input))
21 } else {
22 None
23 }
24 }
25}
26
27#[derive(Debug)]
28pub struct Autocompletion<'a> {
29 autocompleted: Option<usize>,
30 buffer: &'a mut [u8],
31 partial: bool,
32}
33
34impl<'a> Autocompletion<'a> {
35 pub fn new(buffer: &'a mut [u8]) -> Self {
36 Self {
37 autocompleted: None,
38 buffer,
39 partial: false,
40 }
41 }
42
43 pub fn autocompleted(&self) -> Option<&str> {
44 self.autocompleted.map(|len| {
45 unsafe { core::str::from_utf8_unchecked(&self.buffer[..len]) }
47 })
48 }
49
50 pub fn is_partial(&self) -> bool {
53 self.partial
54 }
55
56 pub fn mark_partial(&mut self) {
58 self.partial = true;
59 }
60
61 pub fn merge_autocompletion(&mut self, autocompletion: &str) {
63 if autocompletion.is_empty() || self.buffer.is_empty() {
64 self.partial = self.partial
65 || self.autocompleted.is_some()
66 || (self.buffer.is_empty() && !autocompletion.is_empty());
67 self.autocompleted = Some(0);
68 return;
69 }
70
71 let len = match self.autocompleted() {
74 Some(current) => utils::common_prefix_len(autocompletion, current),
75 None => autocompletion.len(),
76 };
77
78 if len > self.buffer.len() {
79 } else {
83 self.partial =
84 self.partial || len < autocompletion.len() || self.autocompleted.is_some();
85 self.buffer[..len].copy_from_slice(&autocompletion.as_bytes()[..len]);
86 self.autocompleted = Some(len);
87 };
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use rstest::rstest;
94
95 use crate::autocomplete::Autocompletion;
96
97 #[test]
98 fn no_merge() {
99 let mut input = [0; 64];
100
101 let autocompletion = Autocompletion::new(&mut input);
102
103 assert!(!autocompletion.is_partial());
104 assert_eq!(autocompletion.autocompleted(), None);
105 }
106
107 #[rstest]
108 #[case("abc", "abc")]
109 #[case("", "")]
110 fn merge_single(#[case] text: &str, #[case] expected: &str) {
111 let mut input = [0; 64];
112
113 let mut autocompletion = Autocompletion::new(&mut input);
114
115 autocompletion.merge_autocompletion(text);
116
117 assert!(!autocompletion.is_partial());
118 assert_eq!(autocompletion.autocompleted(), Some(expected));
119 assert_eq!(&input[..expected.len()], expected.as_bytes());
120 }
121
122 #[rstest]
123 #[case("abc1", "abc2", "abc")]
124 #[case("ab", "abc", "ab")]
125 #[case("abc", "ab", "ab")]
126 #[case("", "ab", "")]
127 #[case("ab", "", "")]
128 #[case("abc", "def", "")]
129 fn merge_multiple(#[case] text1: &str, #[case] text2: &str, #[case] expected: &str) {
130 let mut input = [0; 64];
131
132 let mut autocompletion = Autocompletion::new(&mut input);
133
134 autocompletion.merge_autocompletion(text1);
135 autocompletion.merge_autocompletion(text2);
136
137 assert!(autocompletion.is_partial());
138 assert_eq!(autocompletion.autocompleted(), Some(expected));
139 assert_eq!(&input[..expected.len()], expected.as_bytes());
140 }
141}