nushell/src/commands/get.rs
Yehuda Katz e4226def16 Extract core stuff into own crates
This commit extracts five new crates:

- nu-source, which contains the core source-code handling logic in Nu,
  including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
  used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
  conveniences
- nu-textview, which is the textview plugin extracted into a crate

One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).

This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-12-02 10:54:12 -08:00

167 lines
5.1 KiB
Rust

use crate::commands::WholeStreamCommand;
use crate::data::base::property_get::get_data_by_column_path;
use crate::data::base::shape::Shapes;
use crate::prelude::*;
use crate::utils::did_you_mean;
use futures_util::pin_mut;
use log::trace;
use nu_errors::ShellError;
use nu_protocol::{
ColumnPath, ReturnSuccess, ReturnValue, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::{span_for_spanned_list, PrettyDebug};
pub struct Get;
#[derive(Deserialize)]
pub struct GetArgs {
rest: Vec<ColumnPath>,
}
impl WholeStreamCommand for Get {
fn name(&self) -> &str {
"get"
}
fn signature(&self) -> Signature {
Signature::build("get").rest(
SyntaxShape::ColumnPath,
"optionally return additional data by path",
)
}
fn usage(&self) -> &str {
"Open given cells as text."
}
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
args.process(registry, get)?.run()
}
}
pub fn get_column_path(path: &ColumnPath, obj: &Value) -> Result<Value, ShellError> {
let fields = path.clone();
get_data_by_column_path(
obj,
path,
Box::new(move |(obj_source, column_path_tried, error)| {
match &obj_source.value {
UntaggedValue::Table(rows) => {
let total = rows.len();
let end_tag = match fields
.members()
.iter()
.nth_back(if fields.members().len() > 2 { 1 } else { 0 })
{
Some(last_field) => last_field.span,
None => column_path_tried.span,
};
return ShellError::labeled_error_with_secondary(
"Row not found",
format!(
"There isn't a row indexed at {}",
column_path_tried.display()
),
column_path_tried.span,
if total == 1 {
format!("The table only has 1 row")
} else {
format!("The table only has {} rows (0 to {})", total, total - 1)
},
end_tag,
);
}
_ => {}
}
match did_you_mean(&obj_source, column_path_tried) {
Some(suggestions) => {
return ShellError::labeled_error(
"Unknown column",
format!("did you mean '{}'?", suggestions[0].1),
span_for_spanned_list(fields.members().iter().map(|p| p.span)),
)
}
None => {}
}
return error;
}),
)
}
pub fn get(
GetArgs { rest: mut fields }: GetArgs,
RunnableContext { input, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
if fields.len() == 0 {
let stream = async_stream! {
let values = input.values;
pin_mut!(values);
let mut shapes = Shapes::new();
let mut index = 0;
while let Some(row) = values.next().await {
shapes.add(&row, index);
index += 1;
}
for row in shapes.to_values() {
yield ReturnSuccess::value(row);
}
};
let stream: BoxStream<'static, ReturnValue> = stream.boxed();
Ok(stream.to_output_stream())
} else {
let member = fields.remove(0);
trace!("get {:?} {:?}", member, fields);
let stream = input
.values
.map(move |item| {
let mut result = VecDeque::new();
let member = vec![member.clone()];
let column_paths = vec![&member, &fields]
.into_iter()
.flatten()
.collect::<Vec<&ColumnPath>>();
for path in column_paths {
let res = get_column_path(&path, &item);
match res {
Ok(got) => match got {
Value {
value: UntaggedValue::Table(rows),
..
} => {
for item in rows {
result.push_back(ReturnSuccess::value(item.clone()));
}
}
other => result.push_back(ReturnSuccess::value(other.clone())),
},
Err(reason) => result.push_back(ReturnSuccess::value(
UntaggedValue::Error(reason).into_untagged_value(),
)),
}
}
result
})
.flatten();
Ok(stream.to_output_stream())
}
}