diff --git a/lib/default.nix b/lib/default.nix index e6e6aaa983d5..63e02882e2c3 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -345,6 +345,7 @@ let upperChars toLower toUpper + toCamelCase toSentenceCase addContextFrom splitString diff --git a/lib/strings.nix b/lib/strings.nix index ba055bc3d22d..ba8c47b69037 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -1500,6 +1500,63 @@ rec { addContextFrom str (toUpper firstChar + toLower rest) ); + /** + Converts a string to camelCase. Handles snake_case, PascalCase, + kebab-case strings as well as strings delimited by spaces. + + # Inputs + + `string` + : The string to convert to camelCase + + # Type + + ``` + toCamelCase :: string -> string + ``` + + # Examples + :::{.example} + ## `lib.strings.toCamelCase` usage example + + ```nix + toCamelCase "hello-world" + => "helloWorld" + toCamelCase "hello_world" + => "helloWorld" + toCamelCase "hello world" + => "helloWorld" + toCamelCase "HelloWorld" + => "helloWorld" + ``` + + ::: + */ + toCamelCase = + str: + lib.throwIfNot (isString str) "toCamelCase does only accepts string values, but got ${typeOf str}" ( + let + separators = splitStringBy ( + prev: curr: + elem curr [ + "-" + "_" + " " + ] + ) false str; + + parts = lib.flatten ( + map (splitStringBy ( + prev: curr: match "[a-z]" prev != null && match "[A-Z]" curr != null + ) true) separators + ); + + first = if length parts > 0 then toLower (head parts) else ""; + rest = if length parts > 1 then map toSentenceCase (tail parts) else [ ]; + in + concatStrings (map (addContextFrom str) ([ first ] ++ rest)) + ); + /** Appends string context from string like object `src` to `target`. diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index ef2d74b63162..0d8377ec6006 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -969,6 +969,28 @@ runTests { testToSentenceCasePath = testingThrow (strings.toSentenceCase ./.); + testToCamelCase = { + expr = strings.toCamelCase "hello world"; + expected = "helloWorld"; + }; + + testToCamelCaseFromKebab = { + expr = strings.toCamelCase "hello-world"; + expected = "helloWorld"; + }; + + testToCamelCaseFromSnake = { + expr = strings.toCamelCase "hello_world"; + expected = "helloWorld"; + }; + + testToCamelCaseFromPascal = { + expr = strings.toCamelCase "HelloWorld"; + expected = "helloWorld"; + }; + + testToCamelCasePath = testingThrow (strings.toCamelCase ./.); + testToInt = testAllTrue [ # Naive (123 == toInt "123")