diff --git a/Cargo.lock b/Cargo.lock index 9358f3e..8b1b515 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,6 +316,7 @@ dependencies = [ "edit-distance", "env_logger", "executable-path", + "heck 0.4.0", "lazy_static", "lexiclean", "libc", diff --git a/Cargo.toml b/Cargo.toml index 01f9ce0..a0a06fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ derivative = "2.0.0" dotenv = "0.15.0" edit-distance = "2.0.0" env_logger = "0.9.0" +heck = "0.4.0" lazy_static = "1.0.0" lexiclean = "0.0.1" libc = "0.2.0" diff --git a/README.md b/README.md index 67823d6..2ef98bc 100644 --- a/README.md +++ b/README.md @@ -1145,12 +1145,24 @@ The executable is at: /bin/just - `capitalize(s)`master - Convert first character of `s` to uppercase and the rest to lowercase. +- `kebabcase(s)`master - Convert `s` to `kebab-case`. + +- `lowercamelcase(s)`master - Convert `s` to `lowerCamelCase`. + - `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`. +- `shoutykebabcase(s)`master - Convert `s` to `SHOUTY-KEBAB-CASE`. + +- `shoutysnakecase(s)`master - Convert `s` to `SHOUTY_SNAKE_CASE`. + +- `snakecase(s)`master - Convert `s` to `snake_case`. + +- `titlecase(s)`master - Convert `s` to `Title Case`. + - `trim(s)` - Remove leading and trailing whitespace from `s`. - `trim_end(s)` - Remove trailing whitespace from `s`. @@ -1167,6 +1179,8 @@ The executable is at: /bin/just - `uppercase(s)` - Convert `s` to uppercase. +- `uppercamelcase(s)`master - Convert `s` to `UpperCamelCase`. + #### Path Manipulation ##### Fallible diff --git a/extras/just.sublime-syntax b/extras/just.sublime-syntax index b5525a6..fc51a81 100644 --- a/extras/just.sublime-syntax +++ b/extras/just.sublime-syntax @@ -31,7 +31,7 @@ contexts: - match: '\}\}' pop: true functions: - - match: \b(arch|os|os_family|env_var|env_var_or_default|invocation_directory|justfile|justfile_directory|just_executable|lowercase|quote|replace|trim|trim_end|trim_end_match|trim_end_matches|trim_start|trim_start_match|trim_start_matches|uppercase|absolute_path|extension|file_name|file_stem|parent_directory|without_extension|join|clean|path_exists|error|sha256|sha256_file|uuid|capitalize)\b(?=\() + - match: \b(arch|os|os_family|env_var|env_var_or_default|invocation_directory|justfile|justfile_directory|just_executable|lowercase|quote|replace|trim|trim_end|trim_end_match|trim_end_matches|trim_start|trim_start_match|trim_start_matches|uppercase|absolute_path|extension|file_name|file_stem|parent_directory|without_extension|join|clean|path_exists|error|sha256|sha256_file|uuid|capitalize|uppercamelcase|lowercamelcase|snakecase|kebabcase|shoutysnakecase|titlecase|shoutykebabcase)\b(?=\() scope: entity.name.function.just keywords: - match: \b(if|else|while)\b diff --git a/src/function.rs b/src/function.rs index 445adf1..461384f 100644 --- a/src/function.rs +++ b/src/function.rs @@ -3,6 +3,11 @@ use super::*; +use heck::{ + ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase, + ToUpperCamelCase, +}; + use Function::*; pub(crate) enum Function { Nullary(fn(&FunctionContext) -> Result), @@ -29,6 +34,8 @@ lazy_static! { ("just_executable", Nullary(just_executable)), ("justfile", Nullary(justfile)), ("justfile_directory", Nullary(justfile_directory)), + ("kebabcase", Unary(kebabcase)), + ("lowercamelcase", Unary(lowercamelcase)), ("lowercase", Unary(lowercase)), ("os", Nullary(os)), ("os_family", Nullary(os_family)), @@ -38,6 +45,10 @@ lazy_static! { ("replace", Ternary(replace)), ("sha256", Unary(sha256)), ("sha256_file", Unary(sha256_file)), + ("shoutykebabcase", Unary(shoutykebabcase)), + ("shoutysnakecase", Unary(shoutysnakecase)), + ("snakecase", Unary(snakecase)), + ("titlecase", Unary(titlecase)), ("trim", Unary(trim)), ("trim_end", Unary(trim_end)), ("trim_end_match", Binary(trim_end_match)), @@ -45,6 +56,7 @@ lazy_static! { ("trim_start", Unary(trim_start)), ("trim_start_match", Binary(trim_start_match)), ("trim_start_matches", Binary(trim_start_matches)), + ("uppercamelcase", Unary(uppercamelcase)), ("uppercase", Unary(uppercase)), ("uuid", Nullary(uuid)), ("without_extension", Unary(without_extension)), @@ -225,6 +237,14 @@ fn justfile_directory(context: &FunctionContext) -> Result { }) } +fn kebabcase(_context: &FunctionContext, s: &str) -> Result { + Ok(s.to_kebab_case()) +} + +fn lowercamelcase(_context: &FunctionContext, s: &str) -> Result { + Ok(s.to_lower_camel_case()) +} + fn lowercase(_context: &FunctionContext, s: &str) -> Result { Ok(s.to_lowercase()) } @@ -283,6 +303,22 @@ fn sha256_file(context: &FunctionContext, path: &str) -> Result Ok(format!("{:x}", hash)) } +fn shoutykebabcase(_context: &FunctionContext, s: &str) -> Result { + Ok(s.to_shouty_kebab_case()) +} + +fn shoutysnakecase(_context: &FunctionContext, s: &str) -> Result { + Ok(s.to_shouty_snake_case()) +} + +fn snakecase(_context: &FunctionContext, s: &str) -> Result { + Ok(s.to_snake_case()) +} + +fn titlecase(_context: &FunctionContext, s: &str) -> Result { + Ok(s.to_title_case()) +} + fn trim(_context: &FunctionContext, s: &str) -> Result { Ok(s.trim().to_owned()) } @@ -311,6 +347,10 @@ fn trim_start_matches(_context: &FunctionContext, s: &str, pat: &str) -> Result< Ok(s.trim_start_matches(pat).to_owned()) } +fn uppercamelcase(_context: &FunctionContext, s: &str) -> Result { + Ok(s.to_upper_camel_case()) +} + fn uppercase(_context: &FunctionContext, s: &str) -> Result { Ok(s.to_uppercase()) } diff --git a/tests/functions.rs b/tests/functions.rs index f2c2ae8..6a7ae0c 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -283,6 +283,76 @@ test! { stderr: "echo bar\n", } +test! { + name: uppercamelcase, + justfile: " + foo: + echo {{ uppercamelcase('foo bar') }} + ", + stdout: "FooBar\n", + stderr: "echo FooBar\n", +} + +test! { + name: lowercamelcase, + justfile: " + foo: + echo {{ lowercamelcase('foo bar') }} + ", + stdout: "fooBar\n", + stderr: "echo fooBar\n", +} + +test! { + name: snakecase, + justfile: " + foo: + echo {{ snakecase('foo bar') }} + ", + stdout: "foo_bar\n", + stderr: "echo foo_bar\n", +} + +test! { + name: kebabcase, + justfile: " + foo: + echo {{ kebabcase('foo bar') }} + ", + stdout: "foo-bar\n", + stderr: "echo foo-bar\n", +} + +test! { + name: shoutysnakecase, + justfile: " + foo: + echo {{ shoutysnakecase('foo bar') }} + ", + stdout: "FOO_BAR\n", + stderr: "echo FOO_BAR\n", +} + +test! { + name: titlecase, + justfile: " + foo: + echo {{ titlecase('foo bar') }} + ", + stdout: "Foo Bar\n", + stderr: "echo Foo Bar\n", +} + +test! { + name: shoutykebabcase, + justfile: " + foo: + echo {{ shoutykebabcase('foo bar') }} + ", + stdout: "FOO-BAR\n", + stderr: "echo FOO-BAR\n", +} + test! { name: trim, justfile: "