diff --git a/README.adoc b/README.adoc index 9a405cb..5496a32 100644 --- a/README.adoc +++ b/README.adoc @@ -888,6 +888,7 @@ The executable is at: /bin/just ==== String Manipulation - `lowercase(s)` - Convert `s` to lowercase. +- `quote(s)` - Replace all single quotes with `'\''` and prepend and append single quotes to `s`. This is sufficient to escape special characters for many shells, including most Bourne shell descendants. - `replace(s, from, to)` - Replace all occurrences of `from` in `s` to `to`. - `trim(s)` - Remove leading and trailing whitespace from `s`. - `trim_end(s)` - Remove trailing whitespace from `s`. diff --git a/src/function.rs b/src/function.rs index db97220..5232814 100644 --- a/src/function.rs +++ b/src/function.rs @@ -27,6 +27,7 @@ lazy_static! { ("os", Nullary(os)), ("os_family", Nullary(os_family)), ("parent_directory", Unary(parent_directory)), + ("quote", Unary(quote)), ("replace", Ternary(replace)), ("trim", Unary(trim)), ("trim_end", Unary(trim_end)), @@ -206,6 +207,10 @@ fn parent_directory(_context: &FunctionContext, path: &str) -> Result Result { + Ok(format!("'{}'", s.replace('\'', "'\\''"))) +} + fn replace(_context: &FunctionContext, s: &str, from: &str, to: &str) -> Result { Ok(s.replace(from, to)) } diff --git a/tests/lib.rs b/tests/lib.rs index fe9819a..250eeaf 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -26,6 +26,7 @@ mod invocation_directory; mod misc; mod positional_arguments; mod quiet; +mod quote; mod readme; mod regexes; mod search; diff --git a/tests/quote.rs b/tests/quote.rs new file mode 100644 index 0000000..4cbdc94 --- /dev/null +++ b/tests/quote.rs @@ -0,0 +1,43 @@ +use crate::common::*; + +#[test] +fn single_quotes_are_prepended_and_appended() { + Test::new() + .justfile( + " + x := quote('abc') + ", + ) + .args(&["--evaluate", "x"]) + .stdout("'abc'") + .run(); +} + +#[test] +fn quotes_are_escaped() { + Test::new() + .justfile( + r#" + x := quote("'") + "#, + ) + .args(&["--evaluate", "x"]) + .stdout(r"''\'''") + .run(); +} + +#[test] +fn quoted_strings_can_be_used_as_arguments() { + Test::new() + .justfile( + r#" + file := quote("foo ' bar") + + @foo: + touch {{ file }} + ls -1 + "#, + ) + .stdout("foo ' bar\njustfile\n") + .run(); +}