Repository: casey/just
Pull request #1375 (2022-10-24 19:24:06)

Add function for capitalizing strings
Hello,

This PR adds a `capitalize` function (name inspired by the Python [string method](https://docs.python.org/3/library/stdtypes.html?highlight=capitalize#str.capitalize)) for converting a string to sentence case; first letter upper case, rest lower case.

I have a use case where a function like this would be useful and it's a small enough change so I'm creating this PR without a discussion issue but it's not a big loss if it doesn't get merged.

Thanks!

casey: Good feature, and thanks for also adding a test! Very minor comment suggesting a slightly tweaked implementation.
casey: Also, could you add a note, like this to the function documentation: `<sup>master</sup>`. This is to indicate that the function is on master and not in a released version of just yet. I'll change it to the version it winds up being released in.
femnad:  Added master designation to the function documentation:

![image](https://user-images.githubusercontent.com/1352288/197663857-6e136a6a-c948-4ae7-a412-476626a44b4a.png) 


Patch:
diff --git a/README.md b/README.md
index 97c92202..098c454d 100644
--- a/README.md
+++ b/README.md
@@ -1142,6 +1142,8 @@ The executable is at: /bin/just
 
 #### String Manipulation
 
+- `capitalize(s)`<sup>master</sup> - Convert first character of `s` to uppercase and the rest to lowercase.
+
 - `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.
diff --git a/extras/just.sublime-syntax b/extras/just.sublime-syntax
index 8b0442cb..b5525a69 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)\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)\b(?=\()
       scope: entity.name.function.just
   keywords:
     - match: \b(if|else|while)\b
diff --git a/src/function.rs b/src/function.rs
index 6ea88f41..d2932f04 100644
--- a/src/function.rs
+++ b/src/function.rs
@@ -16,6 +16,7 @@ lazy_static! {
   pub(crate) static ref TABLE: BTreeMap<&'static str, Function> = vec![
     ("absolute_path", Unary(absolute_path)),
     ("arch", Nullary(arch)),
+    ("capitalize", Unary(capitalize)),
     ("clean", Unary(clean)),
     ("env_var", Unary(env_var)),
     ("env_var_or_default", Binary(env_var_or_default)),
@@ -79,6 +80,21 @@ fn arch(_context: &FunctionContext) -> Result<String, String> {
   Ok(target::arch().to_owned())
 }
 
+fn capitalize(_context: &FunctionContext, s: &str) -> Result<String, String> {
+  Ok(
+    s.chars()
+      .enumerate()
+      .flat_map(|(i, c)| {
+        if i == 0 {
+          c.to_uppercase().collect::<Vec<_>>()
+        } else {
+          c.to_lowercase().collect::<Vec<_>>()
+        }
+      })
+      .collect(),
+  )
+}
+
 fn clean(_context: &FunctionContext, path: &str) -> Result<String, String> {
   Ok(Path::new(path).lexiclean().to_str().unwrap().to_owned())
 }


Test patch:
diff --git a/tests/functions.rs b/tests/functions.rs
index 327abe7f..f2c2ae8a 100644
--- a/tests/functions.rs
+++ b/tests/functions.rs
@@ -303,6 +303,16 @@ test! {
   stderr: "echo foofoofoo\n",
 }
 
+test! {
+    name: capitalize,
+    justfile: "
+      foo:
+        echo {{ capitalize('BAR') }}
+    ",
+    stdout: "Bar\n",
+    stderr: "echo Bar\n",
+}
+
 fn assert_eval_eq(expression: &str, result: &str) {
   Test::new()
     .justfile(format!("x := {}", expression))