sandbox_runtime/utils/
shell.rs1pub fn quote(s: &str) -> String {
6 if s.is_empty() {
8 return "''".to_string();
9 }
10
11 if !needs_quoting(s) {
13 return s.to_string();
14 }
15
16 format!("'{}'", s.replace('\'', "'\"'\"'"))
20}
21
22pub fn quote_always(s: &str) -> String {
24 format!("'{}'", s.replace('\'', "'\"'\"'"))
25}
26
27fn needs_quoting(s: &str) -> bool {
29 s.chars().any(|c| {
30 matches!(
31 c,
32 ' ' | '\t'
33 | '\n'
34 | '\r'
35 | '"'
36 | '\''
37 | '\\'
38 | '$'
39 | '`'
40 | '!'
41 | '*'
42 | '?'
43 | '['
44 | ']'
45 | '{'
46 | '}'
47 | '('
48 | ')'
49 | '<'
50 | '>'
51 | '|'
52 | '&'
53 | ';'
54 | '#'
55 | '~'
56 )
57 })
58}
59
60pub fn join_args<I, S>(args: I) -> String
62where
63 I: IntoIterator<Item = S>,
64 S: AsRef<str>,
65{
66 args.into_iter()
67 .map(|s| quote(s.as_ref()))
68 .collect::<Vec<_>>()
69 .join(" ")
70}
71
72pub fn split_args(s: &str) -> Result<Vec<String>, shell_words::ParseError> {
75 shell_words::split(s)
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn test_quote() {
84 assert_eq!(quote("simple"), "simple");
85 assert_eq!(quote("with space"), "'with space'");
86 assert_eq!(quote("it's"), "'it'\"'\"'s'");
87 assert_eq!(quote(""), "''");
88 assert_eq!(quote("$var"), "'$var'");
89 }
90
91 #[test]
92 fn test_join_args() {
93 let args = vec!["echo", "hello world", "it's"];
94 assert_eq!(join_args(args), "echo 'hello world' 'it'\"'\"'s'");
95 }
96
97 #[test]
98 fn test_split_args() {
99 let args = split_args("echo 'hello world' test").unwrap();
100 assert_eq!(args, vec!["echo", "hello world", "test"]);
101 }
102}