forked from extern/nushell
add "into int" behavior (#3279)
* add 'int' command * create `into int` behavior - forcibly overwrote prior implementation ```sh git mv -f crates/nu-command/src/commands/int_.rs crates/nu-command/src/commands/into_int.rs ``` - picked up prior work, polished and renamed Co-authored-by: Saeed Rasooli <saeed.gnu@gmail.com>
This commit is contained in:
parent
b791d1ab0d
commit
556596bce8
@ -1,28 +1,33 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_protocol::{
|
||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
|
||||
use num_bigint::ToBigInt;
|
||||
use num_bigint::{BigInt, ToBigInt};
|
||||
|
||||
pub struct IntoInt;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IntoIntArgs {
|
||||
pub rest: Vec<Value>,
|
||||
pub rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for IntoInt {
|
||||
fn name(&self) -> &str {
|
||||
"into-int"
|
||||
"into int"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into-int").rest(SyntaxShape::Any, "the values to into-int")
|
||||
Signature::build("into int").rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"column paths to convert to int (for table input)",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to integer."
|
||||
"Convert value to integer"
|
||||
}
|
||||
|
||||
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
@ -32,45 +37,136 @@ impl WholeStreamCommand for IntoInt {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert filesize to integer",
|
||||
example: "into-int 1kb | each { = $it / 1000 }",
|
||||
result: Some(vec![UntaggedValue::int(1).into()]),
|
||||
description: "Convert string to integer in table",
|
||||
example: "echo [[num]; ['-5'] [4] [1.5]] | into int num",
|
||||
result: Some(vec![
|
||||
UntaggedValue::row(indexmap! {
|
||||
"num".to_string() => UntaggedValue::int(-5).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"num".to_string() => UntaggedValue::int(4).into(),
|
||||
})
|
||||
.into(),
|
||||
UntaggedValue::row(indexmap! {
|
||||
"num".to_string() => UntaggedValue::int(1).into(),
|
||||
})
|
||||
.into(),
|
||||
]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert filesize to integer",
|
||||
example: "into-int 1kib | each { = $it / 1024 }",
|
||||
result: Some(vec![UntaggedValue::int(1).into()]),
|
||||
description: "Convert string to integer",
|
||||
example: "echo '2' | into int",
|
||||
result: Some(vec![UntaggedValue::int(2).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert decimal to integer",
|
||||
example: "echo 5.9 | into int",
|
||||
result: Some(vec![UntaggedValue::int(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert decimal string to integer",
|
||||
example: "echo '5.9' | into int",
|
||||
result: Some(vec![UntaggedValue::int(5).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert file size to integer",
|
||||
example: "echo 4KB | into int",
|
||||
result: Some(vec![UntaggedValue::int(4000).into()]),
|
||||
},
|
||||
Example {
|
||||
description: "Convert bool to integer",
|
||||
example: "echo $false $true | into int",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(0).into(),
|
||||
UntaggedValue::int(1).into(),
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn into_int(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (args, _): (IntoIntArgs, _) = args.process()?;
|
||||
let (IntoIntArgs { rest: column_paths }, input) = args.process()?;
|
||||
|
||||
let stream = args.rest.into_iter().map(|i| match i {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(primitive_val),
|
||||
Ok(input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
ReturnSuccess::value(action(&v, v.tag())?)
|
||||
} 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())),
|
||||
)?;
|
||||
}
|
||||
|
||||
ReturnSuccess::value(ret)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
match &input.value {
|
||||
UntaggedValue::Primitive(prim) => Ok(UntaggedValue::int(match prim {
|
||||
Primitive::String(a_string) => match int_from_string(a_string, &tag) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
Primitive::Decimal(dec) => match dec.to_bigint() {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to int",
|
||||
));
|
||||
}
|
||||
},
|
||||
Primitive::Int(n_ref) => n_ref.to_bigint().expect("unexpected error"),
|
||||
Primitive::Boolean(a_bool) => match a_bool {
|
||||
false => 0.to_bigint().expect("unexpected error"),
|
||||
true => 1.to_bigint().expect("unexpected error"),
|
||||
},
|
||||
Primitive::Filesize(a_filesize) => a_filesize
|
||||
.to_bigint()
|
||||
.expect("Conversion should never fail."),
|
||||
_ => {
|
||||
return Err(ShellError::unimplemented(
|
||||
"'int' for non-numeric primitives",
|
||||
))
|
||||
}
|
||||
})
|
||||
.into_value(&tag)),
|
||||
UntaggedValue::Row(_) => Err(ShellError::labeled_error(
|
||||
"specify column name to use, with 'int COLUMN'",
|
||||
"found table",
|
||||
tag,
|
||||
} => match primitive_val {
|
||||
Primitive::Filesize(size) => OutputStream::one(Ok(ReturnSuccess::Value(Value {
|
||||
value: UntaggedValue::int(size.to_bigint().expect("Conversion should never fail.")),
|
||||
tag,
|
||||
}))),
|
||||
Primitive::Int(_) => OutputStream::one(Ok(ReturnSuccess::Value(Value {
|
||||
value: UntaggedValue::Primitive(primitive_val),
|
||||
tag,
|
||||
}))),
|
||||
_ => OutputStream::one(Err(ShellError::labeled_error(
|
||||
"Could not convert int value",
|
||||
"original value",
|
||||
tag,
|
||||
))),
|
||||
},
|
||||
_ => OutputStream::one(Ok(ReturnSuccess::Value(i))),
|
||||
});
|
||||
)),
|
||||
_ => Err(ShellError::unimplemented("'int' for unsupported type")),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(stream.flatten().to_output_stream())
|
||||
fn int_from_string(a_string: &str, tag: &Tag) -> Result<BigInt, ShellError> {
|
||||
match a_string.parse::<BigInt>() {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => match a_string.parse::<f64>() {
|
||||
Ok(res_float) => match res_float.to_bigint() {
|
||||
Some(n) => Ok(n),
|
||||
None => Err(ShellError::unimplemented(
|
||||
"failed to convert decimal to int",
|
||||
)),
|
||||
},
|
||||
Err(_) => Err(ShellError::labeled_error(
|
||||
"Could not convert string value to int",
|
||||
"original value",
|
||||
tag.clone(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -5,7 +5,7 @@ fn into_int_filesize() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
into-int 1kb | each {= $it / 1000 }
|
||||
echo 1kb | into int | each {= $it / 1000 }
|
||||
"#
|
||||
));
|
||||
|
||||
@ -17,7 +17,7 @@ fn into_int_filesize2() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
into-int 1kib | each {= $it / 1024 }
|
||||
echo 1kib | into int | each {= $it / 1024 }
|
||||
"#
|
||||
));
|
||||
|
||||
@ -29,7 +29,7 @@ fn into_int_int() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
into-int 1024 | each {= $it / 1024 }
|
||||
echo 1024 | into int | each {= $it / 1024 }
|
||||
"#
|
||||
));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user