humane_commands/
opt_arg.rs

1use std::{ffi::OsStr, process::Command};
2
3#[cfg(test)]
4mod test {
5    use std::process::Command;
6
7    use crate::opt_arg::OptionalArgExtension as _;
8
9    #[test]
10    fn opt_arg_some() {
11        let mut cmd = Command::new("echo");
12        cmd.opt_arg(Some("test"));
13        assert_eq!(cmd.get_args().into_iter().collect::<Vec<_>>(), ["test"]);
14    }
15    #[test]
16    fn opt_arg_none() {
17        let mut cmd = Command::new("echo");
18        cmd.arg("test").opt_arg(Option::<&str>::None);
19        assert_eq!(cmd.get_args().into_iter().collect::<Vec<_>>(), ["test"]);
20    }
21    #[test]
22    fn opt_arg_flag_some() {
23        let mut cmd = Command::new("echo");
24        cmd.opt_arg(("foo", Some("bar")));
25        assert_eq!(
26            cmd.get_args().into_iter().collect::<Vec<_>>(),
27            ["foo", "bar"]
28        );
29    }
30    #[test]
31    fn opt_arg_flag_none() {
32        let mut cmd = Command::new("echo");
33        cmd.arg("test").opt_arg(("foo", Option::<&str>::None));
34        assert_eq!(cmd.get_args().into_iter().collect::<Vec<_>>(), ["test"]);
35    }
36}
37
38pub trait OptionalArgExtension<T> {
39    /// If T is an Option<Val>, only include this argument if it is `Some(Val)`
40    /// 
41    /// If T is a (Name, Option<Val>) or (Name, IntoIter<Item = Val>), the Name will also only be included if the Option is 
42    /// Some(Val) or there is at least one item in the iterator
43    /// 
44    /// ## Examples
45    /// 
46    /// ```
47    /// # use std::process::Command;
48    /// # use humane_commands::opt_arg::OptionalArgExtension;
49    /// let maybe = Some("test");
50    /// Command::new("echo").opt_arg(maybe).status();
51    /// ```
52    /// 
53    /// Only include the "-c" if there is a command to run
54    /// ```
55    /// # use std::process::Command;
56    /// # use humane_commands::opt_arg::OptionalArgExtension;
57    /// let maybe_flag = ("-c", Some("emacs"));
58    /// Command::new("bash").opt_arg(maybe_flag).status();
59    /// ```
60    /// ```
61    /// # use std::process::Command;
62    /// # use humane_commands::opt_arg::OptionalArgExtension;
63    /// let maybe_flag = ("-c", ["echo", "some", "stuff"]);
64    /// Command::new("bash").opt_arg(maybe_flag).status();
65    /// ```
66    fn opt_arg(&mut self, val: T) -> &mut Self;
67}
68
69impl<Value: AsRef<OsStr>> OptionalArgExtension<Option<Value>> for Command {
70    fn opt_arg(&mut self, val: Option<Value>) -> &mut Self {
71        if let Some(value) = val {
72            self.arg(value);
73        }
74        self
75    }
76}
77
78
79impl<Name: AsRef<OsStr>, Value: AsRef<OsStr>, I: IntoIterator<Item = Value>> OptionalArgExtension<(Name, I)>
80    for Command
81{
82    fn opt_arg(&mut self, val: (Name, I)) -> &mut Self {
83        let (flag, i) = val;
84        let mut i = i.into_iter();
85        if let Some(value) = i.next() {
86            self.arg(flag.as_ref()).arg(value.as_ref());
87            self.args(i);
88        }
89        self
90    }
91}