Skip to main content

qemu_command_builder/
to_command.rs

1use std::fmt::Debug;
2use std::hash::Hash;
3use std::str::FromStr;
4
5fn shell_escape_arg(arg: &str) -> String {
6    if !arg.is_empty() && arg.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | '.' | '/' | ':' | ',' | '=' | '+')) {
7        return arg.to_string();
8    }
9
10    let escaped = arg.replace('\'', r#"'\''"#);
11    format!("'{}'", escaped)
12}
13
14pub trait ToCommand: Debug + Clone + Hash + Ord + PartialOrd + Eq + PartialEq + FromStr {
15    fn has_args(&self) -> bool {
16        true
17    }
18
19    /// Convert to a type suitable to pass to [`std::process::Command::new()`]
20    fn command(&self) -> String {
21        String::new()
22    }
23
24    /// Convert to a type suitable to pass to [`std::process::Command::args()`]
25    fn to_args(&self) -> Vec<String>;
26
27    /// Construct the full command in keep in pieces
28    fn to_command(&self) -> Vec<String> {
29        if self.has_args() {
30            let mut cmd = vec![self.command()];
31            cmd.append(&mut self.to_args());
32            cmd
33        } else {
34            vec![]
35        }
36    }
37
38    /// Construct the full command as a single [`String`]
39    fn to_single_command(&self) -> String {
40        self.to_command().iter().map(|arg| shell_escape_arg(arg)).collect::<Vec<_>>().join(" ")
41    }
42
43    /// Construct only the args as a single [`String`]
44    fn to_single_arg(&self) -> String {
45        self.to_args().iter().map(|arg| shell_escape_arg(arg)).collect::<Vec<_>>().join(" ")
46    }
47}
48
49pub trait ToArg {
50    fn to_arg(&self) -> &str;
51}
52
53// Quick Add Optional
54#[macro_export]
55macro_rules! qao {
56    ($a:expr,$b:expr,$c:expr) => {{
57        if let Some(val) = $a {
58            $b.push(format!("{}{}", $c, val));
59        }
60    }};
61}
62
63// Quick Add Optional OsString
64#[macro_export]
65macro_rules! qaoo {
66    ($a:expr,$b:expr,$c:expr) => {
67        if let Some(val) = $a {
68            match val.clone().into_string() {
69                Ok(str) => $b.push(format!("{}{}", $c, str)),
70                Err(x) => panic!("can't convert {:?} to a string", x),
71            }
72        }
73    };
74}
75
76// Quick Add Optional Path
77#[macro_export]
78macro_rules! qaop {
79    ($a:expr,$b:expr,$c:expr) => {{
80        if let Some(val) = $a {
81            $b.push(format!("{}{}", $c, val.display()));
82        }
83    }};
84}
85
86// Parse Custom (Type) Option
87#[macro_export]
88macro_rules! pco {
89    ($a:ident,$b:expr,$c:ty,$d:expr) => {
90        fn $a(s: &mut &str) -> ModalResult<$c> {
91            let _ = literal(DELIM_COMMA).parse_next(s)?;
92            let _ = literal($d).parse_next(s)?;
93            let parsed = $b.parse_to::<$c>().parse_next(s)?;
94            Ok(parsed)
95        }
96    };
97}
98// Parse Custom (Type) Option with optional leading comma
99#[macro_export]
100macro_rules! pco0 {
101    ($a:ident,$b:expr,$c:ty,$d:expr) => {
102        fn $a(s: &mut &str) -> ModalResult<$c> {
103            let _ = opt(literal(DELIM_COMMA)).parse_next(s)?;
104            let _ = literal($d).parse_next(s)?;
105            let parsed = $b.parse_to::<$c>().parse_next(s)?;
106            Ok(parsed)
107        }
108    };
109}
110// Parse Primitive (Type) Option
111#[macro_export]
112macro_rules! ppo {
113    ($a:ident,$b:expr,$c:ty,$d:expr) => {
114        fn $a(s: &mut &str) -> ModalResult<$c> {
115            let _ = literal(DELIM_COMMA).parse_next(s)?;
116            let _ = literal($d).parse_next(s)?;
117            let parsed = $b.parse_next(s)?;
118            Ok(parsed)
119        }
120    };
121}
122
123// Parse Primitive (Type) Option with optional leading comma
124#[macro_export]
125macro_rules! ppo0 {
126    ($a:ident,$b:expr,$c:ty,$d:expr) => {
127        fn $a(s: &mut &str) -> ModalResult<$c> {
128            let _ = opt(literal(DELIM_COMMA)).parse_next(s)?;
129            let _ = literal($d).parse_next(s)?;
130            let parsed = $b.parse_next(s)?;
131            Ok(parsed)
132        }
133    };
134}
135
136// Parse String (Type) Option
137#[macro_export]
138macro_rules! pso {
139    ($a:ident,$b:expr) => {
140        fn $a(s: &mut &str) -> ModalResult<ShellString> {
141            let _ = literal(DELIM_COMMA).parse_next(s)?;
142            let _ = literal($b).parse_next(s)?;
143            let parsed = ascii_plus_more.parse_next(s)?;
144            Ok(ShellString { s: parsed.to_string() })
145        }
146    };
147}
148
149// Parse String (Type) Option with optional leading comma
150#[macro_export]
151macro_rules! pso0 {
152    ($a:ident,$b:expr) => {
153        fn $a(s: &mut &str) -> ModalResult<ShellString> {
154            let _ = opt(literal(DELIM_COMMA)).parse_next(s)?;
155            let _ = literal($b).parse_next(s)?;
156            let parsed = ascii_plus_more.parse_next(s)?;
157            Ok(ShellString { s: parsed.to_string() })
158        }
159    };
160}