fallow_engine/
shell_quote.rs1#[must_use]
9pub fn shell_quote(word: &str) -> String {
10 if cfg!(windows) {
11 windows_quote(word)
12 } else {
13 posix_quote(word)
14 }
15}
16
17#[must_use]
20pub fn posix_quote(word: &str) -> String {
21 if is_plain(word, &[]) {
22 word.to_owned()
23 } else {
24 format!("'{}'", word.replace('\'', "'\\''"))
25 }
26}
27
28#[must_use]
39pub fn windows_quote(word: &str) -> String {
40 if is_plain(word, &['\\', ':']) {
41 word.to_owned()
42 } else if word.contains(['$', '`', '%', '!', '"']) {
43 format!("'{}'", word.replace('\'', "''"))
44 } else {
45 format!("\"{word}\"")
46 }
47}
48
49fn is_plain(word: &str, extra: &[char]) -> bool {
50 !word.is_empty()
51 && word.chars().all(|c| {
52 c.is_ascii_alphanumeric()
53 || matches!(c, '_' | '-' | '.' | '/' | '@' | '+' | '=')
54 || extra.contains(&c)
55 })
56}
57
58#[cfg(test)]
59mod tests {
60 use super::{posix_quote, windows_quote};
61
62 #[test]
63 fn posix_quote_keeps_plain_words_and_quotes_the_rest() {
64 assert_eq!(
65 posix_quote("baselines/dead-code.json"),
66 "baselines/dead-code.json"
67 );
68 assert_eq!(posix_quote("--root=/srv/app"), "--root=/srv/app");
69 assert_eq!(posix_quote("a b.json"), "'a b.json'");
70 assert_eq!(posix_quote("x;rm -rf ~"), "'x;rm -rf ~'");
71 assert_eq!(posix_quote("a|b&c"), "'a|b&c'");
72 assert_eq!(posix_quote("$(id)"), "'$(id)'");
73 assert_eq!(posix_quote("`id`"), "'`id`'");
74 assert_eq!(posix_quote("it's"), "'it'\\''s'");
75 assert_eq!(posix_quote(""), "''");
76 }
77
78 #[test]
79 fn windows_quote_keeps_paths_and_quotes_shell_characters() {
80 assert_eq!(
81 windows_quote(r"C:\tools\fallow-mcp.exe"),
82 r"C:\tools\fallow-mcp.exe"
83 );
84 assert_eq!(
85 windows_quote(r"C:\Program Files\nodejs\npx.cmd"),
86 r#""C:\Program Files\nodejs\npx.cmd""#
87 );
88 assert_eq!(windows_quote("a&b"), "\"a&b\"");
89 assert_eq!(windows_quote("x;y|z"), "\"x;y|z\"");
90 assert_eq!(windows_quote("say \"hi\""), "'say \"hi\"'");
91 assert_eq!(windows_quote("$(calc)"), "'$(calc)'");
92 assert_eq!(windows_quote("`calc`"), "'`calc`'");
93 assert_eq!(windows_quote("%PATH%"), "'%PATH%'");
94 assert_eq!(windows_quote("it's $x"), "'it''s $x'");
95 assert_eq!(windows_quote(""), "\"\"");
96 }
97}