use std::fmt;
use std::str::FromStr;
use clap::Args;
#[derive(Args, Clone, Debug, PartialEq, Eq)]
pub struct Cmd {
#[clap(name = "CMD")]
cmd: String,
#[clap(name = "ARGS")]
args: Vec<String>,
}
impl Cmd {
pub fn new<C, I, A>(cmd: C, args: I) -> Self
where
C: Into<String>,
I: Iterator<Item = A>,
A: Into<String>,
{
Self {
cmd: cmd.into(),
args: args.map(Into::into).collect(),
}
}
}
impl From<Cmd> for String {
fn from(cmd: Cmd) -> Self {
cmd.to_string()
}
}
impl fmt::Display for Cmd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.cmd)?;
for arg in self.args.iter() {
write!(f, " {arg}")?;
}
Ok(())
}
}
impl<'a> From<&'a str> for Cmd {
fn from(s: &'a str) -> Self {
s.parse().expect("Failed to parse into cmd")
}
}
impl FromStr for Cmd {
type Err = Box<dyn std::error::Error>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let tokens = if cfg!(unix) {
shell_words::split(s)?
} else if cfg!(windows) {
winsplit::split(s)
} else {
unreachable!(
"FromStr<Cmd>: Unsupported operating system outside Unix and Windows families!"
);
};
if tokens.is_empty() {
return Ok(Self {
cmd: String::new(),
args: Vec::new(),
});
}
let mut it = tokens.into_iter();
Ok(Self {
cmd: it.next().unwrap(),
args: it.collect(),
})
}
}