use super::CliTableError;
pub fn expand_completion(pattern: &str) -> Result<String, CliTableError> {
let mut result = String::with_capacity(pattern.len() * 2);
let mut chars = pattern.chars().peekable();
while let Some(c) = chars.next() {
if c == '[' && chars.peek() == Some(&'[') {
chars.next();
let mut completion_chars = Vec::new();
loop {
match chars.next() {
Some(']') if chars.peek() == Some(&']') => {
chars.next(); break;
}
Some(ch) => completion_chars.push(ch),
None => {
return Err(CliTableError::InvalidCompletion(
"unclosed [[...]] completion".into(),
));
}
}
}
if completion_chars.is_empty() {
continue;
}
result.push('(');
for (i, ch) in completion_chars.iter().enumerate() {
if is_regex_meta(*ch) {
result.push('\\');
}
result.push(*ch);
if i < completion_chars.len() - 1 {
result.push('(');
}
}
for _ in 0..completion_chars.len() {
result.push_str(")?");
}
} else {
result.push(c);
}
}
Ok(result)
}
fn is_regex_meta(c: char) -> bool {
matches!(
c,
'.' | '*' | '+' | '?' | '(' | ')' | '[' | ']' | '{' | '}' | '|' | '^' | '$' | '\\'
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_completion() {
assert_eq!(expand_completion("sh[[ow]]").unwrap(), "sh(o(w)?)?");
}
#[test]
fn test_multiple_completions() {
assert_eq!(
expand_completion("sh[[ow]] ver[[sion]]").unwrap(),
"sh(o(w)?)? ver(s(i(o(n)?)?)?)?",
);
}
#[test]
fn test_no_completion() {
assert_eq!(expand_completion("show version").unwrap(), "show version");
}
#[test]
fn test_empty_completion() {
assert_eq!(expand_completion("sh[[]]ow").unwrap(), "show");
}
#[test]
fn test_single_char_completion() {
assert_eq!(expand_completion("sh[[o]]w").unwrap(), "sh(o)?w");
}
#[test]
fn test_completion_at_end() {
assert_eq!(
expand_completion("sh[[ow]] int[[erfaces]]").unwrap(),
"sh(o(w)?)? int(e(r(f(a(c(e(s)?)?)?)?)?)?)?",
);
}
#[test]
fn test_unclosed_completion() {
let result = expand_completion("sh[[ow");
assert!(matches!(result, Err(CliTableError::InvalidCompletion(_))));
}
#[test]
fn test_regex_metachar_in_completion() {
assert_eq!(expand_completion("test[[.x]]").unwrap(), "test(\\.(x)?)?");
}
#[test]
fn test_real_ntc_pattern() {
let result = expand_completion("sh[[ow]] (in[[terfaces]] e[[thernet]]|in[[terfaces]])$");
assert!(result.is_ok());
let expanded = result.unwrap();
assert!(expanded.contains("sh(o(w)?)?"));
assert!(expanded.contains("in(t(e(r(f(a(c(e(s)?)?)?)?)?)?)?)?"));
}
}