Introduce metadata into the pipeline (#397)

This commit is contained in:
JT 2021-12-02 18:59:10 +13:00 committed by GitHub
parent 56307553ae
commit 45eba8b922
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 329 additions and 199 deletions

View File

@ -34,19 +34,22 @@ impl Command for Echo {
let n = to_be_echoed.len(); let n = to_be_echoed.len();
match n.cmp(&1usize) { match n.cmp(&1usize) {
// More than one value is converted in a stream of values // More than one value is converted in a stream of values
std::cmp::Ordering::Greater => PipelineData::Stream(ValueStream::from_stream( std::cmp::Ordering::Greater => PipelineData::Stream(
to_be_echoed.into_iter(), ValueStream::from_stream(to_be_echoed.into_iter(), engine_state.ctrlc.clone()),
engine_state.ctrlc.clone(), None,
)), ),
// But a single value can be forwarded as it is // But a single value can be forwarded as it is
std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone()), std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone(), None),
// When there are no elements, we echo the empty string // When there are no elements, we echo the empty string
std::cmp::Ordering::Less => PipelineData::Value(Value::String { std::cmp::Ordering::Less => PipelineData::Value(
val: "".to_string(), Value::String {
span: call.head, val: "".to_string(),
}), span: call.head,
},
None,
),
} }
}) })
} }

View File

@ -1,10 +1,10 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use lscolors::{LsColors, Style};
use nu_engine::eval_expression; use nu_engine::eval_expression;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, IntoInterruptiblePipelineData, PipelineData, Signature, SyntaxShape, Value, Category, DataSource, IntoInterruptiblePipelineData, PipelineData, PipelineMetadata, Signature,
SyntaxShape, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -37,7 +37,6 @@ impl Command for Ls {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let config = stack.get_config()?;
let pattern = if let Some(expr) = call.positional.get(0) { let pattern = if let Some(expr) = call.positional.get(0) {
let result = eval_expression(engine_state, stack, expr)?; let result = eval_expression(engine_state, stack, expr)?;
let mut result = result.as_string()?; let mut result = result.as_string()?;
@ -57,10 +56,6 @@ impl Command for Ls {
let call_span = call.head; let call_span = call.head;
let glob = glob::glob(&pattern).unwrap(); let glob = glob::glob(&pattern).unwrap();
let ls_colors = match stack.get_env_var("LS_COLORS") {
Some(s) => LsColors::from_string(&s),
None => LsColors::default(),
};
Ok(glob Ok(glob
.into_iter() .into_iter()
@ -72,22 +67,11 @@ impl Command for Ls {
let is_dir = metadata.is_dir(); let is_dir = metadata.is_dir();
let filesize = metadata.len(); let filesize = metadata.len();
let mut cols = vec!["name".into(), "type".into(), "size".into()]; let mut cols = vec!["name".into(), "type".into(), "size".into()];
let style =
ls_colors.style_for_path_with_metadata(path.clone(), Some(&metadata));
let ansi_style = style.map(Style::to_crossterm_style).unwrap_or_default();
let use_ls_colors = config.use_ls_colors;
let mut vals = vec![ let mut vals = vec![
if use_ls_colors { Value::String {
Value::String { val: path.to_string_lossy().to_string(),
val: ansi_style.apply(path.to_string_lossy()).to_string(), span: call_span,
span: call_span,
}
} else {
Value::String {
val: path.to_string_lossy().to_string(),
span: call_span,
}
}, },
if is_symlink { if is_symlink {
Value::string("symlink", call_span) Value::string("symlink", call_span)
@ -120,34 +104,26 @@ impl Command for Ls {
span: call_span, span: call_span,
} }
} }
Err(_) => { Err(_) => Value::Record {
let style = ls_colors.style_for_path(path.clone()); cols: vec!["name".into(), "type".into(), "size".into()],
let ansi_style = style.map(Style::to_crossterm_style).unwrap_or_default(); vals: vec![
let use_ls_colors = config.use_ls_colors; Value::String {
val: path.to_string_lossy().to_string(),
Value::Record { span: call_span,
cols: vec!["name".into(), "type".into(), "size".into()], },
vals: vec![ Value::Nothing { span: call_span },
if use_ls_colors { Value::Nothing { span: call_span },
Value::String { ],
val: ansi_style.apply(path.to_string_lossy()).to_string(), span: call_span,
span: call_span, },
}
} else {
Value::String {
val: path.to_string_lossy().to_string(),
span: call_span,
}
},
Value::Nothing { span: call_span },
Value::Nothing { span: call_span },
],
span: call_span,
}
}
}, },
_ => Value::Nothing { span: call_span }, _ => Value::Nothing { span: call_span },
}) })
.into_pipeline_data(engine_state.ctrlc.clone())) .into_pipeline_data_with_metadata(
PipelineMetadata {
data_source: DataSource::Ls,
},
engine_state.ctrlc.clone(),
))
} }
} }

View File

