fn is_name_char(character: char) -> bool {
character.is_ascii_alphanumeric() || matches!(character, '_' | ':' | '.' | '-')
}
pub fn invocation(prompt: &str) -> Option<(&str, &str)> {
let rest = prompt.strip_prefix('/')?;
let end = rest.find(char::is_whitespace).unwrap_or(rest.len());
let (name, arguments) = rest.split_at(end);
(!name.is_empty() && name.chars().all(is_name_char)).then_some((name, arguments.trim()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_the_first_token_is_read_as_a_name() {
assert_eq!(
invocation("/fix auth login.rs"),
Some(("fix", "auth login.rs"))
);
assert_eq!(invocation("/fix"), Some(("fix", "")));
assert_eq!(
invocation("/review\nthe diff below"),
Some(("review", "the diff below")),
"a multi-line prompt still opens with one token"
);
assert_eq!(invocation("fix /review"), None);
assert_eq!(invocation("/"), None);
}
#[test]
fn a_namespaced_name_is_one_token() {
assert_eq!(
invocation("/git:commit the parser fix"),
Some(("git:commit", "the parser fix"))
);
}
#[test]
fn a_path_is_a_path_and_passes_straight_through() {
for prose in [
"/usr/bin/x crashes on startup",
"/ is the root directory",
"//comment syntax",
"look at /etc/hosts",
] {
assert_eq!(invocation(prose), None, "{prose} is prose");
}
}
}