pub fn comma_separated(s: &str) -> Vec<String> {
s.split(',').map(|s| s.to_string()).collect()
}
pub fn program_from_arg0(program: &str, arg0: &str) -> String {
arg0.rsplit_once('/').map(|x| x.0)
.map(|s| format!("{}/{}", s, program))
.unwrap_or(program.to_string())
}
pub fn find_program_from_env(program: &str) -> String {
let p = program_from_arg0(program, &std::env::args().next().unwrap());
if std::path::Path::new(&p).exists() {
p
} else {
program.to_string()
}
}
pub struct PhCommand<'p, 'a> {
program: &'p str,
args: &'a [String],
ph: String,
}
impl PhCommand<'_, '_> {
pub fn program(&self) -> &str {
self.program
}
pub fn args(&self) -> Vec<String> {
self.args
.iter()
.map(|s| s.replace("{}", &self.ph))
.collect()
}
pub fn command(&self) -> std::process::Command {
let mut command = std::process::Command::new(self.program);
command.args(&self.args());
command
}
pub fn command_string(&self) -> String {
let command = self.args().join(" ");
format!("{} {}", self.program, command)
}
}
pub struct PhCommandVec {
program: String,
args: Vec<String>,
phargs: Vec<String>,
args_has_ph: bool,
}
impl PhCommandVec {
pub fn new<P: Into<String>, A: Into<String>, H: Into<String>>(
program: P,
args: Vec<A>,
phargs: Vec<H>,
) -> Self {
let args = args.into_iter().map(Into::into).collect::<Vec<_>>();
let phargs = phargs.into_iter().map(Into::into).collect::<Vec<_>>();
let args = extend_row(args.iter(), &phargs);
Self {
args_has_ph: row_has_ph(args.iter()),
program: program.into(),
args,
phargs,
}
}
pub fn iter(&self) -> PhCommandIterZero<impl Iterator<Item = &String>> {
PhCommandIterZero {
program: &self.program,
args: &self.args,
phargs: self.phargs.iter(),
args_has_ph: self.args_has_ph,
is_first: true,
}
}
}
pub struct PhCommandIterZero<'p, 'a, P>
where
P: Iterator,
P::Item: Into<String>,
{
program: &'p str,
args: &'a [String],
phargs: P,
args_has_ph: bool,
is_first: bool,
}
impl<'p, 'a, P> Iterator for PhCommandIterZero<'p, 'a, P>
where
P: Iterator,
P::Item: Into<String>,
{
type Item = PhCommand<'p, 'a>;
fn next(&mut self) -> Option<Self::Item> {
if !self.args_has_ph && !self.is_first {
None
} else {
self.is_first = false;
self.phargs.next().map(|ph| PhCommand {
program: self.program,
args: self.args,
ph: ph.into(),
})
}
}
}
pub fn extend_array<'a, S: AsRef<str>, T: AsRef<str> + 'a + ?Sized>(
fmt: S,
args: impl IntoIterator<Item = &'a T>,
) -> Vec<String> {
let (first, last) = (
fmt.as_ref().chars().next(),
fmt.as_ref().chars().next_back(),
);
if Some('[') == first && Some(']') == last {
let fmt = &fmt.as_ref()[1..fmt.as_ref().len() - 1];
args.into_iter()
.map(|s| fmt.replace("{}", s.as_ref()))
.collect::<Vec<_>>()
} else {
vec![fmt.as_ref().to_string()]
}
}
pub fn row_has_ph<'a, T: AsRef<str> + 'a>(row: impl IntoIterator<Item = &'a T>) -> bool {
row.into_iter().any(|s| s.as_ref().contains("{}"))
}
pub fn extend_row<'r, 'a, R: AsRef<str> + 'r + ?Sized, A: AsRef<str> + 'a>(
row: impl IntoIterator<Item = &'r R>,
args: &'a [A],
) -> Vec<String> {
row.into_iter()
.flat_map(|s| extend_array(s, args.iter()))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_comma_separated() {
assert_eq!(comma_separated("a,b,c"), vec!["a", "b", "c"]);
}
#[test]
fn test_program_from_arg0() {
assert_eq!(program_from_arg0("A", "/a"), "/A");
assert_eq!(program_from_arg0("A", "/b/c/a"), "/b/c/A");
assert_eq!(program_from_arg0("A", "b/c/a"), "b/c/A");
assert_eq!(program_from_arg0("A", "./c/a"), "./c/A");
assert_eq!(program_from_arg0("A", "../c/a"), "../c/A");
assert_eq!(program_from_arg0("A", "a"), "A");
}
#[test]
#[allow(clippy::useless_vec)]
fn test_rows() {
assert_eq!(
extend_array("[{}.txt]", &vec!["a", "b"]),
vec!["a.txt", "b.txt"]
);
assert_eq!(
extend_array("{}.txt", vec!["a".to_string(), "b".to_string()].iter()),
vec!["{}.txt"]
);
}
#[test]
#[allow(clippy::useless_vec)]
fn test_row_has_ph() {
assert!(row_has_ph(&vec!["a", "{}"]));
assert!(row_has_ph(&vec!["a", "{}.txt"]));
assert!(!row_has_ph(&vec!["a", "b", "c"]));
let v = vec!["a".to_string(), "b".to_string()];
assert!(!row_has_ph(v.iter()));
assert!(!row_has_ph(v.iter()));
}
#[test]
#[allow(clippy::useless_vec)]
fn test_extend_row() {
assert_eq!(
extend_row(["a", "[{}.txt]"], &["1", "2"]),
["a", "1.txt", "2.txt"]
);
assert_eq!(
extend_row(
vec!["a".to_string(), "[{}.txt]".to_string()].iter(),
&vec!["1".to_string(), "2".to_string()]
),
vec!["a", "1.txt", "2.txt"]
);
}
#[test]
fn test_ph_command() {
let pc = PhCommand {
program: "echo",
args: &["{}".to_string(), "b".to_string()],
ph: "a".to_string(),
};
assert_eq!(pc.args(), vec!["a", "b"]);
assert_eq!(pc.command_string(), "echo a b");
}
#[test]
fn test_ph_command_vec() {
let pcv = PhCommandVec::new(
"echo".to_string(),
vec!["{}".to_string(), "b".to_string()],
vec!["a".to_string(), "c".to_string()],
);
let mut iter = pcv.iter();
assert_eq!(iter.next().unwrap().command_string(), "echo a b");
assert_eq!(iter.next().unwrap().command_string(), "echo c b");
assert!(iter.next().is_none());
let pcv = PhCommandVec::new("echo", vec!["[{}.txt]", "b"], vec!["a", "c"]);
let mut iter = pcv.iter();
assert_eq!(iter.next().unwrap().command_string(), "echo a.txt c.txt b");
assert!(iter.next().is_none());
let pcv = PhCommandVec::new("echo", vec!["[{}.txt]", "{}"], vec!["a", "c"]);
let mut iter = pcv.iter();
assert_eq!(iter.next().unwrap().command_string(), "echo a.txt c.txt a");
assert_eq!(iter.next().unwrap().command_string(), "echo a.txt c.txt c");
assert!(iter.next().is_none());
}
}