forked from extern/nushell
added various case conversion commands for str. Added the inflection … (#2363)
* added various case conversion commands for str. Added the inflection crate as a dependency * lighten the restriction on the inflector dependency * publishing the case commands * fix typo * fix kebab case test * formatting
This commit is contained in:
parent
a224cd38ab
commit
f6ff6ab6e4
@ -97,6 +97,7 @@ quick-xml = "0.18.1"
|
||||
rayon = "1.3.1"
|
||||
trash = {version = "1.0.1", optional = true}
|
||||
url = {version = "2.1.1"}
|
||||
Inflector = "0.11"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
users = "0.10.0"
|
||||
|
@ -320,6 +320,11 @@ pub fn create_default_context(
|
||||
whole_stream_command(StrCollect),
|
||||
whole_stream_command(StrLength),
|
||||
whole_stream_command(StrReverse),
|
||||
whole_stream_command(StrCamelCase),
|
||||
whole_stream_command(StrPascalCase),
|
||||
whole_stream_command(StrKebabCase),
|
||||
whole_stream_command(StrSnakeCase),
|
||||
whole_stream_command(StrScreamingSnakeCase),
|
||||
whole_stream_command(BuildString),
|
||||
whole_stream_command(Ansi),
|
||||
whole_stream_command(Char),
|
||||
|
@ -231,8 +231,9 @@ pub(crate) use sort_by::SortBy;
|
||||
pub(crate) use split::{Split, SplitChars, SplitColumn, SplitRow};
|
||||
pub(crate) use split_by::SplitBy;
|
||||
pub(crate) use str_::{
|
||||
Str, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith, StrFindReplace, StrFrom,
|
||||
StrIndexOf, StrLength, StrReverse, StrSet, StrStartsWith, StrSubstring, StrToDatetime,
|
||||
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
|
||||
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLength, StrPascalCase, StrReverse,
|
||||
StrScreamingSnakeCase, StrSet, StrSnakeCase, StrStartsWith, StrSubstring, StrToDatetime,
|
||||
StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
||||
};
|
||||
pub(crate) use table::Table;
|
||||
|
74
crates/nu-cli/src/commands/str_/case/camel_case.rs
Normal file
74
crates/nu-cli/src/commands/str_/case/camel_case.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use super::operate;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use inflector::cases::camelcase::to_camel_case;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str camel-case"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("str camel-case").rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"optionally convert text to camelCase by column paths",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"converts a string to camelCase"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
operate(args, registry, &to_camel_case).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "convert a string to camelCase",
|
||||
example: "echo 'NuShell' | str camel-case",
|
||||
result: Some(vec![Value::from("nuShell")]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{to_camel_case, SubCommand};
|
||||
use crate::commands::str_::case::action;
|
||||
use nu_plugin::test_helpers::value::string;
|
||||
use nu_source::Tag;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn camel_case_from_kebab() {
|
||||
let word = string("this-is-the-first-case");
|
||||
let expected = string("thisIsTheFirstCase");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_camel_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
#[test]
|
||||
fn camel_case_from_snake() {
|
||||
let word = string("this_is_the_second_case");
|
||||
let expected = string("thisIsTheSecondCase");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_camel_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
74
crates/nu-cli/src/commands/str_/case/kebab_case.rs
Normal file
74
crates/nu-cli/src/commands/str_/case/kebab_case.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use super::operate;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use inflector::cases::kebabcase::to_kebab_case;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str kebab-case"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("str kebab-case").rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"optionally convert text to kebab-case by column paths",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"converts a string to kebab-case"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
operate(args, registry, &to_kebab_case).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "convert a string to kebab-case",
|
||||
example: "echo 'NuShell' | str kebab-case",
|
||||
result: Some(vec![Value::from("nu-shell")]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{to_kebab_case, SubCommand};
|
||||
use crate::commands::str_::case::action;
|
||||
use nu_plugin::test_helpers::value::string;
|
||||
use nu_source::Tag;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kebab_case_from_camel() {
|
||||
let word = string("thisIsTheFirstCase");
|
||||
let expected = string("this-is-the-first-case");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_kebab_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
#[test]
|
||||
fn kebab_case_from_screaming_snake() {
|
||||
let word = string("THIS_IS_THE_SECOND_CASE");
|
||||
let expected = string("this-is-the-second-case");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_kebab_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
79
crates/nu-cli/src/commands/str_/case/mod.rs
Normal file
79
crates/nu-cli/src/commands/str_/case/mod.rs
Normal file
@ -0,0 +1,79 @@
|
||||
pub mod camel_case;
|
||||
pub mod kebab_case;
|
||||
pub mod pascal_case;
|
||||
pub mod screaming_snake_case;
|
||||
pub mod snake_case;
|
||||
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::ShellTypeName;
|
||||
use nu_protocol::{ColumnPath, Primitive, ReturnSuccess, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
pub use camel_case::SubCommand as CamelCase;
|
||||
pub use pascal_case::SubCommand as PascalCase;
|
||||
pub use screaming_snake_case::SubCommand as ScreamingSnakeCase;
|
||||
pub use snake_case::SubCommand as SnakeCase;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Arguments {
|
||||
rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
pub async fn operate<F>(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
case_operation: &'static F,
|
||||
) -> Result<OutputStream, ShellError>
|
||||
where
|
||||
F: Fn(&str) -> String + Send + Sync + 'static,
|
||||
{
|
||||
let registry = registry.clone();
|
||||
|
||||
let (Arguments { rest }, input) = args.process(®istry).await?;
|
||||
|
||||
let column_paths: Vec<_> = rest;
|
||||
Ok(input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
ReturnSuccess::value(action(&v, v.tag(), &case_operation)?)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
|
||||
for path in &column_paths {
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, old.tag(), &case_operation)),
|
||||
)?;
|
||||
}
|
||||
|
||||
ReturnSuccess::value(ret)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
pub fn action<F>(
|
||||
input: &Value,
|
||||
tag: impl Into<Tag>,
|
||||
case_operation: &F,
|
||||
) -> Result<Value, ShellError>
|
||||
where
|
||||
F: Fn(&str) -> String + Send + Sync + 'static,
|
||||
{
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(Primitive::Line(s))
|
||||
| UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||
Ok(UntaggedValue::string(case_operation(s)).into_value(tag))
|
||||
}
|
||||
other => {
|
||||
let got = format!("got {}", other.type_name());
|
||||
Err(ShellError::labeled_error(
|
||||
"value is not string",
|
||||
got,
|
||||
tag.into().span,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
74
crates/nu-cli/src/commands/str_/case/pascal_case.rs
Normal file
74
crates/nu-cli/src/commands/str_/case/pascal_case.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use super::operate;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use inflector::cases::pascalcase::to_pascal_case;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str pascal-case"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("str pascal-case").rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"optionally convert text to PascalCase by column paths",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"converts a string to PascalCase"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
operate(args, registry, &to_pascal_case).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "convert a string to PascalCase",
|
||||
example: "echo 'nu-shell' | str pascal-case",
|
||||
result: Some(vec![Value::from("NuShell")]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{to_pascal_case, SubCommand};
|
||||
use crate::commands::str_::case::action;
|
||||
use nu_plugin::test_helpers::value::string;
|
||||
use nu_source::Tag;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pascal_case_from_kebab() {
|
||||
let word = string("this-is-the-first-case");
|
||||
let expected = string("ThisIsTheFirstCase");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_pascal_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
#[test]
|
||||
fn pascal_case_from_snake() {
|
||||
let word = string("this_is_the_second_case");
|
||||
let expected = string("ThisIsTheSecondCase");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_pascal_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
74
crates/nu-cli/src/commands/str_/case/screaming_snake_case.rs
Normal file
74
crates/nu-cli/src/commands/str_/case/screaming_snake_case.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use super::operate;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str screaming-snake-case"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("str screaming-snake-case").rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"optionally convert text to SCREAMING_SNAKE_CASE by column paths",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"converts a string to SCREAMING_SNAKE_CASE"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
operate(args, registry, &to_screaming_snake_case).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "convert a string to SCREAMING_SNAKE_CASE",
|
||||
example: "echo 'NuShell' | str screaming-snake-case",
|
||||
result: Some(vec![Value::from("NU_SHELL")]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{to_screaming_snake_case, SubCommand};
|
||||
use crate::commands::str_::case::action;
|
||||
use nu_plugin::test_helpers::value::string;
|
||||
use nu_source::Tag;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snake_case_from_kebab() {
|
||||
let word = string("this-is-the-first-case");
|
||||
let expected = string("THIS_IS_THE_FIRST_CASE");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_screaming_snake_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
#[test]
|
||||
fn snake_case_from_snake() {
|
||||
let word = string("this_is_the_second_case");
|
||||
let expected = string("THIS_IS_THE_SECOND_CASE");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_screaming_snake_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
74
crates/nu-cli/src/commands/str_/case/snake_case.rs
Normal file
74
crates/nu-cli/src/commands/str_/case/snake_case.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use super::operate;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use inflector::cases::snakecase::to_snake_case;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str snake-case"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("str snake-case").rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"optionally convert text to snake_case by column paths",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"converts a string to snake_case"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
operate(args, registry, &to_snake_case).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "convert a string to snake_case",
|
||||
example: "echo 'NuShell' | str snake-case",
|
||||
result: Some(vec![Value::from("nu_shell")]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{to_snake_case, SubCommand};
|
||||
use crate::commands::str_::case::action;
|
||||
use nu_plugin::test_helpers::value::string;
|
||||
use nu_source::Tag;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snake_case_from_kebab() {
|
||||
let word = string("this-is-the-first-case");
|
||||
let expected = string("this_is_the_first_case");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_snake_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
#[test]
|
||||
fn snake_case_from_camel() {
|
||||
let word = string("thisIsTheSecondCase");
|
||||
let expected = string("this_is_the_second_case");
|
||||
|
||||
let actual = action(&word, Tag::unknown(), &to_snake_case).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
mod capitalize;
|
||||
mod case;
|
||||
mod collect;
|
||||
mod command;
|
||||
mod contains;
|
||||
@ -19,6 +20,11 @@ mod trim;
|
||||
mod upcase;
|
||||
|
||||
pub use capitalize::SubCommand as StrCapitalize;
|
||||
pub use case::camel_case::SubCommand as StrCamelCase;
|
||||
pub use case::kebab_case::SubCommand as StrKebabCase;
|
||||
pub use case::pascal_case::SubCommand as StrPascalCase;
|
||||
pub use case::screaming_snake_case::SubCommand as StrScreamingSnakeCase;
|
||||
pub use case::snake_case::SubCommand as StrSnakeCase;
|
||||
pub use collect::SubCommand as StrCollect;
|
||||
pub use command::Command as Str;
|
||||
pub use contains::SubCommand as StrContains;
|
||||
|
@ -97,6 +97,26 @@ fn upcases() {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn camelcases() {
|
||||
Playground::setup("str_test_3", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContent(
|
||||
"sample.toml",
|
||||
r#"
|
||||
[dependency]
|
||||
name = "THIS_IS_A_TEST"
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"open sample.toml | str camel-case dependency.name | get dependency.name | echo $it"
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "thisIsATest");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn converts_to_int() {
|
||||
let actual = nu!(
|
||||
|
Loading…
Reference in New Issue
Block a user