From a5f69c51500a942ea9da9e224d2e2bf54d3d1876 Mon Sep 17 00:00:00 2001 From: Cicatrice Date: Fri, 24 Feb 2023 14:14:34 +0100 Subject: [PATCH] feat: add strings lib --- docs/README.md | 5 +- docs/strings.md | 90 ++++++++++++++++++++++++++++++++ main.libsonnet | 1 + strings.libsonnet | 127 ++++++++++++++++++++++++++++++++++++++++++++++ test.jsonnet | 45 ++++++++++++++++ 5 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 docs/strings.md create mode 100644 strings.libsonnet diff --git a/docs/README.md b/docs/README.md index 4dd9852..e95bd1e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,7 +2,7 @@ permalink: / --- -# package xtd +# xtd ```jsonnet local xtd = import "github.com/jsonnet-libs/xtd/main.libsonnet" @@ -14,10 +14,9 @@ This package serves as a test field for functions intended to be contributed to in the future, but also provides a place for less general, yet useful utilities. -## Subpackages - * [aggregate](aggregate.md) * [ascii](ascii.md) * [camelcase](camelcase.md) * [inspect](inspect.md) +* [strings](strings.md) * [url](url.md) \ No newline at end of file diff --git a/docs/strings.md b/docs/strings.md new file mode 100644 index 0000000..683039a --- /dev/null +++ b/docs/strings.md @@ -0,0 +1,90 @@ +--- +permalink: /strings/ +--- + +# strings + +```jsonnet +local strings = import "github.com/jsonnet-libs/xtd/strings.libsonnet" +``` + +`strings` implements few helpers functions to manipulate strings. + + +## Index + +* [`fn camelcase(str, lower=false)`](#fn-camelcase) +* [`fn kebabcase(str, caps=false)`](#fn-kebabcase) +* [`fn screamingsnakecase(str)`](#fn-screamingsnakecase) +* [`fn snakecase(str, caps=false)`](#fn-snakecase) +* [`fn traincase(str)`](#fn-traincase) + +## Fields + +### fn camelcase + +```ts +camelcase(str, lower=false) +``` + +`camelcase` turns `str` string into its camelcase version. +By default, first letter will be capitalized, use `lower=true` to keep it down case. +E.g. +* `camelcase("A simple string")` → `"ASimpleString"` +* `camelcase("foo_bar",lower=true)` → `"fooBar"` +* `camelcase("+++More symbols!!!")` → `"MoreSymbols"` + +Function alias: `camelcase` + + +### fn kebabcase + +```ts +kebabcase(str, caps=false) +``` + +`kebabcase` turns `str` string into its kebab version. +By default, all letters are down-cases ; use `caps=true` to have them all capitalized. +E.g. +* `kebabcase("A simple string")` → `"a-simple-string"` +* `kebabcase("FooBar")` → `"foo-bar"` +* `kebabcase("+++More symbols!!!")` → `"more-symbols"` + +Function aliases: `dasherize`, `kebabcase` + + +### fn screamingsnakecase + +```ts +screamingsnakecase(str) +``` + +`screamingsnakecase` turns `str` string into its screaming snake version. +E.g. +* `screamingsnakecase("A simple string")` → `"A_SIMPLE_STRING"` + + +### fn snakecase + +```ts +snakecase(str, caps=false) +``` + +`snakecase` turns `str` string into its snake version. +E.g. +* `snakecase("A simple string")` → `"a_simple_string"` +* `snakecase("FooBar")` → `"foo_bar"` +* `snakecase("+++More symbols!!!",caps=true)` → `"MORE_SYMBOLS"` + +Function aliases: `underscore`, `snakecase` + + +### fn traincase + +```ts +traincase(str) +``` + +`traincase` turns `str` string into its train case version. +E.g. +* `traincase("A simple string")` → `"A-SIMPLE-STRING"` diff --git a/main.libsonnet b/main.libsonnet index 87a9c72..8f0cebb 100644 --- a/main.libsonnet +++ b/main.libsonnet @@ -17,4 +17,5 @@ local d = import 'doc-util/main.libsonnet'; camelcase: (import './camelcase.libsonnet'), inspect: (import './inspect.libsonnet'), url: (import './url.libsonnet'), + strings: (import './strings.libsonnet'), } diff --git a/strings.libsonnet b/strings.libsonnet new file mode 100644 index 0000000..13d858e --- /dev/null +++ b/strings.libsonnet @@ -0,0 +1,127 @@ +local d = import 'doc-util/main.libsonnet'; + +{ +'#': d.pkg( + name='strings', + url='github.com/jsonnet-libs/xtd/strings.libsonnet', + help=||| + `strings` implements few helpers functions to manipulate strings. + ||| + ), + + isUpcase(c):: std.codepoint(c) >= 65 && std.codepoint(c) <= 90, // between A & Z + isDowncase(c):: std.codepoint(c) >= 97 && std.codepoint(c) <= 122, // between a & z + isDigit(c) :: std.codepoint(c) >= 48 && std.codepoint(c) <= 57, // between 0 & 9 + isAlphaNum(c):: self.isUpcase(c) || self.isDowncase(c) || self.isDigit(c), + + + '#camelcase':: d.fn( + ||| + `camelcase` turns `str` string into its camelcase version. + By default, first letter will be capitalized, use `lower=true` to keep it down case. + E.g. + * `camelcase("A simple string")` → `"ASimpleString"` + * `camelcase("foo_bar",lower=true)` → `"fooBar"` + * `camelcase("+++More symbols!!!")` → `"MoreSymbols"` + + Function alias: `camelcase` + |||, + [ + d.arg('str', d.T.string), + d.arg('lower', d.T.boolean, default=false), + ] + ), + camelize(w,lower=false): self.camelcase(w, lower), + camelcase(str,lower=false): + if std.length(str) == 0 + then "" + else + local wcs = std.stringChars(str); + local camcs = std.filter(function(x) std.length(x) == 1 && self.isAlphaNum(x), [ + if self.isAlphaNum(wcs[i]) + then if i!=0 && self.isAlphaNum(wcs[i-1]) + then wcs[i] + else std.asciiUpper(wcs[i]) + else "" + + for i in std.range(0,std.length(wcs)-1) + ]); + if std.length(camcs) == 0 + then "" + else if lower + then std.join("",[std.asciiLower(camcs[0])] + camcs[1:]) + else std.join("",[std.asciiUpper(camcs[0])] + camcs[1:]) + , + + '#kebabcase':: d.fn( + ||| + `kebabcase` turns `str` string into its kebab version. + By default, all letters are down-cases ; use `caps=true` to have them all capitalized. + E.g. + * `kebabcase("A simple string")` → `"a-simple-string"` + * `kebabcase("FooBar")` → `"foo-bar"` + * `kebabcase("+++More symbols!!!")` → `"more-symbols"` + + Function aliases: `dasherize`, `kebabcase` + |||, + [ + d.arg('str', d.T.string), + d.arg('caps', d.T.boolean, default=false ), + ] + ), + dasherize(str,caps=false): self.kebabcase(str,caps), + kebabize(str,caps=false): self.kebabcase(str,caps), + kebabcase(str,caps=false): std.join("-",std.filter(function(x) std.length(x)>0,std.split(std.join("",std.flattenArrays([ + if self.isDowncase(c) || self.isDigit(c) + then [c] + else if self.isUpcase(c) + then ['-',if caps then std.asciiUpper(c) else std.asciiLower(c)] + else ["-"] + for c in std.stringChars(str)])),"-"))), + + '#traincase':: d.fn( + ||| + `traincase` turns `str` string into its train case version. + E.g. + * `traincase("A simple string")` → `"A-SIMPLE-STRING"` + |||, + [ + d.arg('str', d.T.string), + ] + ), + traincase(str): self.kebabcase(str,caps=true), + + + '#snakecase':: d.fn( + ||| + `snakecase` turns `str` string into its snake version. + By default, all letters are down-cases ; use `caps=true` to have them all capitalized. + E.g. + * `snakecase("A simple string")` → `"a_simple_string"` + * `snakecase("FooBar")` → `"foo_bar"` + * `snakecase("+++More symbols!!!",caps=true)` → `"MORE_SYMBOLS"` + + Function aliases: `underscore`, `snakecase` + |||, + [ + d.arg('str', d.T.string), + d.arg('caps', d.T.boolean, default=false ), + ] + ), + underscore(str,caps=false): self.snakecase(str,caps), + snakize(str,caps=false): self.snakecase(str,caps), + snakecase(str,caps=false): std.strReplace(self.kebabcase(str,caps), "-", "_"), + + '#screamingsnakecase':: d.fn( + ||| + `screamingsnakecase` turns `str` string into its screaming snake version. + E.g. + * `screamingsnakecase("A simple string")` → `"A_SIMPLE_STRING"` + |||, + [ + d.arg('str', d.T.string), + ] + ), + screamingsnakecase(str): self.snakecase(str, caps=true), + +} \ No newline at end of file diff --git a/test.jsonnet b/test.jsonnet index 410d37d..7be7aa3 100644 --- a/test.jsonnet +++ b/test.jsonnet @@ -170,9 +170,54 @@ local TestInspect = : name('maxRecursionDepth'); true; +local TestStrings = + local name(case) = 'TestStrings:%s failed' % case; + // Tests for camelcase + assert xtd.strings.camelcase("") == "" + : name('camelcase an empty string'); + assert xtd.strings.camelcase("-") == "" + : name('camelcase a single dash'); + assert xtd.strings.camelcase("camel-this") == "CamelThis" + : name('camel-this'); + assert xtd.strings.camelcase("camel this") == "CamelThis" + : name('camel this'); + assert xtd.strings.camelcase("camelThis") == "CamelThis" + : name('camelThis'); + assert xtd.strings.camelcase("--camel---this") == "CamelThis" + : name('--camel---this'); + assert xtd.strings.camelcase("--camel---this",lower=true) == "camelThis" + : name('--camel---this lower'); + assert xtd.strings.camelcase(" camel this_*(*&_") == "CamelThis" + : name(' camel this_*(*&_'); + // Tests for kebabcase + assert xtd.strings.kebabcase("") == "" + : name('kebabcase an empty string'); + assert xtd.strings.kebabcase("-") == "" + : name('kebabcase a single dash'); + assert xtd.strings.kebabcase("KebabThis") == "kebab-this" + : name('KebabThis'); + assert xtd.strings.kebabcase("KebabAVeryVeryAnnoyingCamelcasedStringSoMeTiMeSRaNd0mElYWithNumb3r5") + == "kebab-a-very-very-annoying-camelcased-string-so-me-ti-me-s-ra-nd0m-el-y-with-numb3r5" + : name('kebabcaseAnnoyingString'); + assert xtd.strings.kebabcase("!!!Kebab!This_*(*&_") == "kebab-this" + : name('!!!Kebab!This_*(*&_'); + // Tests for snakecase + assert xtd.strings.snakecase("") == "" + : name('snakecase an empty string'); + assert xtd.strings.snakecase("-") == "" + : name('snakecase a single dash'); + assert xtd.strings.snakecase("-") == "" + : name('snakecase a single dash'); + assert xtd.strings.snakecase("a-_-_-b-_-_-c") == "a_b_c" + : name('snakecase a-b-c'); + assert xtd.strings.snakecase("SnakeAVeryVeryAnnoyingCamelcasedStringSoMeTiMeSRaNd0mElYWithNumb3r5") + == "snake_a_very_very_annoying_camelcased_string_so_me_ti_me_s_ra_nd0m_el_y_with_numb3r5" + : name('snakecaseAnnoyingString'); + true; true && TestEscapeString && TestEncodeQuery && TestCamelCaseSplit && TestInspect +&& TestStrings