Skip to content

Commit

Permalink
feat(stdlib): add casing functions (#973)
Browse files Browse the repository at this point in the history
* feat(stdlib): add casing functions

* feat(stdlib): add fns to bench

* feat(stdlib): fix test

* feat(stdlib): add LICENSE
  • Loading branch information
leshow authored Aug 7, 2024
1 parent f258a70 commit ea40963
Show file tree
Hide file tree
Showing 12 changed files with 661 additions and 1 deletion.
12 changes: 11 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ stdlib = [
"dep:cfb-mode",
"dep:chacha20poly1305",
"dep:charset",
"dep:convert_case",
"dep:cidr-utils",
"dep:community-id",
"dep:crypto_secretbox",
Expand Down Expand Up @@ -129,6 +130,7 @@ cidr-utils = { version = "0.6", optional = true }
csv = { version = "1", optional = true }
clap = { version = "4", features = ["derive"], optional = true }
codespan-reporting = {version = "0.11", optional = true }
convert_case = { version = "0.6.0", optional = true }
data-encoding = { version = "2", optional = true }
digest = { version = "0.10", optional = true }
dyn-clone = { version = "1", default-features = false, optional = true }
Expand Down
1 change: 1 addition & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ combine,https://github.com/Marwes/combine,MIT,Markus Westerlind <marwes91@gmail.
community-id,https://github.com/traceflight/rs-community-id,MIT OR Apache-2.0,Julian Wang <[email protected]>
concurrent-queue,https://github.com/smol-rs/concurrent-queue,Apache-2.0 OR MIT,"Stjepan Glavina <[email protected]>, Taiki Endo <[email protected]>, John Nunley <[email protected]>"
convert_case,https://github.com/rutrum/convert-case,MIT,David Purdum <[email protected]>
convert_case,https://github.com/rutrum/convert-case,MIT,Rutrum <[email protected]>
core-foundation,https://github.com/servo/core-foundation-rs,MIT OR Apache-2.0,The Servo Project Developers
cpufeatures,https://github.com/RustCrypto/utils,MIT OR Apache-2.0,RustCrypto Developers
crc32fast,https://github.com/srijs/rust-crc32fast,MIT OR Apache-2.0,"Sam Rijs <[email protected]>, Alex Crichton <[email protected]>"
Expand Down
50 changes: 50 additions & 0 deletions benches/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ criterion_group!(
assert,
assert_eq,
r#bool,
camelcase,
ceil,
chunks,
compact,
Expand Down Expand Up @@ -79,6 +80,7 @@ criterion_group!(
is_timestamp,
join,
keys,
kebabcase,
length,
log,
r#match,
Expand Down Expand Up @@ -117,6 +119,7 @@ criterion_group!(
parse_url,
parse_user_agent,
parse_xml,
pascalcase,
push,
// TODO: value is non-deterministic and so cannot assert equality
// random_bool,
Expand All @@ -130,6 +133,8 @@ criterion_group!(
round,
seahash,
set,
screamingsnakecase,
snakecase,
sha1,
sha2,
sha3,
Expand Down Expand Up @@ -2766,3 +2771,48 @@ bench_function! {
want: Ok("1:wCb3OG7yAFWelaUydu0D+125CLM="),
}
}

bench_function! {
camelcase => vrl::stdlib::Camelcase;

default {
args: func_args![value: "input-string"],
want: Ok("inputString"),
}
}

bench_function! {
pascalcase => vrl::stdlib::Pascalcase;

default {
args: func_args![value: "input-string"],
want: Ok("InputString"),
}
}

bench_function! {
kebabcase => vrl::stdlib::Kebabcase;

default {
args: func_args![value: "inputString"],
want: Ok("input-string"),
}
}

bench_function! {
snakecase => vrl::stdlib::Snakecase;

default {
args: func_args![value: "input-string"],
want: Ok("input_string"),
}
}

bench_function! {
screamingsnakecase => vrl::stdlib::ScreamingSnakecase;

default {
args: func_args![value: "input-string"],
want: Ok("INPUT_STRING"),
}
}
1 change: 1 addition & 0 deletions changelog.d/973.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
added casing functions `camelcase`, `kebabcase`, `screamingsnakecase`, `snakecase`, `pascalcase`
103 changes: 103 additions & 0 deletions src/stdlib/casing/camelcase.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use crate::compiler::prelude::*;

use convert_case::Case;

fn camelcase(value: Value, orig_case: Option<Case>) -> Resolved {
super::convert_case(value, Case::Camel, orig_case)
}

#[derive(Clone, Copy, Debug)]
pub struct Camelcase;

impl Function for Camelcase {
fn identifier(&self) -> &'static str {
"camelcase"
}

fn parameters(&self) -> &'static [Parameter] {
&[
Parameter {
keyword: "value",
kind: kind::BYTES,
required: true,
},
Parameter {
keyword: "original_case",
kind: kind::BYTES,
required: false,
},
]
}

fn compile(
&self,
state: &state::TypeState,
_ctx: &mut FunctionCompileContext,
arguments: ArgumentList,
) -> Compiled {
let value = arguments.required("value");
let original_case = arguments
.optional_enum("original_case", &super::variants(), state)?
.map(|b| {
b.try_bytes_utf8_lossy()
.expect("cant convert to string")
.into_owned()
})
.map(super::into_case)
.transpose()?;

Ok(CamelcaseFn {
value,
original_case,
}
.as_expr())
}

fn examples(&self) -> &'static [Example] {
&[Example {
title: "camelcase",
source: r#"camelcase("input_string")"#,
result: Ok("inputString"),
}]
}
}

#[derive(Debug, Clone)]
struct CamelcaseFn {
value: Box<dyn Expression>,
original_case: Option<Case>,
}

impl FunctionExpression for CamelcaseFn {
fn resolve(&self, ctx: &mut Context) -> Resolved {
let value = self.value.resolve(ctx)?;
let original_case = self.original_case;
camelcase(value, original_case)
}

fn type_def(&self, _: &state::TypeState) -> TypeDef {
TypeDef::bytes().infallible()
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::value;

test_function![
camelcase => Camelcase;

simple {
args: func_args![value: value!("into_camel"), original_case: "snake_case"],
want: Ok(value!("intoCamel")),
tdef: TypeDef::bytes(),
}

no_case {
args: func_args![value: value!("into_camel")],
want: Ok(value!("intoCamel")),
tdef: TypeDef::bytes(),
}
];
}
103 changes: 103 additions & 0 deletions src/stdlib/casing/kebabcase.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use crate::compiler::prelude::*;

use convert_case::Case;

fn kebabcase(value: Value, orig_case: Option<Case>) -> Resolved {
super::convert_case(value, Case::Kebab, orig_case)
}

#[derive(Clone, Copy, Debug)]
pub struct Kebabcase;

impl Function for Kebabcase {
fn identifier(&self) -> &'static str {
"kebabcase"
}

fn parameters(&self) -> &'static [Parameter] {
&[
Parameter {
keyword: "value",
kind: kind::BYTES,
required: true,
},
Parameter {
keyword: "original_case",
kind: kind::BYTES,
required: false,
},
]
}

fn compile(
&self,
state: &state::TypeState,
_ctx: &mut FunctionCompileContext,
arguments: ArgumentList,
) -> Compiled {
let value = arguments.required("value");
let original_case = arguments
.optional_enum("original_case", &super::variants(), state)?
.map(|b| {
b.try_bytes_utf8_lossy()
.expect("cant convert to string")
.into_owned()
})
.map(super::into_case)
.transpose()?;

Ok(KebabcaseFn {
value,
original_case,
}
.as_expr())
}

fn examples(&self) -> &'static [Example] {
&[Example {
title: "kebabcase",
source: r#"kebabcase("input_string")"#,
result: Ok("input-string"),
}]
}
}

#[derive(Debug, Clone)]
struct KebabcaseFn {
value: Box<dyn Expression>,
original_case: Option<Case>,
}

impl FunctionExpression for KebabcaseFn {
fn resolve(&self, ctx: &mut Context) -> Resolved {
let value = self.value.resolve(ctx)?;
let original_case = self.original_case;
kebabcase(value, original_case)
}

fn type_def(&self, _: &state::TypeState) -> TypeDef {
TypeDef::bytes().infallible()
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::value;

test_function![
kebabcase => Kebabcase;

simple {
args: func_args![value: value!("input_string"), original_case: "snake_case"],
want: Ok(value!("input-string")),
tdef: TypeDef::bytes(),
}

no_case {
args: func_args![value: value!("input_string")],
want: Ok(value!("input-string")),
tdef: TypeDef::bytes(),
}
];
}
Loading

0 comments on commit ea40963

Please sign in to comment.