use {
std::fmt,
};
#[derive(Clone, Debug, PartialEq)]
pub struct VerbInvocation {
pub name: String,
pub args: Option<String>,
pub bang: bool,
}
impl fmt::Display for VerbInvocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, ":")?;
if self.bang {
write!(f, "!")?;
}
write!(f, "{}", &self.name)?;
if let Some(args) = &self.args {
write!(f, " {}", &args)?;
}
Ok(())
}
}
impl VerbInvocation {
pub fn new<T: Into<String>>(name: T, args: Option<T>, bang: bool) -> Self {
Self {
name: name.into(),
args: args.map(|s| s.into()),
bang,
}
}
pub fn is_empty(&self) -> bool {
self.name.is_empty()
}
pub fn complete_name(&self) -> String {
if self.bang {
format!("{}_tab", &self.name)
} else {
self.name.clone()
}
}
pub fn to_string_for_name(&self, name: &str) -> String {
let mut s = String::new();
if self.bang {
s.push('!');
}
s.push_str(name);
if let Some(args) = &self.args {
s.push(' ');
s.push_str(&args);
}
s
}
}
impl From<&str> for VerbInvocation {
fn from(invocation: &str) -> Self {
let caps = regex!(
r"(?x)
^
(?P<bang_before>!)?
(?P<name>[^!\s]*)
(?P<bang_after>!(?P<post_bang>[^\s:]+)?)?
(?:[\s:]+(?P<args>.*))?
\s*
$
"
)
.captures(invocation)
.unwrap();
let bang_before = caps.name("bang_before").is_some();
let bang_after = caps.name("bang_after").is_some();
let bang = bang_before || bang_after;
if let Some(post_bang) = caps.name("post_bang") {
info!("ignored post_bang: {:?}", post_bang);
}
let name = caps.name("name").unwrap().as_str().to_string();
let args = caps.name("args").map(|c| c.as_str().to_string());
VerbInvocation { name, args, bang }
}
}
#[cfg(test)]
mod verb_invocation_tests {
use super::*;
#[test]
fn check_verb_invocation_parsing_empty_arg() {
assert_eq!(
VerbInvocation::from("!mv"),
VerbInvocation::new("mv", None, true),
);
assert_eq!(
VerbInvocation::from("mva!"),
VerbInvocation::new("mva", None, true),
);
assert_eq!(
VerbInvocation::from("cp "),
VerbInvocation::new("cp", Some(""), false),
);
assert_eq!(
VerbInvocation::from("cp ../"),
VerbInvocation::new("cp", Some("../"), false),
);
}
#[test]
fn check_verb_invocation_parsing_post_bang() {
assert_eq!(
VerbInvocation::from("mva!a"),
VerbInvocation::new("mva", None, true),
);
assert_eq!(
VerbInvocation::from("!!!"),
VerbInvocation::new("", None, true),
);
}
#[test]
fn check_verb_invocation_parsing_empty_verb() {
assert_eq!(
VerbInvocation::from(""),
VerbInvocation::new("", None, false),
);
assert_eq!(
VerbInvocation::from("!"),
VerbInvocation::new("", None, true),
);
assert_eq!(
VerbInvocation::from("!!"),
VerbInvocation::new("", None, true),
);
assert_eq!(
VerbInvocation::from("!!a"), VerbInvocation::new("", None, true),
);
assert_eq!(
VerbInvocation::from("!! "),
VerbInvocation::new("", Some(""), true),
);
assert_eq!(
VerbInvocation::from("!! a"),
VerbInvocation::new("", Some("a"), true),
);
}
#[test]
fn check_verb_invocation_parsing_oddities() {
assert_eq!(
VerbInvocation::from("a ! !"),
VerbInvocation::new("a", Some("! !"), false),
);
assert_eq!(
VerbInvocation::from("!a !a"),
VerbInvocation::new("a", Some("!a"), true),
);
assert_eq!(
VerbInvocation::from("a! ! //"),
VerbInvocation::new("a", Some("! //"), true),
);
assert_eq!(
VerbInvocation::from(".. .."),
VerbInvocation::new("..", Some(".."), false),
);
}
}