nushell/crates/nu-command/src/commands/echo.rs

206 lines
6.4 KiB
Rust
Raw Normal View History

2019-09-08 01:43:53 +02:00
use crate::prelude::*;
use bigdecimal::Zero;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
2020-05-27 20:07:53 +02:00
use nu_protocol::hir::Operator;
use nu_protocol::{Primitive, Range, RangeInclusion, Signature, SyntaxShape, UntaggedValue, Value};
2019-09-08 01:43:53 +02:00
pub struct Echo;
impl WholeStreamCommand for Echo {
2019-09-08 01:43:53 +02:00
fn name(&self) -> &str {
"echo"
}
fn signature(&self) -> Signature {
2019-10-28 06:15:35 +01:00
Signature::build("echo").rest(SyntaxShape::Any, "the values to echo")
2019-09-08 01:43:53 +02:00
}
fn usage(&self) -> &str {
2019-09-25 00:15:53 +02:00
"Echo the arguments back to the user."
2019-09-08 01:43:53 +02:00
}
fn run(&self, args: CommandArgs) -> Result<InputStream, ShellError> {
echo(args)
2019-09-08 01:43:53 +02:00
}
2020-05-12 07:17:17 +02:00
fn examples(&self) -> Vec<Example> {
vec![
2020-05-12 07:17:17 +02:00
Example {
description: "Put a hello message in the pipeline",
example: "echo 'hello'",
result: Some(vec![Value::from("hello")]),
2020-05-12 07:17:17 +02:00
},
Example {
description: "Print the value of the special '$nu' variable",
example: "echo $nu",
result: None,
2020-05-12 07:17:17 +02:00
},
]
}
2019-09-08 01:43:53 +02:00
}
fn echo(args: CommandArgs) -> Result<InputStream, ShellError> {
let args = args.evaluate_once()?;
let rest: Vec<Value> = args.rest(0)?;
2019-09-08 01:43:53 +02:00
let stream = rest.into_iter().map(|i| match i.as_string() {
Ok(s) => InputStream::one(UntaggedValue::string(s).into_value(i.tag.clone())),
_ => match i {
Value {
value: UntaggedValue::Table(table),
..
} => InputStream::from_stream(table.into_iter()),
Value {
value: UntaggedValue::Primitive(Primitive::Range(range)),
tag,
} => InputStream::from_stream(RangeIterator::new(*range, tag)),
x => InputStream::one(x),
},
});
Ok(InputStream::from_stream(stream.flatten()))
}
struct RangeIterator {
curr: UntaggedValue,
end: UntaggedValue,
tag: Tag,
is_end_inclusive: bool,
2021-01-12 20:27:54 +01:00
moves_up: bool,
one: UntaggedValue,
negative_one: UntaggedValue,
done: bool,
}
impl RangeIterator {
pub fn new(range: Range, tag: Tag) -> RangeIterator {
let start = match range.from.0.item {
Primitive::Nothing => Primitive::Int(0.into()),
x => x,
};
2021-01-12 20:27:54 +01:00
let end = match range.to.0.item {
Primitive::Nothing => Primitive::Int(u64::MAX.into()),
x => x,
};
RangeIterator {
2021-01-12 20:27:54 +01:00
moves_up: start <= end,
curr: UntaggedValue::Primitive(start),
end: UntaggedValue::Primitive(end),
tag,
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
one: UntaggedValue::int(1),
negative_one: UntaggedValue::int(-1),
done: false,
2019-09-08 01:43:53 +02:00
}
}
}
2019-09-08 01:43:53 +02:00
impl Iterator for RangeIterator {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
use std::cmp::Ordering;
if self.done {
return None;
}
let ordering = if self.end == UntaggedValue::Primitive(Primitive::Nothing) {
Ordering::Less
} else {
match (&self.curr, &self.end) {
(
UntaggedValue::Primitive(Primitive::Int(x)),
UntaggedValue::Primitive(Primitive::Int(y)),
) => x.cmp(y),
(
UntaggedValue::Primitive(Primitive::Decimal(x)),
UntaggedValue::Primitive(Primitive::Decimal(y)),
) => x.cmp(y),
(
UntaggedValue::Primitive(Primitive::Decimal(x)),
UntaggedValue::Primitive(Primitive::Int(y)),
) => x.cmp(&(BigDecimal::zero() + y)),
(
UntaggedValue::Primitive(Primitive::Int(x)),
UntaggedValue::Primitive(Primitive::Decimal(y)),
) => (BigDecimal::zero() + x).cmp(y),
_ => {
self.done = true;
return Some(
UntaggedValue::Error(ShellError::labeled_error(
"Cannot create range",
"unsupported range",
self.tag.span,
))
.into_untagged_value(),
);
}
}
};
2021-01-12 20:27:54 +01:00
if self.moves_up
&& (ordering == Ordering::Less || self.is_end_inclusive && ordering == Ordering::Equal)
{
let next_value = nu_data::value::compute_values(Operator::Plus, &self.curr, &self.one);
let mut next = match next_value {
Ok(result) => result,
2021-01-12 20:27:54 +01:00
Err((left_type, right_type)) => {
self.done = true;
return Some(
UntaggedValue::Error(ShellError::coerce_error(
left_type.spanned(self.tag.span),
right_type.spanned(self.tag.span),
))
.into_untagged_value(),
);
2021-01-12 20:27:54 +01:00
}
};
std::mem::swap(&mut self.curr, &mut next);
Some(next.into_value(self.tag.clone()))
2021-01-12 20:27:54 +01:00
} else if !self.moves_up
&& (ordering == Ordering::Greater
|| self.is_end_inclusive && ordering == Ordering::Equal)
{
let next_value =
nu_data::value::compute_values(Operator::Plus, &self.curr, &self.negative_one);
let mut next = match next_value {
Ok(result) => result,
Err((left_type, right_type)) => {
self.done = true;
return Some(
UntaggedValue::Error(ShellError::coerce_error(
left_type.spanned(self.tag.span),
right_type.spanned(self.tag.span),
))
.into_untagged_value(),
);
}
};
std::mem::swap(&mut self.curr, &mut next);
Some(next.into_value(self.tag.clone()))
} else {
None
}
}
2019-09-08 01:43:53 +02:00
}
#[cfg(test)]
mod tests {
use super::Echo;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
2021-02-12 11:13:14 +01:00
test_examples(Echo {})
}
}