Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add strings lib #8

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
permalink: /
---

# package xtd
# xtd

```jsonnet
local xtd = import "github.com/jsonnet-libs/xtd/main.libsonnet"
Expand All @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does camelcase deliver the same result here?

If so, we might want to deprecate the original one. To do so, we can remove the docs and and perhap provide use strings.camelcase there instead to keep it backwards compatible for libraries depending on it.

* [inspect](inspect.md)
* [strings](strings.md)
* [url](url.md)
90 changes: 90 additions & 0 deletions docs/strings.md
Original file line number Diff line number Diff line change
@@ -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"`
1 change: 1 addition & 0 deletions main.libsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
}
127 changes: 127 additions & 0 deletions strings.libsonnet
Original file line number Diff line number Diff line change
@@ -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),
Comment on lines +12 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are already covered in the ascii package, perhaps we can reference those instead of redefining them here.



'#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`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Function alias: `camelcase`
Function alias: `camelize`

|||,
[
d.arg('str', d.T.string),
d.arg('lower', d.T.boolean, default=false),
]
),
camelize(w,lower=false): self.camelcase(w, lower),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can leave out the function arguments here, it gets inherited.

Suggested change
camelize(w,lower=false): self.camelcase(w, lower),
camelize: self.camelcase,

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`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Function aliases: `dasherize`, `kebabcase`
Function aliases: `dasherize`, `kebabize`

|||,
[
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`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Function aliases: `underscore`, `snakecase`
Function aliases: `underscore`, `snakize`

|||,
[
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),

}
45 changes: 45 additions & 0 deletions test.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -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