Add --to-float to str plugin (#1872)

This commit is contained in:
k-brk 2020-05-25 00:11:49 +02:00 committed by GitHub
parent fb09d7d1a1
commit 8a99d112fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 86 additions and 5 deletions

2
Cargo.lock generated
View file

@ -2319,6 +2319,7 @@ dependencies = [
name = "nu-plugin"
version = "0.14.1"
dependencies = [
"bigdecimal",
"indexmap",
"nu-build",
"nu-errors",
@ -2531,6 +2532,7 @@ dependencies = [
name = "nu_plugin_str"
version = "0.14.1"
dependencies = [
"bigdecimal",
"chrono",
"nu-build",
"nu-errors",

View file

@ -179,7 +179,16 @@ pub fn autoview(context: RunnableContext) -> Result<OutputStream, ShellError> {
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: UntaggedValue::Primitive(Primitive::Boolean(b)),

View file

@ -19,6 +19,7 @@ indexmap = { version = "1.3.2", features = ["serde-1"] }
serde = { version = "1.0.110", features = ["derive"] }
num-bigint = { version = "0.2.6", features = ["serde"] }
serde_json = "1.0.53"
bigdecimal = { version = "0.1.2", features = ["serde"] }
[build-dependencies]
nu-build = { version = "0.14.1", path = "../nu-build" }

View file

@ -172,6 +172,7 @@ pub fn expect_return_value_at(
}
pub mod value {
use bigdecimal::BigDecimal;
use nu_errors::ShellError;
use nu_protocol::{Primitive, TaggedDictBuilder, UntaggedValue, Value};
use nu_source::Tag;
@ -186,6 +187,10 @@ pub mod 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 {
UntaggedValue::string(input.into()).into_untagged_value()
}

View file

@ -19,6 +19,7 @@ chrono = { version = "0.4.11", features = ["serde"] }
regex = "1"
num-bigint = "0.2.6"
bigdecimal = { version = "0.1.2", features = ["serde"] }
[build-dependencies]
nu-build = { version = "0.14.1", path = "../nu-build" }

View file

@ -19,6 +19,7 @@ impl Plugin for Str {
.switch("downcase", "convert string to lowercase", Some('d'))
.switch("upcase", "convert string to uppercase", Some('U'))
.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'))
.named(
"replace",
@ -66,6 +67,9 @@ impl Plugin for Str {
if args.has("to-int") {
self.for_to_int();
}
if args.has("to-float") {
self.for_to_float();
}
if args.has("substring") {
if let Some(start_end) = args.get("substring") {
match start_end {

View file

@ -3,7 +3,7 @@ mod integration {
use crate::Str;
use nu_errors::ShellError;
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,
};
use nu_plugin::test_helpers::{expect_return_value_at, plugin, CallStub};
@ -91,6 +91,13 @@ mod integration {
.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]
fn picks_up_arguments_for_replace_flag() {
let argument = String::from("replace_text");
@ -259,6 +266,24 @@ mod integration {
assert_eq!(get_data(actual, "Nu_birthday"), int(10));
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]
fn replaces_the_input_using_the_field_passed_as_parameter() -> Result<(), ShellError> {

View file

@ -1,5 +1,6 @@
extern crate chrono;
use bigdecimal::BigDecimal;
use chrono::DateTime;
use nu_errors::ShellError;
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 regex::Regex;
use std::cmp;
use std::str::FromStr;
#[derive(Debug, Eq, PartialEq)]
pub enum Action {
@ -14,6 +16,7 @@ pub enum Action {
Downcase,
Upcase,
ToInteger,
ToFloat,
Substring(usize, usize),
Replace(ReplaceAction),
ToDateTime(String),
@ -93,6 +96,10 @@ impl Str {
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) {
Ok(d) => UntaggedValue::date(d),
Err(_) => UntaggedValue::string(input),
@ -119,6 +126,10 @@ impl Str {
self.add_action(Action::ToInteger);
}
pub fn for_to_float(&mut self) {
self.add_action(Action::ToFloat);
}
pub fn for_capitalize(&mut self) {
self.add_action(Action::Capitalize);
}
@ -177,7 +188,7 @@ impl 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> {
@ -240,7 +251,7 @@ impl Str {
pub mod tests {
use super::ReplaceAction;
use super::Str;
use nu_plugin::test_helpers::value::{int, string};
use nu_plugin::test_helpers::value::{decimal, int, string};
#[test]
fn trim() -> Result<(), Box<dyn std::error::Error>> {
@ -282,6 +293,14 @@ pub mod tests {
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]
fn replaces() -> Result<(), Box<dyn std::error::Error>> {
let mut strutils = Str::new();

View file

@ -9,7 +9,7 @@ fn can_only_apply_one() {
"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]
@ -131,6 +131,21 @@ fn converts_to_int() {
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]
fn replaces() {
Playground::setup("plugin_str_test_5", |dirs, sandbox| {