Skip to main content

anodizer_core/
shell.rs

1//! POSIX shell quoting.
2
3/// Wrap `v` in POSIX single quotes so a shell reads it as exactly one literal
4/// word.
5///
6/// Inside single quotes a POSIX shell performs no expansion at all, so `$`,
7/// backticks, `"`, whitespace and newlines pass through untouched. The one
8/// character that cannot appear is `'` itself; each is emitted as `'\''` —
9/// close the quote, supply an escaped quote, reopen — which is why the result
10/// is always quoted rather than quoted-if-needed.
11///
12/// ```
13/// use anodizer_core::shell::shell_single_quote;
14/// assert_eq!(shell_single_quote("it's"), r"'it'\''s'");
15/// ```
16pub fn shell_single_quote(v: &str) -> String {
17    format!("'{}'", v.replace('\'', r"'\''"))
18}
19
20#[cfg(test)]
21mod tests {
22    use super::*;
23
24    #[test]
25    fn single_quote_escapes_embedded_quote() {
26        assert_eq!(shell_single_quote("foo"), "'foo'");
27        assert_eq!(shell_single_quote("foo's"), r"'foo'\''s'");
28        assert_eq!(shell_single_quote(""), "''");
29        assert_eq!(shell_single_quote(r#"a "b" c"#), r#"'a "b" c'"#);
30        assert_eq!(shell_single_quote("$(rm -rf /)"), "'$(rm -rf /)'");
31        assert_eq!(shell_single_quote("$HOME `id`"), "'$HOME `id`'");
32        assert_eq!(shell_single_quote("a\nb"), "'a\nb'");
33    }
34}