use nu_engine::CallExt; use nu_protocol::ast::{Call, CellPath}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, }; #[derive(Clone)] pub struct Select; impl Command for Select { fn name(&self) -> &str { "select" } fn signature(&self) -> Signature { Signature::build("select") .rest( "rest", SyntaxShape::CellPath, "the columns to select from the table", ) .category(Category::Filters) } fn usage(&self) -> &str { "Down-select table to only these columns." } fn run( &self, engine_state: &EngineState, stack: &mut Stack, call: &Call, input: PipelineData, ) -> Result { let columns: Vec = call.rest(engine_state, stack, 0)?; let span = call.head; select(engine_state, span, columns, input) } fn examples(&self) -> Vec { vec![ Example { description: "Select just the name column", example: "ls | select name", result: None, }, Example { description: "Select the name and size columns", example: "ls | select name size", result: None, }, ] } } fn select( engine_state: &EngineState, span: Span, columns: Vec, input: PipelineData, ) -> Result { if columns.is_empty() { return Err(ShellError::CantFindColumn(span, span)); //FIXME? } match input { PipelineData::Value( Value::List { vals: input_vals, span, }, .., ) => { let mut output = vec![]; for input_val in input_vals { let mut cols = vec![]; let mut vals = vec![]; for path in &columns { //FIXME: improve implementation to not clone let fetcher = input_val.clone().follow_cell_path(&path.members)?; cols.push(path.into_string()); vals.push(fetcher); } output.push(Value::Record { cols, vals, span }) } Ok(output .into_iter() .into_pipeline_data(engine_state.ctrlc.clone())) } PipelineData::ListStream(stream, ..) => Ok(stream .map(move |x| { let mut cols = vec![]; let mut vals = vec![]; for path in &columns { //FIXME: improve implementation to not clone match x.clone().follow_cell_path(&path.members) { Ok(value) => { cols.push(path.into_string()); vals.push(value); } Err(error) => { cols.push(path.into_string()); vals.push(Value::Error { error }); } } } Value::Record { cols, vals, span } }) .into_pipeline_data(engine_state.ctrlc.clone())), PipelineData::Value(v, ..) => { let mut cols = vec![]; let mut vals = vec![]; for cell_path in columns { // FIXME: remove clone let result = v.clone().follow_cell_path(&cell_path.members)?; cols.push(cell_path.into_string()); vals.push(result); } Ok(Value::Record { cols, vals, span }.into_pipeline_data()) } _ => Ok(PipelineData::new(span)), } }