mirror of
https://github.com/nushell/nushell
synced 2024-11-10 07:04:13 +00:00
Add --to-float to str plugin (#1872)
This commit is contained in:
parent
fb09d7d1a1
commit
8a99d112fc
9 changed files with 86 additions and 5 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2319,6 +2319,7 @@ dependencies = [
|
||||||
name = "nu-plugin"
|
name = "nu-plugin"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bigdecimal",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"nu-build",
|
"nu-build",
|
||||||
"nu-errors",
|
"nu-errors",
|
||||||
|
@ -2531,6 +2532,7 @@ dependencies = [
|
||||||
name = "nu_plugin_str"
|
name = "nu_plugin_str"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bigdecimal",
|
||||||
"chrono",
|
"chrono",
|
||||||
"nu-build",
|
"nu-build",
|
||||||
"nu-errors",
|
"nu-errors",
|
||||||
|
|
|
@ -179,7 +179,16 @@ pub fn autoview(context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||||
value: UntaggedValue::Primitive(Primitive::Decimal(n)),
|
value: UntaggedValue::Primitive(Primitive::Decimal(n)),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
out!("{}", n);
|
// TODO: normalize decimal to remove trailing zeros.
|
||||||
|
// normalization will be available in next release of bigdecimal crate
|
||||||
|
let mut output = n.to_string();
|
||||||
|
if output.contains('.') {
|
||||||
|
output = output.trim_end_matches('0').to_owned();
|
||||||
|
}
|
||||||
|
if output.ends_with('.') {
|
||||||
|
output.push('0');
|
||||||
|
}
|
||||||
|
out!("{}", output);
|
||||||
}
|
}
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::Boolean(b)),
|
value: UntaggedValue::Primitive(Primitive::Boolean(b)),
|
||||||
|
|
|
@ -19,6 +19,7 @@ indexmap = { version = "1.3.2", features = ["serde-1"] }
|
||||||
serde = { version = "1.0.110", features = ["derive"] }
|
serde = { version = "1.0.110", features = ["derive"] }
|
||||||
num-bigint = { version = "0.2.6", features = ["serde"] }
|
num-bigint = { version = "0.2.6", features = ["serde"] }
|
||||||
serde_json = "1.0.53"
|
serde_json = "1.0.53"
|
||||||
|
bigdecimal = { version = "0.1.2", features = ["serde"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
nu-build = { version = "0.14.1", path = "../nu-build" }
|
nu-build = { version = "0.14.1", path = "../nu-build" }
|
||||||
|
|
|
@ -172,6 +172,7 @@ pub fn expect_return_value_at(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod value {
|
pub mod value {
|
||||||
|
use bigdecimal::BigDecimal;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Primitive, TaggedDictBuilder, UntaggedValue, Value};
|
use nu_protocol::{Primitive, TaggedDictBuilder, UntaggedValue, Value};
|
||||||
use nu_source::Tag;
|
use nu_source::Tag;
|
||||||
|
@ -186,6 +187,10 @@ pub mod value {
|
||||||
UntaggedValue::Primitive(Primitive::Int(i.into())).into_untagged_value()
|
UntaggedValue::Primitive(Primitive::Int(i.into())).into_untagged_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn decimal(f: impl Into<BigDecimal>) -> Value {
|
||||||
|
UntaggedValue::Primitive(Primitive::Decimal(f.into())).into_untagged_value()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn string(input: impl Into<String>) -> Value {
|
pub fn string(input: impl Into<String>) -> Value {
|
||||||
UntaggedValue::string(input.into()).into_untagged_value()
|
UntaggedValue::string(input.into()).into_untagged_value()
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ chrono = { version = "0.4.11", features = ["serde"] }
|
||||||
|
|
||||||
regex = "1"
|
regex = "1"
|
||||||
num-bigint = "0.2.6"
|
num-bigint = "0.2.6"
|
||||||
|
bigdecimal = { version = "0.1.2", features = ["serde"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
nu-build = { version = "0.14.1", path = "../nu-build" }
|
nu-build = { version = "0.14.1", path = "../nu-build" }
|
||||||
|
|
|
@ -19,6 +19,7 @@ impl Plugin for Str {
|
||||||
.switch("downcase", "convert string to lowercase", Some('d'))
|
.switch("downcase", "convert string to lowercase", Some('d'))
|
||||||
.switch("upcase", "convert string to uppercase", Some('U'))
|
.switch("upcase", "convert string to uppercase", Some('U'))
|
||||||
.switch("to-int", "convert string to integer", Some('i'))
|
.switch("to-int", "convert string to integer", Some('i'))
|
||||||
|
.switch("to-float", "convert string to float", Some('F'))
|
||||||
.switch("trim", "trims the string", Some('t'))
|
.switch("trim", "trims the string", Some('t'))
|
||||||
.named(
|
.named(
|
||||||
"replace",
|
"replace",
|
||||||
|
@ -66,6 +67,9 @@ impl Plugin for Str {
|
||||||
if args.has("to-int") {
|
if args.has("to-int") {
|
||||||
self.for_to_int();
|
self.for_to_int();
|
||||||
}
|
}
|
||||||
|
if args.has("to-float") {
|
||||||
|
self.for_to_float();
|
||||||
|
}
|
||||||
if args.has("substring") {
|
if args.has("substring") {
|
||||||
if let Some(start_end) = args.get("substring") {
|
if let Some(start_end) = args.get("substring") {
|
||||||
match start_end {
|
match start_end {
|
||||||
|
|
|
@ -3,7 +3,7 @@ mod integration {
|
||||||
use crate::Str;
|
use crate::Str;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_plugin::test_helpers::value::{
|
use nu_plugin::test_helpers::value::{
|
||||||
column_path, get_data, int, string, structured_sample_record, table,
|
column_path, decimal, get_data, int, string, structured_sample_record, table,
|
||||||
unstructured_sample_record,
|
unstructured_sample_record,
|
||||||
};
|
};
|
||||||
use nu_plugin::test_helpers::{expect_return_value_at, plugin, CallStub};
|
use nu_plugin::test_helpers::{expect_return_value_at, plugin, CallStub};
|
||||||
|
@ -91,6 +91,13 @@ mod integration {
|
||||||
.setup(|plugin, _| plugin.expect_action(Action::ToInteger));
|
.setup(|plugin, _| plugin.expect_action(Action::ToInteger));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn picks_up_to_float_flag() {
|
||||||
|
plugin(&mut Str::new())
|
||||||
|
.args(CallStub::new().with_long_flag("to-float").create())
|
||||||
|
.setup(|plugin, _| plugin.expect_action(Action::ToFloat));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn picks_up_arguments_for_replace_flag() {
|
fn picks_up_arguments_for_replace_flag() {
|
||||||
let argument = String::from("replace_text");
|
let argument = String::from("replace_text");
|
||||||
|
@ -259,6 +266,24 @@ mod integration {
|
||||||
assert_eq!(get_data(actual, "Nu_birthday"), int(10));
|
assert_eq!(get_data(actual, "Nu_birthday"), int(10));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn converts_the_input_to_float_using_the_field_passed_as_parameter() -> Result<(), ShellError> {
|
||||||
|
let run = plugin(&mut Str::new())
|
||||||
|
.args(
|
||||||
|
CallStub::new()
|
||||||
|
.with_long_flag("to-float")
|
||||||
|
.with_parameter("PI")?
|
||||||
|
.create(),
|
||||||
|
)
|
||||||
|
.input(structured_sample_record("PI", "3.1415"))
|
||||||
|
.setup(|_, _| {})
|
||||||
|
.test();
|
||||||
|
|
||||||
|
let actual = expect_return_value_at(run, 0);
|
||||||
|
|
||||||
|
assert_eq!(get_data(actual, "PI"), decimal(3.1415));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn replaces_the_input_using_the_field_passed_as_parameter() -> Result<(), ShellError> {
|
fn replaces_the_input_using_the_field_passed_as_parameter() -> Result<(), ShellError> {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
|
|
||||||
|
use bigdecimal::BigDecimal;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{did_you_mean, ColumnPath, Primitive, ShellTypeName, UntaggedValue, Value};
|
use nu_protocol::{did_you_mean, ColumnPath, Primitive, ShellTypeName, UntaggedValue, Value};
|
||||||
|
@ -7,6 +8,7 @@ use nu_source::{span_for_spanned_list, Tagged};
|
||||||
use nu_value_ext::ValueExt;
|
use nu_value_ext::ValueExt;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
|
@ -14,6 +16,7 @@ pub enum Action {
|
||||||
Downcase,
|
Downcase,
|
||||||
Upcase,
|
Upcase,
|
||||||
ToInteger,
|
ToInteger,
|
||||||
|
ToFloat,
|
||||||
Substring(usize, usize),
|
Substring(usize, usize),
|
||||||
Replace(ReplaceAction),
|
Replace(ReplaceAction),
|
||||||
ToDateTime(String),
|
ToDateTime(String),
|
||||||
|
@ -93,6 +96,10 @@ impl Str {
|
||||||
Err(_) => UntaggedValue::string(input),
|
Err(_) => UntaggedValue::string(input),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(Action::ToFloat) => match BigDecimal::from_str(input.trim()) {
|
||||||
|
Ok(v) => UntaggedValue::decimal(v),
|
||||||
|
Err(_) => UntaggedValue::string(input),
|
||||||
|
},
|
||||||
Some(Action::ToDateTime(dt)) => match DateTime::parse_from_str(input, dt) {
|
Some(Action::ToDateTime(dt)) => match DateTime::parse_from_str(input, dt) {
|
||||||
Ok(d) => UntaggedValue::date(d),
|
Ok(d) => UntaggedValue::date(d),
|
||||||
Err(_) => UntaggedValue::string(input),
|
Err(_) => UntaggedValue::string(input),
|
||||||
|
@ -119,6 +126,10 @@ impl Str {
|
||||||
self.add_action(Action::ToInteger);
|
self.add_action(Action::ToInteger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_to_float(&mut self) {
|
||||||
|
self.add_action(Action::ToFloat);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn for_capitalize(&mut self) {
|
pub fn for_capitalize(&mut self) {
|
||||||
self.add_action(Action::Capitalize);
|
self.add_action(Action::Capitalize);
|
||||||
}
|
}
|
||||||
|
@ -177,7 +188,7 @@ impl Str {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn usage() -> &'static str {
|
pub fn usage() -> &'static str {
|
||||||
"Usage: str field [--capitalize|--downcase|--upcase|--to-int|--substring \"start,end\"|--replace|--find-replace [pattern replacement]|to-date-time|--trim]"
|
"Usage: str field [--capitalize|--downcase|--upcase|--to-int|--to-float|--substring \"start,end\"|--replace|--find-replace [pattern replacement]|to-date-time|--trim]"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strutils(&self, value: Value) -> Result<Value, ShellError> {
|
pub fn strutils(&self, value: Value) -> Result<Value, ShellError> {
|
||||||
|
@ -240,7 +251,7 @@ impl Str {
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::ReplaceAction;
|
use super::ReplaceAction;
|
||||||
use super::Str;
|
use super::Str;
|
||||||
use nu_plugin::test_helpers::value::{int, string};
|
use nu_plugin::test_helpers::value::{decimal, int, string};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trim() -> Result<(), Box<dyn std::error::Error>> {
|
fn trim() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
@ -282,6 +293,14 @@ pub mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn converts_to_float() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut strutils = Str::new();
|
||||||
|
strutils.for_to_float();
|
||||||
|
assert_eq!(strutils.apply("3.1415")?, decimal(3.1415).value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn replaces() -> Result<(), Box<dyn std::error::Error>> {
|
fn replaces() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut strutils = Str::new();
|
let mut strutils = Str::new();
|
||||||
|
|
|
@ -9,7 +9,7 @@ fn can_only_apply_one() {
|
||||||
"open caco3_plastics.csv | first 1 | str origin --downcase --upcase"
|
"open caco3_plastics.csv | first 1 | str origin --downcase --upcase"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(actual.err.contains(r#"--capitalize|--downcase|--upcase|--to-int|--substring "start,end"|--replace|--find-replace [pattern replacement]|to-date-time|--trim]"#));
|
assert!(actual.err.contains(r#"--capitalize|--downcase|--upcase|--to-int|--to-float|--substring "start,end"|--replace|--find-replace [pattern replacement]|to-date-time|--trim]"#));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -131,6 +131,21 @@ fn converts_to_int() {
|
||||||
assert_eq!(actual.out, "1");
|
assert_eq!(actual.out, "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn converts_to_float() {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: "tests/fixtures/formats", pipeline(
|
||||||
|
r#"
|
||||||
|
echo "3.1, 0.0415"
|
||||||
|
| split row ","
|
||||||
|
| str --to-float
|
||||||
|
| sum
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual.out, "3.1415");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn replaces() {
|
fn replaces() {
|
||||||
Playground::setup("plugin_str_test_5", |dirs, sandbox| {
|
Playground::setup("plugin_str_test_5", |dirs, sandbox| {
|
||||||
|
|
Loading…
Reference in a new issue