textfsm_core/clitable/
completion.rs1use super::CliTableError;
8
9pub fn expand_completion(pattern: &str) -> Result<String, CliTableError> {
22 let mut result = String::with_capacity(pattern.len() * 2);
23 let mut chars = pattern.chars().peekable();
24
25 while let Some(c) = chars.next() {
26 if c == '[' && chars.peek() == Some(&'[') {
27 chars.next(); let mut completion_chars = Vec::new();
31
32 loop {
34 match chars.next() {
35 Some(']') if chars.peek() == Some(&']') => {
36 chars.next(); break;
38 }
39 Some(ch) => completion_chars.push(ch),
40 None => {
41 return Err(CliTableError::InvalidCompletion(
42 "unclosed [[...]] completion".into(),
43 ));
44 }
45 }
46 }
47
48 if completion_chars.is_empty() {
51 continue;
53 }
54
55 result.push('(');
56 for (i, ch) in completion_chars.iter().enumerate() {
57 if is_regex_meta(*ch) {
59 result.push('\\');
60 }
61 result.push(*ch);
62 if i < completion_chars.len() - 1 {
63 result.push('(');
64 }
65 }
66 for _ in 0..completion_chars.len() {
68 result.push_str(")?");
69 }
70 } else {
71 result.push(c);
72 }
73 }
74
75 Ok(result)
76}
77
78fn is_regex_meta(c: char) -> bool {
80 matches!(
81 c,
82 '.' | '*' | '+' | '?' | '(' | ')' | '[' | ']' | '{' | '}' | '|' | '^' | '$' | '\\'
83 )
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_simple_completion() {
92 assert_eq!(expand_completion("sh[[ow]]").unwrap(), "sh(o(w)?)?");
93 }
94
95 #[test]
96 fn test_multiple_completions() {
97 assert_eq!(
98 expand_completion("sh[[ow]] ver[[sion]]").unwrap(),
99 "sh(o(w)?)? ver(s(i(o(n)?)?)?)?",
100 );
101 }
102
103 #[test]
104 fn test_no_completion() {
105 assert_eq!(expand_completion("show version").unwrap(), "show version");
106 }
107
108 #[test]
109 fn test_empty_completion() {
110 assert_eq!(expand_completion("sh[[]]ow").unwrap(), "show");
111 }
112
113 #[test]
114 fn test_single_char_completion() {
115 assert_eq!(expand_completion("sh[[o]]w").unwrap(), "sh(o)?w");
116 }
117
118 #[test]
119 fn test_completion_at_end() {
120 assert_eq!(
121 expand_completion("sh[[ow]] int[[erfaces]]").unwrap(),
122 "sh(o(w)?)? int(e(r(f(a(c(e(s)?)?)?)?)?)?)?",
123 );
124 }
125
126 #[test]
127 fn test_unclosed_completion() {
128 let result = expand_completion("sh[[ow");
129 assert!(matches!(result, Err(CliTableError::InvalidCompletion(_))));
130 }
131
132 #[test]
133 fn test_regex_metachar_in_completion() {
134 assert_eq!(expand_completion("test[[.x]]").unwrap(), "test(\\.(x)?)?");
136 }
137
138 #[test]
139 fn test_real_ntc_pattern() {
140 let result = expand_completion("sh[[ow]] (in[[terfaces]] e[[thernet]]|in[[terfaces]])$");
142 assert!(result.is_ok());
143 let expanded = result.unwrap();
144 assert!(expanded.contains("sh(o(w)?)?"));
145 assert!(expanded.contains("in(t(e(r(f(a(c(e(s)?)?)?)?)?)?)?)?"));
146 }
147}