use crate::shared::i18n::Locale;
pub const ALIASES: [&str; 2] = ["/exit", "/quit"];
pub fn parse(input: &str, loc: &Locale) -> Option<Result<(), String>> {
let mut tokens = input.split_whitespace();
let first = tokens.next()?;
let cmd = ALIASES.iter().find(|a| first.eq_ignore_ascii_case(a))?;
match tokens.next() {
None => Some(Ok(())),
Some(arg) => Some(Err(loc.tf("ui.exit.bad_arg", &[("cmd", cmd), ("arg", arg)]))),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ru() -> &'static Locale {
crate::shared::i18n::locale(crate::shared::i18n::Lang::Ru)
}
#[test]
fn every_alias_parses_bare_in_any_case_and_padding() {
for cmd in ALIASES {
for text in [
cmd.to_string(),
format!(" {cmd} "),
format!("\t{cmd}\n"),
cmd.to_uppercase(),
format!(" {} ", cmd.to_uppercase()),
] {
assert_eq!(parse(&text, ru()), Some(Ok(())), "input {text:?}");
}
}
}
#[test]
fn trailing_arguments_are_rejected() {
for cmd in ALIASES {
for text in [
format!("{cmd} now"),
format!("{cmd} --force"),
format!(" {} now ", cmd.to_uppercase()),
] {
assert!(matches!(parse(&text, ru()), Some(Err(_))), "input {text:?}");
}
}
}
#[test]
fn the_error_names_the_typed_command_and_the_argument() {
let Some(Err(msg)) = parse("/quit nooow", ru()) else {
panic!("expected a syntax error");
};
assert!(msg.contains("nooow"), "{msg}");
assert!(msg.contains("/quit"), "{msg}");
assert!(
!msg.contains("/exit"),
"the untyped spelling leaked in: {msg}"
);
}
#[test]
fn other_input_is_none() {
assert_eq!(parse("/compact", ru()), None);
assert_eq!(parse("/reindex", ru()), None);
assert_eq!(parse("/rag rebuild", ru()), None);
assert_eq!(parse("/tts stop", ru()), None);
assert_eq!(parse("/exits", ru()), None);
assert_eq!(parse("/exit-now", ru()), None);
assert_eq!(parse("/quitter", ru()), None);
assert_eq!(parse("exit", ru()), None);
assert_eq!(parse("how do I quit vim?", ru()), None);
assert_eq!(parse("", ru()), None);
assert_eq!(parse(" ", ru()), None);
}
#[test]
fn errors_are_localized_for_all_langs() {
for &lang in crate::shared::i18n::Lang::ALL {
let loc = crate::shared::i18n::locale(lang);
let Some(Err(msg)) = parse("/exit now", loc) else {
panic!("expected a syntax error in {lang:?}");
};
assert!(
!msg.contains('{') && !msg.contains('}'),
"unsubstituted placeholder in {lang:?}: {msg}"
);
assert!(
msg.contains("/exit"),
"the message must name the command in {lang:?}: {msg}"
);
assert!(
msg.contains("Ctrl+Q") && msg.contains("F10"),
"the message must name the keys that also quit in {lang:?}: {msg}"
);
if lang == crate::shared::i18n::Lang::En {
assert!(
!msg.chars().any(|c| ('\u{0400}'..='\u{04FF}').contains(&c)),
"Cyrillic leaked into the en message: {msg}"
);
}
}
}
}