1use std::path::PathBuf;
9
10pub fn quote(s: &str) -> String {
14 if cfg!(windows) {
15 return format!("\"{s}\"");
16 }
17
18 let mut out = String::with_capacity(s.len() + 2);
19 out.push('\'');
20 for ch in s.chars() {
21 if ch == '\'' {
22 out.push_str("'\\''");
23 } else {
24 out.push(ch);
25 }
26 }
27 out.push('\'');
28 out
29}
30
31pub fn invocation(bin: &str, config_paths: &[PathBuf], account: Option<&str>) -> String {
39 let mut invocation = String::from(bin);
40
41 for path in config_paths {
42 invocation.push_str(" -c ");
43 invocation.push_str("e(&path.to_string_lossy()));
44 }
45
46 if let Some(name) = account.filter(|name| !name.is_empty()) {
47 invocation.push_str(" -a ");
48 invocation.push_str("e(name));
49 }
50
51 invocation
52}
53
54#[cfg(test)]
55mod tests {
56 use std::path::PathBuf;
57
58 use crate::shell;
59
60 #[test]
61 #[cfg(not(windows))]
62 fn quote_escapes_embedded_quotes() {
63 assert_eq!(shell::quote("plain"), "'plain'");
64 assert_eq!(shell::quote("a;b"), "'a;b'");
65 assert_eq!(shell::quote("it's"), "'it'\\''s'");
66 }
67
68 #[test]
69 #[cfg(not(windows))]
70 fn invocation_carries_the_account_and_config() {
71 assert_eq!(shell::invocation("app", &[], None), "app");
72 assert_eq!(shell::invocation("app", &[], Some("")), "app");
73 assert_eq!(shell::invocation("app", &[], Some("mail")), "app -a 'mail'");
74
75 let paths = [PathBuf::from("/a.toml"), PathBuf::from("/b c.toml")];
76 assert_eq!(
77 shell::invocation("app", &paths, Some("work's")),
78 "app -c '/a.toml' -c '/b c.toml' -a 'work'\\''s'"
79 );
80 }
81}