@ -74,8 +74,8 @@ impl Command for Each {
let span = call.head; let span = call.head;
match input { match input {
PipelineData::Value(Value::Range { .. }) PipelineData::Value(Value::Range { .. }, ..)
| PipelineData::Value(Value::List { .. }) | PipelineData::Value(Value::List { .. }, ..)
| PipelineData::Stream { .. } => Ok(input | PipelineData::Stream { .. } => Ok(input
.into_iter() .into_iter()
.enumerate() .enumerate()
@ -109,7 +109,7 @@ impl Command for Each {
} }
}) })
.into_pipeline_data(ctrlc)), .into_pipeline_data(ctrlc)),
PipelineData::Value(Value::Record { cols, vals, .. }) => { PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
let mut output_cols = vec![]; let mut output_cols = vec![];
let mut output_vals = vec![]; let mut output_vals = vec![];
@ -138,9 +138,12 @@ impl Command for Each {
} }
match eval_block(&engine_state, &mut stack, block, PipelineData::new(span))? { match eval_block(&engine_state, &mut stack, block, PipelineData::new(span))? {
PipelineData::Value(Value::Record { PipelineData::Value(
mut cols, mut vals, .. Value::Record {
}) => { mut cols, mut vals, ..
},
..,
) => {
// TODO check that the lengths match when traversing record // TODO check that the lengths match when traversing record
output_cols.append(&mut cols); output_cols.append(&mut cols);
output_vals.append(&mut vals); output_vals.append(&mut vals);
@ -159,7 +162,7 @@ impl Command for Each {
} }
.into_pipeline_data()) .into_pipeline_data())
} }
PipelineData::Value(x) => { PipelineData::Value(x, ..) => {
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
if let Some(var) = block.signature.get_positional(0) { if let Some(var) = block.signature.get_positional(0) {

View File

@ -26,7 +26,7 @@ impl Command for Length {
input: PipelineData, input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
match input { match input {
PipelineData::Value(Value::Nothing { .. }) => Ok(Value::Int { PipelineData::Value(Value::Nothing { .. }, ..) => Ok(Value::Int {
val: 0, val: 0,
span: call.head, span: call.head,
} }

View File

@ -34,7 +34,7 @@ impl Command for Lines {
// Collect is needed because the string may not live long enough for // Collect is needed because the string may not live long enough for
// the Rc structure to continue using it. If split could take ownership // the Rc structure to continue using it. If split could take ownership
// of the split values, then this wouldn't be needed // of the split values, then this wouldn't be needed
PipelineData::Value(Value::String { val, span }) => { PipelineData::Value(Value::String { val, span }, ..) => {
let lines = val let lines = val
.split(SPLIT_CHAR) .split(SPLIT_CHAR)
.map(|s| s.to_string()) .map(|s| s.to_string())
@ -50,7 +50,7 @@ impl Command for Lines {
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone())) Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
} }
PipelineData::Stream(stream) => { PipelineData::Stream(stream, ..) => {
let iter = stream let iter = stream
.into_iter() .into_iter()
.filter_map(|value| { .filter_map(|value| {
@ -78,7 +78,7 @@ impl Command for Lines {
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone())) Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
} }
PipelineData::Value(val) => Err(ShellError::UnsupportedInput( PipelineData::Value(val, ..) => Err(ShellError::UnsupportedInput(
format!("Not supported input: {}", val.as_string()?), format!("Not supported input: {}", val.as_string()?),
call.head, call.head,
)), )),

View File

@ -57,7 +57,7 @@ impl Command for ParEach {
let span = call.head; let span = call.head;
match input { match input {
PipelineData::Value(Value::Range { val, .. }) => Ok(val PipelineData::Value(Value::Range { val, .. }, ..) => Ok(val
.into_range_iter()? .into_range_iter()?
.enumerate() .enumerate()
.par_bridge() .par_bridge()
@ -98,7 +98,7 @@ impl Command for ParEach {
.into_iter() .into_iter()
.flatten() .flatten()
.into_pipeline_data(ctrlc)), .into_pipeline_data(ctrlc)),
PipelineData::Value(Value::List { vals: val, .. }) => Ok(val PipelineData::Value(Value::List { vals: val, .. }, ..) => Ok(val
.into_iter() .into_iter()
.enumerate() .enumerate()
.par_bridge() .par_bridge()
@ -139,7 +139,7 @@ impl Command for ParEach {
.into_iter() .into_iter()
.flatten() .flatten()
.into_pipeline_data(ctrlc)), .into_pipeline_data(ctrlc)),
PipelineData::Stream(stream) => Ok(stream PipelineData::Stream(stream, ..) => Ok(stream
.enumerate() .enumerate()
.par_bridge() .par_bridge()
.map(move |(idx, x)| { .map(move |(idx, x)| {
@ -179,7 +179,7 @@ impl Command for ParEach {
.into_iter() .into_iter()
.flatten() .flatten()
.into_pipeline_data(ctrlc)), .into_pipeline_data(ctrlc)),
PipelineData::Value(Value::Record { cols, vals, .. }) => { PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
let mut output_cols = vec![]; let mut output_cols = vec![];
let mut output_vals = vec![]; let mut output_vals = vec![];
@ -208,9 +208,12 @@ impl Command for ParEach {
} }
match eval_block(&engine_state, &mut stack, block, PipelineData::new(span))? { match eval_block(&engine_state, &mut stack, block, PipelineData::new(span))? {
PipelineData::Value(Value::Record { PipelineData::Value(
mut cols, mut vals, .. Value::Record {
}) => { mut cols, mut vals, ..
},
..,
) => {
// TODO check that the lengths match when traversing record // TODO check that the lengths match when traversing record
output_cols.append(&mut cols); output_cols.append(&mut cols);
output_vals.append(&mut vals); output_vals.append(&mut vals);
@ -229,7 +232,7 @@ impl Command for ParEach {
} }
.into_pipeline_data()) .into_pipeline_data())
} }
PipelineData::Value(x) => { PipelineData::Value(x, ..) => {
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
if let Some(var) = block.signature.get_positional(0) { if let Some(var) = block.signature.get_positional(0) {

View File

@ -90,7 +90,10 @@ impl Command for Range {
}; };
if from > to { if from > to {
Ok(PipelineData::Value(Value::Nothing { span: call.head })) Ok(PipelineData::Value(
Value::Nothing { span: call.head },
None,
))
} else { } else {
let iter = v.into_iter().skip(from).take(to - from + 1); let iter = v.into_iter().skip(from).take(to - from + 1);
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone())) Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
@ -100,7 +103,10 @@ impl Command for Range {
let to = rows_to as usize; let to = rows_to as usize;
if from > to { if from > to {
Ok(PipelineData::Value(Value::Nothing { span: call.head })) Ok(PipelineData::Value(
Value::Nothing { span: call.head },
None,
))
} else { } else {
let iter = input.into_iter().skip(from).take(to - from + 1); let iter = input.into_iter().skip(from).take(to - from + 1);
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone())) Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))

View File

@ -68,10 +68,13 @@ fn select(
} }
match input { match input {
PipelineData::Value(Value::List { PipelineData::Value(
vals: input_vals, Value::List {
span, vals: input_vals,
}) => { span,
},
..,
) => {
let mut output = vec![]; let mut output = vec![];
for input_val in input_vals { for input_val in input_vals {
@ -92,7 +95,7 @@ fn select(
.into_iter() .into_iter()
.into_pipeline_data(engine_state.ctrlc.clone())) .into_pipeline_data(engine_state.ctrlc.clone()))
} }
PipelineData::Stream(stream) => Ok(stream PipelineData::Stream(stream, ..) => Ok(stream
.map(move |x| { .map(move |x| {
let mut cols = vec![]; let mut cols = vec![];
let mut vals = vec![]; let mut vals = vec![];
@ -113,7 +116,7 @@ fn select(
Value::Record { cols, vals, span } Value::Record { cols, vals, span }
}) })
.into_pipeline_data(engine_state.ctrlc.clone())), .into_pipeline_data(engine_state.ctrlc.clone())),
PipelineData::Value(v) => { PipelineData::Value(v, ..) => {
let mut cols = vec![]; let mut cols = vec![];
let mut vals = vec![]; let mut vals = vec![];

View File

@ -35,7 +35,7 @@ impl Command for Wrap {
let name: String = call.req(engine_state, stack, 0)?; let name: String = call.req(engine_state, stack, 0)?;
match input { match input {
PipelineData::Value(Value::List { vals, .. }) => Ok(vals PipelineData::Value(Value::List { vals, .. }, ..) => Ok(vals
.into_iter() .into_iter()
.map(move |x| Value::Record { .map(move |x| Value::Record {
cols: vec![name.clone()], cols: vec![name.clone()],
@ -43,14 +43,14 @@ impl Command for Wrap {
span, span,
}) })
.into_pipeline_data(engine_state.ctrlc.clone())), .into_pipeline_data(engine_state.ctrlc.clone())),
PipelineData::Stream(stream) => Ok(stream PipelineData::Stream(stream, ..) => Ok(stream
.map(move |x| Value::Record { .map(move |x| Value::Record {
cols: vec![name.clone()], cols: vec![name.clone()],
vals: vec![x], vals: vec![x],
span, span,
}) })
.into_pipeline_data(engine_state.ctrlc.clone())), .into_pipeline_data(engine_state.ctrlc.clone())),
PipelineData::Value(input) => Ok(Value::Record { PipelineData::Value(input, ..) => Ok(Value::Record {
cols: vec![name], cols: vec![name],
vals: vec![input], vals: vec![input],
span, span,

View File

@ -230,10 +230,13 @@ fn from_eml(
); );
} }
Ok(PipelineData::Value(Value::from(Spanned { Ok(PipelineData::Value(
item: collected, Value::from(Spanned {
span: head, item: collected,
}))) span: head,
}),
None,
))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -194,7 +194,7 @@ fn from_ods(
span: head, span: head,
}; };
Ok(PipelineData::Value(record)) Ok(PipelineData::Value(record, None))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -67,11 +67,14 @@ fn from_url(input: PipelineData, head: Span, config: &Config) -> Result<Pipeline
vals.push(Value::String { val: v, span: head }) vals.push(Value::String { val: v, span: head })
} }
Ok(PipelineData::Value(Value::Record { Ok(PipelineData::Value(
cols, Value::Record {
vals, cols,
span: head, vals,
})) span: head,
},
None,
))
} }
_ => Err(ShellError::UnsupportedInput( _ => Err(ShellError::UnsupportedInput(
"String not compatible with url-encoding".to_string(), "String not compatible with url-encoding".to_string(),

View File

@ -194,7 +194,7 @@ fn from_xlsx(
span: head, span: head,
}; };
Ok(PipelineData::Value(record)) Ok(PipelineData::Value(record, None))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -57,14 +57,14 @@ pub fn eval(
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
if let Some(expr) = spanned_expr { if let Some(expr) = spanned_expr {
match parse(&expr.item, &expr.span) { match parse(&expr.item, &expr.span) {
Ok(value) => Ok(PipelineData::Value(value)), Ok(value) => Ok(PipelineData::Value(value, None)),
Err(err) => Err(ShellError::UnsupportedInput( Err(err) => Err(ShellError::UnsupportedInput(
format!("Math evaluation error: {}", err), format!("Math evaluation error: {}", err),
expr.span, expr.span,
)), )),
} }
} else { } else {
if let PipelineData::Value(Value::Nothing { .. }) = input { if let PipelineData::Value(Value::Nothing { .. }, ..) = input {
return Ok(input); return Ok(input);
} }
input.map( input.map(

View File

@ -62,12 +62,12 @@ pub fn calculate(
mf: impl Fn(&[Value], &Span) -> Result<Value, ShellError>, mf: impl Fn(&[Value], &Span) -> Result<Value, ShellError>,
) -> Result<Value, ShellError> { ) -> Result<Value, ShellError> {
match values { match values {
PipelineData::Stream(s) => helper_for_tables(&s.collect::<Vec<Value>>(), name, mf), PipelineData::Stream(s, ..) => helper_for_tables(&s.collect::<Vec<Value>>(), name, mf),
PipelineData::Value(Value::List { ref vals, .. }) => match &vals[..] { PipelineData::Value(Value::List { ref vals, .. }, ..) => match &vals[..] {
[Value::Record { .. }, _end @ ..] => helper_for_tables(vals, name, mf), [Value::Record { .. }, _end @ ..] => helper_for_tables(vals, name, mf),
_ => mf(vals, &name), _ => mf(vals, &name),
}, },
PipelineData::Value(Value::Record { vals, cols, span }) => { PipelineData::Value(Value::Record { vals, cols, span }, ..) => {
let new_vals: Result<Vec<Value>, ShellError> = let new_vals: Result<Vec<Value>, ShellError> =
vals.into_iter().map(|val| mf(&[val], &name)).collect(); vals.into_iter().map(|val| mf(&[val], &name)).collect();
match new_vals { match new_vals {
@ -79,7 +79,7 @@ pub fn calculate(
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
PipelineData::Value(Value::Range { val, .. }) => { PipelineData::Value(Value::Range { val, .. }, ..) => {
let new_vals: Result<Vec<Value>, ShellError> = val let new_vals: Result<Vec<Value>, ShellError> = val
.into_range_iter()? .into_range_iter()?
.map(|val| mf(&[val], &name)) .map(|val| mf(&[val], &name))
@ -87,6 +87,6 @@ pub fn calculate(
mf(&new_vals?, &name) mf(&new_vals?, &name)
} }
PipelineData::Value(val) => mf(&[val], &name), PipelineData::Value(val, ..) => mf(&[val], &name),
} }
} }

View File

@ -78,10 +78,13 @@ fn bool(
let mut rng = thread_rng(); let mut rng = thread_rng();
let bool_result: bool = rng.gen_bool(probability); let bool_result: bool = rng.gen_bool(probability);
Ok(PipelineData::Value(Value::Bool { Ok(PipelineData::Value(
val: bool_result, Value::Bool {
span, val: bool_result,
})) span,
},
None,
))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -70,10 +70,13 @@ fn chars(
.map(char::from) .map(char::from)
.collect::<String>(); .collect::<String>();
Ok(PipelineData::Value(Value::String { Ok(PipelineData::Value(
val: random_string, Value::String {
span, val: random_string,
})) span,
},
None,
))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -125,7 +125,10 @@ fn format(
// We can only handle a Record or a List of Record's // We can only handle a Record or a List of Record's
match data_as_value { match data_as_value {
Value::Record { .. } => match format_record(format_operations, &data_as_value) { Value::Record { .. } => match format_record(format_operations, &data_as_value) {
Ok(value) => Ok(PipelineData::Value(Value::string(value, Span::unknown()))), Ok(value) => Ok(PipelineData::Value(
Value::string(value, Span::unknown()),
None,
)),
Err(value) => Err(value), Err(value) => Err(value),
}, },
@ -151,10 +154,10 @@ fn format(
} }
} }
Ok(PipelineData::Stream(ValueStream::from_stream( Ok(PipelineData::Stream(
list.into_iter(), ValueStream::from_stream(list.into_iter(), None),
None, None,
))) ))
} }
_ => Err(ShellError::UnsupportedInput( _ => Err(ShellError::UnsupportedInput(
"Input data is not supported by this command.".to_string(), "Input data is not supported by this command.".to_string(),

View File

@ -74,6 +74,7 @@ fn operate(
let head = call.head; let head = call.head;
let pattern: Spanned<String> = call.req(engine_state, stack, 0)?; let pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
let regex: bool = call.has_flag("regex"); let regex: bool = call.has_flag("regex");
let ctrlc = engine_state.ctrlc.clone();
let pattern_item = pattern.item; let pattern_item = pattern.item;
let pattern_span = pattern.span; let pattern_span = pattern.span;
@ -125,10 +126,10 @@ fn operate(
} }
} }
Ok(PipelineData::Stream(ValueStream::from_stream( Ok(PipelineData::Stream(
parsed.into_iter(), ValueStream::from_stream(parsed.into_iter(), ctrlc),
None, None,
))) ))
} }
fn build_regex(input: &str, span: Span) -> Result<String, ShellError> { fn build_regex(input: &str, span: Span) -> Result<String, ShellError> {

View File

@ -94,7 +94,7 @@ impl ExternalCommand {
// If there is an input from the pipeline. The stdin from the process // If there is an input from the pipeline. The stdin from the process
// is piped so it can be used to send the input information // is piped so it can be used to send the input information
if !matches!(input, PipelineData::Value(Value::Nothing { .. })) { if !matches!(input, PipelineData::Value(Value::Nothing { .. }, ..)) {
process.stdin(Stdio::piped()); process.stdin(Stdio::piped());
} }

View File

@ -64,7 +64,7 @@ prints out the list properly."#
let use_grid_icons = config.use_grid_icons; let use_grid_icons = config.use_grid_icons;
match input { match input {
PipelineData::Value(Value::List { vals, .. }) => { PipelineData::Value(Value::List { vals, .. }, ..) => {
// dbg!("value::list"); // dbg!("value::list");
let data = convert_to_list(vals, &config); let data = convert_to_list(vals, &config);
if let Some(items) = data { if let Some(items) = data {
@ -81,7 +81,7 @@ prints out the list properly."#
Ok(PipelineData::new(call.head)) Ok(PipelineData::new(call.head))
} }
} }
PipelineData::Stream(stream) => { PipelineData::Stream(stream, ..) => {
// dbg!("value::stream"); // dbg!("value::stream");
let data = convert_to_list(stream, &config); let data = convert_to_list(stream, &config);
if let Some(items) = data { if let Some(items) = data {
@ -99,7 +99,7 @@ prints out the list properly."#
Ok(PipelineData::new(call.head)) Ok(PipelineData::new(call.head))
} }
} }
PipelineData::Value(Value::Record { cols, vals, .. }) => { PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
// dbg!("value::record"); // dbg!("value::record");
let mut items = vec![]; let mut items = vec![];

View File

@ -1,9 +1,11 @@
use super::color_config::style_primitive; use super::color_config::style_primitive;
use crate::viewers::color_config::get_color_config; use crate::viewers::color_config::get_color_config;
use lscolors::{LsColors, Style};
use nu_protocol::ast::{Call, PathMember}; use nu_protocol::ast::{Call, PathMember};
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Config, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value, Category, Config, DataSource, IntoPipelineData, PipelineData, PipelineMetadata, ShellError,
Signature, Span, Value, ValueStream,
}; };
use nu_table::{StyledString, TextStyle, Theme}; use nu_table::{StyledString, TextStyle, Theme};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@ -45,7 +47,7 @@ impl Command for Table {
}; };
match input { match input {
PipelineData::Value(Value::List { vals, .. }) => { PipelineData::Value(Value::List { vals, .. }, ..) => {
let table = convert_to_table(vals, ctrlc, &config)?; let table = convert_to_table(vals, ctrlc, &config)?;
if let Some(table) = table { if let Some(table) = table {
@ -60,7 +62,84 @@ impl Command for Table {
Ok(PipelineData::new(call.head)) Ok(PipelineData::new(call.head))
} }
} }
PipelineData::Stream(stream) => { PipelineData::Stream(stream, metadata) => {
let stream = match metadata {
Some(PipelineMetadata {
data_source: DataSource::Ls,
}) => {
let config = config.clone();
let ctrlc = ctrlc.clone();
let ls_colors = match stack.get_env_var("LS_COLORS") {
Some(s) => LsColors::from_string(&s),
None => LsColors::default(),
};
ValueStream::from_stream(
stream.map(move |mut x| match &mut x {
Value::Record { cols, vals, .. } => {
let mut idx = 0;
while idx < cols.len() {
if cols[idx] == "name" {
if let Some(Value::String { val: path, span }) =
vals.get(idx)
{
match std::fs::symlink_metadata(&path) {
Ok(metadata) => {
let style = ls_colors
.style_for_path_with_metadata(
path.clone(),
Some(&metadata),
);
let ansi_style = style
.map(Style::to_crossterm_style)
.unwrap_or_default();
let use_ls_colors = config.use_ls_colors;
if use_ls_colors {
vals[idx] = Value::String {
val: ansi_style
.apply(path)
.to_string(),
span: *span,
};
}
}
Err(_) => {
let style =
ls_colors.style_for_path(path.clone());
let ansi_style = style
.map(Style::to_crossterm_style)
.unwrap_or_default();
let use_ls_colors = config.use_ls_colors;
if use_ls_colors {
vals[idx] = Value::String {
val: ansi_style
.apply(path)
.to_string(),
span: *span,
};
}
}
}
}
}
idx += 1;
}
x
}
_ => x,
}),
ctrlc,
)
}
_ => stream,
};
let table = convert_to_table(stream, ctrlc, &config)?; let table = convert_to_table(stream, ctrlc, &config)?;
if let Some(table) = table { if let Some(table) = table {
@ -75,7 +154,7 @@ impl Command for Table {
Ok(PipelineData::new(call.head)) Ok(PipelineData::new(call.head))
} }
} }
PipelineData::Value(Value::Record { cols, vals, .. }) => { PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
let mut output = vec![]; let mut output = vec![];
for (c, v) in cols.into_iter().zip(vals.into_iter()) { for (c, v) in cols.into_iter().zip(vals.into_iter()) {
@ -105,8 +184,8 @@ impl Command for Table {
} }
.into_pipeline_data()) .into_pipeline_data())
} }
PipelineData::Value(Value::Error { error }) => Err(error), PipelineData::Value(Value::Error { error }, ..) => Err(error),
PipelineData::Value(Value::CustomValue { val, span }) => { PipelineData::Value(Value::CustomValue { val, span }, ..) => {
let base_pipeline = val.to_base_value(span)?.into_pipeline_data(); let base_pipeline = val.to_base_value(span)?.into_pipeline_data();
self.run(engine_state, stack, call, base_pipeline) self.run(engine_state, stack, call, base_pipeline)
} }

View File

@ -147,7 +147,7 @@ mod test {
assert!(call.has_flag("flag")); assert!(call.has_flag("flag"));
let required: f64 = call.req(0).unwrap(); let required: f64 = call.req(0).unwrap();
assert_eq!(required, 1.0); assert!((required - 1.0).abs() < f64::EPSILON);
let optional: Option<String> = call.opt(1).unwrap(); let optional: Option<String> = call.opt(1).unwrap();
assert_eq!(optional, Some("something".to_string())); assert_eq!(optional, Some("something".to_string()));

View File

@ -145,8 +145,8 @@ impl Command for PluginDeclaration {
})?; })?;
let input = match input { let input = match input {
PipelineData::Value(value) => value, PipelineData::Value(value, ..) => value,
PipelineData::Stream(stream) => { PipelineData::Stream(stream, ..) => {
let values = stream.collect::<Vec<Value>>(); let values = stream.collect::<Vec<Value>>();
Value::List { Value::List {
@ -192,7 +192,9 @@ impl Command for PluginDeclaration {
})?; })?;
match response { match response {
PluginResponse::Value(value) => Ok(PipelineData::Value(value.as_ref().clone())), PluginResponse::Value(value) => {
Ok(PipelineData::Value(value.as_ref().clone(), None))
}
PluginResponse::Error(msg) => Err(ShellError::LabeledError( PluginResponse::Error(msg) => Err(ShellError::LabeledError(
"Error received from plugin".into(), "Error received from plugin".into(),
msg, msg,

View File

@ -363,8 +363,8 @@ impl EngineState {
let decl = self.get_decl(decl_id); let decl = self.get_decl(decl_id);
match input { match input {
PipelineData::Stream(_) => decl, PipelineData::Stream(..) => decl,
PipelineData::Value(value) => match value { PipelineData::Value(value, ..) => match value {
Value::CustomValue { val, .. } => { Value::CustomValue { val, .. } => {
// This filter works because the custom definitions were declared // This filter works because the custom definitions were declared
// before the default nushell declarations. This means that the custom // before the default nushell declarations. This means that the custom

View File

@ -33,19 +33,29 @@ use crate::{ast::PathMember, Config, ShellError, Span, Value, ValueStream};
/// Nushell. /// Nushell.
#[derive(Debug)] #[derive(Debug)]
pub enum PipelineData { pub enum PipelineData {
Value(Value), Value(Value, Option<PipelineMetadata>),
Stream(ValueStream), Stream(ValueStream, Option<PipelineMetadata>),
}
#[derive(Debug)]
pub struct PipelineMetadata {
pub data_source: DataSource,
}
#[derive(Debug)]
pub enum DataSource {
Ls,
} }
impl PipelineData { impl PipelineData {
pub fn new(span: Span) -> PipelineData { pub fn new(span: Span) -> PipelineData {
PipelineData::Value(Value::Nothing { span }) PipelineData::Value(Value::Nothing { span }, None)
} }
pub fn into_value(self, span: Span) -> Value { pub fn into_value(self, span: Span) -> Value {
match self { match self {
PipelineData::Value(v) => v, PipelineData::Value(v, ..) => v,
PipelineData::Stream(s) => Value::List { PipelineData::Stream(s, ..) => Value::List {
vals: s.collect(), vals: s.collect(),
span, // FIXME? span, // FIXME?
}, },
@ -55,7 +65,7 @@ impl PipelineData {
pub fn into_interruptible_iter(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineIterator { pub fn into_interruptible_iter(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineIterator {
let mut iter = self.into_iter(); let mut iter = self.into_iter();
if let PipelineIterator(PipelineData::Stream(s)) = &mut iter { if let PipelineIterator(PipelineData::Stream(s, ..)) = &mut iter {
s.ctrlc = ctrlc; s.ctrlc = ctrlc;
} }
@ -64,20 +74,20 @@ impl PipelineData {
pub fn collect_string(self, separator: &str, config: &Config) -> String { pub fn collect_string(self, separator: &str, config: &Config) -> String {
match self { match self {
PipelineData::Value(v) => v.into_string(separator, config), PipelineData::Value(v, ..) => v.into_string(separator, config),
PipelineData::Stream(s) => s.into_string(separator, config), PipelineData::Stream(s, ..) => s.into_string(separator, config),
} }
} }
pub fn follow_cell_path(self, cell_path: &[PathMember]) -> Result<Value, ShellError> { pub fn follow_cell_path(self, cell_path: &[PathMember]) -> Result<Value, ShellError> {
match self { match self {
// FIXME: there are probably better ways of doing this // FIXME: there are probably better ways of doing this
PipelineData::Stream(stream) => Value::List { PipelineData::Stream(stream, ..) => Value::List {
vals: stream.collect(), vals: stream.collect(),
span: Span::unknown(), span: Span::unknown(),
} }
.follow_cell_path(cell_path), .follow_cell_path(cell_path),
PipelineData::Value(v) => v.follow_cell_path(cell_path), PipelineData::Value(v, ..) => v.follow_cell_path(cell_path),
} }
} }
@ -88,12 +98,12 @@ impl PipelineData {
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
match self { match self {
// FIXME: there are probably better ways of doing this // FIXME: there are probably better ways of doing this
PipelineData::Stream(stream) => Value::List { PipelineData::Stream(stream, ..) => Value::List {
vals: stream.collect(), vals: stream.collect(),
span: Span::unknown(), span: Span::unknown(),
} }
.update_cell_path(cell_path, callback), .update_cell_path(cell_path, callback),
PipelineData::Value(v) => v.update_cell_path(cell_path, callback), PipelineData::Value(v, ..) => v.update_cell_path(cell_path, callback),
} }
} }
@ -108,14 +118,14 @@ impl PipelineData {
F: FnMut(Value) -> Value + 'static + Send, F: FnMut(Value) -> Value + 'static + Send,
{ {
match self { match self {
PipelineData::Value(Value::List { vals, .. }) => { PipelineData::Value(Value::List { vals, .. }, ..) => {
Ok(vals.into_iter().map(f).into_pipeline_data(ctrlc)) Ok(vals.into_iter().map(f).into_pipeline_data(ctrlc))
} }
PipelineData::Stream(stream) => Ok(stream.map(f).into_pipeline_data(ctrlc)), PipelineData::Stream(stream, ..) => Ok(stream.map(f).into_pipeline_data(ctrlc)),
PipelineData::Value(Value::Range { val, .. }) => { PipelineData::Value(Value::Range { val, .. }, ..) => {
Ok(val.into_range_iter()?.map(f).into_pipeline_data(ctrlc)) Ok(val.into_range_iter()?.map(f).into_pipeline_data(ctrlc))
} }
PipelineData::Value(v) => match f(v) { PipelineData::Value(v, ..) => match f(v) {
Value::Error { error } => Err(error), Value::Error { error } => Err(error),
v => Ok(v.into_pipeline_data()), v => Ok(v.into_pipeline_data()),
}, },
@ -135,15 +145,17 @@ impl PipelineData {
F: FnMut(Value) -> U + 'static + Send, F: FnMut(Value) -> U + 'static + Send,
{ {
match self { match self {
PipelineData::Value(Value::List { vals, .. }) => { PipelineData::Value(Value::List { vals, .. }, ..) => {
Ok(vals.into_iter().map(f).flatten().into_pipeline_data(ctrlc)) Ok(vals.into_iter().map(f).flatten().into_pipeline_data(ctrlc))
} }
PipelineData::Stream(stream) => Ok(stream.map(f).flatten().into_pipeline_data(ctrlc)), PipelineData::Stream(stream, ..) => {
PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() { Ok(stream.map(f).flatten().into_pipeline_data(ctrlc))
}
PipelineData::Value(Value::Range { val, .. }, ..) => match val.into_range_iter() {
Ok(iter) => Ok(iter.map(f).flatten().into_pipeline_data(ctrlc)), Ok(iter) => Ok(iter.map(f).flatten().into_pipeline_data(ctrlc)),
Err(error) => Err(error), Err(error) => Err(error),
}, },
PipelineData::Value(v) => Ok(f(v).into_iter().into_pipeline_data(ctrlc)), PipelineData::Value(v, ..) => Ok(f(v).into_iter().into_pipeline_data(ctrlc)),
} }
} }
@ -157,14 +169,14 @@ impl PipelineData {
F: FnMut(&Value) -> bool + 'static + Send, F: FnMut(&Value) -> bool + 'static + Send,
{ {
match self { match self {
PipelineData::Value(Value::List { vals, .. }) => { PipelineData::Value(Value::List { vals, .. }, ..) => {
Ok(vals.into_iter().filter(f).into_pipeline_data(ctrlc)) Ok(vals.into_iter().filter(f).into_pipeline_data(ctrlc))
} }
PipelineData::Stream(stream) => Ok(stream.filter(f).into_pipeline_data(ctrlc)), PipelineData::Stream(stream, ..) => Ok(stream.filter(f).into_pipeline_data(ctrlc)),
PipelineData::Value(Value::Range { val, .. }) => { PipelineData::Value(Value::Range { val, .. }, ..) => {
Ok(val.into_range_iter()?.filter(f).into_pipeline_data(ctrlc)) Ok(val.into_range_iter()?.filter(f).into_pipeline_data(ctrlc))
} }
PipelineData::Value(v) => { PipelineData::Value(v, ..) => {
if f(&v) { if f(&v) {
Ok(v.into_pipeline_data()) Ok(v.into_pipeline_data())
} else { } else {
@ -190,22 +202,33 @@ impl IntoIterator for PipelineData {
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
match self { match self {
PipelineData::Value(Value::List { vals, .. }) => { PipelineData::Value(Value::List { vals, .. }, metadata) => {
PipelineIterator(PipelineData::Stream(ValueStream { PipelineIterator(PipelineData::Stream(
stream: Box::new(vals.into_iter()), ValueStream {
ctrlc: None, stream: Box::new(vals.into_iter()),
})) ctrlc: None,
},
metadata,
))
}
PipelineData::Value(Value::Range { val, .. }, metadata) => {
match val.into_range_iter() {
Ok(iter) => PipelineIterator(PipelineData::Stream(
ValueStream {
stream: Box::new(iter),
ctrlc: None,
},
metadata,
)),
Err(error) => PipelineIterator(PipelineData::Stream(
ValueStream {
stream: Box::new(std::iter::once(Value::Error { error })),
ctrlc: None,
},
metadata,
)),
}
} }
PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() {
Ok(iter) => PipelineIterator(PipelineData::Stream(ValueStream {
stream: Box::new(iter),
ctrlc: None,
})),
Err(error) => PipelineIterator(PipelineData::Stream(ValueStream {
stream: Box::new(std::iter::once(Value::Error { error })),
ctrlc: None,
})),
},
x => PipelineIterator(x), x => PipelineIterator(x),
} }
} }
@ -216,9 +239,9 @@ impl Iterator for PipelineIterator {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
match &mut self.0 { match &mut self.0 {
PipelineData::Value(Value::Nothing { .. }) => None, PipelineData::Value(Value::Nothing { .. }, ..) => None,
PipelineData::Value(v) => Some(std::mem::take(v)), PipelineData::Value(v, ..) => Some(std::mem::take(v)),
PipelineData::Stream(stream) => stream.next(), PipelineData::Stream(stream, ..) => stream.next(),
} }
} }
} }
@ -232,12 +255,17 @@ where
V: Into<Value>, V: Into<Value>,
{ {
fn into_pipeline_data(self) -> PipelineData { fn into_pipeline_data(self) -> PipelineData {
PipelineData::Value(self.into()) PipelineData::Value(self.into(), None)
} }
} }
pub trait IntoInterruptiblePipelineData { pub trait IntoInterruptiblePipelineData {
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData; fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData;
fn into_pipeline_data_with_metadata(
self,
metadata: PipelineMetadata,
ctrlc: Option<Arc<AtomicBool>>,
) -> PipelineData;
} }
impl<I> IntoInterruptiblePipelineData for I impl<I> IntoInterruptiblePipelineData for I
@ -247,9 +275,26 @@ where
<I::IntoIter as Iterator>::Item: Into<Value>, <I::IntoIter as Iterator>::Item: Into<Value>,
{ {
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData { fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData {
PipelineData::Stream(ValueStream { PipelineData::Stream(
stream: Box::new(self.into_iter().map(Into::into)), ValueStream {
ctrlc, stream: Box::new(self.into_iter().map(Into::into)),
}) ctrlc,
},
None,
)
}
fn into_pipeline_data_with_metadata(
self,
metadata: PipelineMetadata,
ctrlc: Option<Arc<AtomicBool>>,
) -> PipelineData {
PipelineData::Stream(
ValueStream {
stream: Box::new(self.into_iter().map(Into::into)),
ctrlc,
},
Some(metadata),
)
} }
} }

View File

@ -126,14 +126,14 @@ fn test2(call: &EvaluatedCall, input: &Value) -> Result<Value, ShellError> {
let vals = (0..3) let vals = (0..3)
.map(|v| Value::Int { .map(|v| Value::Int {
val: v * i, val: v * i,
span: call.head.clone(), span: call.head,
}) })
.collect::<Vec<Value>>(); .collect::<Vec<Value>>();
Value::Record { Value::Record {
cols: cols.clone(), cols: cols.clone(),
vals, vals,
span: call.head.clone(), span: call.head,
} }
}) })
.collect::<Vec<Value>>(); .collect::<Vec<Value>>();

View File

@ -19,7 +19,7 @@ use nu_parser::parse;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
IntoPipelineData, PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID, PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID,
}; };
use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt}; use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt};
@ -313,8 +313,8 @@ fn main() -> Result<()> {
} }
} }
fn print_value( fn print_pipeline_data(
value: Value, input: PipelineData,
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
@ -325,15 +325,13 @@ fn print_value(
let output = match engine_state.find_decl("table".as_bytes()) { let output = match engine_state.find_decl("table".as_bytes()) {
Some(decl_id) => { Some(decl_id) => {
let table = engine_state.get_decl(decl_id).run( let table =
engine_state, engine_state
stack, .get_decl(decl_id)
&Call::new(), .run(engine_state, stack, &Call::new(), input)?;
value.into_pipeline_data(),
)?;
table.collect_string("\n", &config) table.collect_string("\n", &config)
} }
None => value.into_string(", ", &config), None => input.collect_string(", ", &config),
}; };
let stdout = std::io::stdout(); let stdout = std::io::stdout();
@ -436,11 +434,7 @@ fn eval_source(
PipelineData::new(Span::unknown()), PipelineData::new(Span::unknown()),
) { ) {
Ok(pipeline_data) => { Ok(pipeline_data) => {
if let Err(err) = print_value( if let Err(err) = print_pipeline_data(pipeline_data, engine_state, stack) {
pipeline_data.into_value(Span::unknown()),
engine_state,
stack,
) {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &err); report_error(&working_set, &err);