nushell/crates/nu-cli/src/commands/calc.rs

89 lines
2.3 KiB
Rust
Raw Normal View History

use crate::commands::WholeStreamCommand;
2020-01-29 14:34:36 +01:00
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
2020-01-29 14:34:36 +01:00
pub struct Calc;
2020-05-29 10:22:52 +02:00
#[async_trait]
impl WholeStreamCommand for Calc {
2020-01-29 14:34:36 +01:00
fn name(&self) -> &str {
"calc"
}
fn usage(&self) -> &str {
"Parse a math expression into a number"
}
2020-05-29 10:22:52 +02:00
async fn run(
2020-01-29 14:34:36 +01:00
&self,
args: CommandArgs,
registry: &CommandRegistry,
2020-01-29 14:34:36 +01:00
) -> Result<OutputStream, ShellError> {
calc(args, registry).await
2020-01-29 14:34:36 +01:00
}
2020-05-12 03:00:55 +02:00
fn examples(&self) -> Vec<Example> {
vec![Example {
2020-05-12 03:00:55 +02:00
description: "Calculate math in the pipeline",
example: "echo '10 / 4' | calc",
result: Some(vec![UntaggedValue::decimal(2.5).into()]),
2020-05-12 03:00:55 +02:00
}]
}
2020-01-29 14:34:36 +01:00
}
pub async fn calc(
args: CommandArgs,
_registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let input = args.input;
let name = args.call_info.name_tag.span;
Ok(input
.map(move |input| {
if let Ok(string) = input.as_string() {
match parse(&string, &input.tag) {
Ok(value) => ReturnSuccess::value(value),
Err(err) => Err(ShellError::labeled_error(
"Calculation error",
err,
&input.tag.span,
)),
}
} else {
Err(ShellError::labeled_error(
"Expected a string from pipeline",
"requires string input",
name,
))
}
})
.to_output_stream())
2020-01-29 14:34:36 +01:00
}
pub fn parse(math_expression: &str, tag: impl Into<Tag>) -> Result<Value, String> {
use std::f64;
2020-01-29 14:34:36 +01:00
let num = meval::eval_str(math_expression);
match num {
Ok(num) => {
if num == f64::INFINITY || num == f64::NEG_INFINITY {
return Err(String::from("cannot represent result"));
}
Ok(UntaggedValue::from(Primitive::from(num)).into_value(tag))
}
2020-01-29 14:34:36 +01:00
Err(error) => Err(error.to_string()),
}
}
#[cfg(test)]
mod tests {
use super::Calc;
#[test]
fn examples_work_as_expected() {
use crate::examples::test as test_examples;
test_examples(Calc {})
}
}