mkargs/
lib.rs

1/*!
2Build command arguments.
3
4```
5use std::process::Command;
6use mkargs::mkargs;
7
8let world = "world".to_string();
9let mut args = mkargs!["hello", world];
10
11args.push_pair("foo", "bar");
12
13Command::new("echo")
14    .args(&*args)
15    .spawn()
16    .expect("echo command failed to start");
17```
18*/
19use std::{ffi::OsString, fmt::{Display, self}, ops::Deref};
20
21/// Represents list of command arguments.
22#[derive(Debug, Default, Clone)]
23pub struct MkArgs(Vec<OsString>);
24
25impl MkArgs {
26    /// Crates empty list of arguments.
27    pub fn new() -> MkArgs {
28        Default::default()
29    }
30
31    /// Appends argument returning modified Self.
32    pub fn with(mut self, arg: impl Into<OsString>) -> Self {
33        self.push(arg);
34        self
35    }
36
37    /// Appends pair of arguments returning modified Self.
38    pub fn with_pair(mut self, arg1: impl Into<OsString>, arg2: impl Into<OsString>) -> Self {
39        self.push_pair(arg1, arg2);
40        self
41    }
42
43    /// Appends another list of arguments returning modified Self.
44    pub fn with_append(mut self, args: MkArgs) -> Self {
45        self.append(args);
46        self
47    }
48
49    /// Appends argument if condition is true returning modified Self.
50    pub fn with_cond(mut self, arg: impl Into<OsString>, cond: bool) -> Self {
51        if cond {
52            self.push(arg);
53        }
54        self
55    }
56
57    /// Appends pair of arguments if condition is true returning modified Self.
58    pub fn with_pair_cond(mut self, arg1: impl Into<OsString>, arg2: impl Into<OsString>, cond: bool) -> Self {
59        if cond {
60            self.push_pair(arg1, arg2);
61        }
62        self
63    }
64
65    /// Appends another list of arguments if condition is true returning modified Self.
66    pub fn with_append_cond(mut self, args: MkArgs, cond: bool) -> Self {
67        if cond {
68            self.append(args);
69        }
70        self
71    }
72
73    /// Appends argument.
74    pub fn push(&mut self, arg: impl Into<OsString>) {
75        self.0.push(arg.into())
76    }
77
78    /// Appends pair of arguments.
79    pub fn push_pair(&mut self, arg1: impl Into<OsString>, arg2: impl Into<OsString>) {
80        self.0.push(arg1.into());
81        self.0.push(arg2.into());
82    }
83
84    /// Appends another list of arguments.
85    pub fn append(&mut self, args: impl Into<MkArgs>) {
86        self.0.extend(args.into().0.into_iter());
87    }
88
89    /// Returns constructed argument list.
90    pub fn into_inner(self) -> Vec<OsString> {
91        self.0
92    }
93
94    /// Returns slice of constructed argument list.
95    pub fn as_slice(&self) -> &[OsString] {
96        self.0.as_slice()
97    }
98
99    /// Converts constructed argument list into vector of UTF-8 strings.
100    ///
101    /// Returns `Err(OsString)` for first argument that could not be converted to UTF-8 encoding.
102    pub fn into_strings(self) -> Result<Vec<String>, OsString> {
103        self.0.into_iter().map(|a| a.into_string()).collect()
104    }
105}
106
107impl Deref for MkArgs {
108    type Target = [OsString];
109
110    fn deref(&self) -> &Self::Target {
111        self.as_slice()
112    }
113}
114
115impl From<MkArgs> for Vec<OsString> {
116    fn from(args: MkArgs) -> Vec<OsString> {
117        args.into_inner()
118    }
119}
120
121impl TryFrom<MkArgs> for Vec<String> {
122    type Error = OsString;
123
124    fn try_from(args: MkArgs) -> Result<Vec<String>, OsString> {
125        args.into_strings()
126    }
127}
128
129impl From<Vec<OsString>> for MkArgs {
130    fn from(args: Vec<OsString>) -> MkArgs {
131        MkArgs(args)
132    }
133}
134
135impl<'i> From<&'i [OsString]> for MkArgs {
136    fn from(args: &'i [OsString]) -> MkArgs {
137        MkArgs(args.to_vec())
138    }
139}
140
141impl Display for MkArgs {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
143        write!(f, "{:?}", self.0)
144    }
145}
146
147#[macro_export]
148macro_rules! mkargs {
149    ($($arg:expr),*) => {{
150        use std::ffi::OsString;
151        let args: &[OsString] = &[$(Into::<OsString>::into($arg)),*];
152        $crate::MkArgs::from(args)
153    }};
154}
155
156#[cfg(target_family = "unix")]
157#[cfg(test)]
158mod tests {
159    use super::*;
160
161    #[test]
162    fn test_echo() {
163        use std::process::Command;
164
165        let world = "world".to_string();
166        let mut args = mkargs!["hello", world];
167
168        args.push_pair("foo", "bar");
169
170        let out = Command::new("echo")
171            .args(&*args)
172            .output()
173            .expect("echo command failed to start");
174
175        assert_eq!(out.stdout, b"hello world foo bar\n");
176    }
177}