From 7f0921a14b8d85e64c702e93feb2cd2defd00975 Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Fri, 24 Dec 2021 11:16:50 +1100 Subject: [PATCH] Add metadata command (#569) * Add metadata command * Add string interpolation to testing --- crates/nu-command/src/core_commands/for_.rs | 35 +++--- .../nu-command/src/core_commands/metadata.rs | 103 ++++++++++++++++++ crates/nu-command/src/core_commands/mod.rs | 2 + crates/nu-command/src/default_context.rs | 1 + crates/nu-command/src/example_test.rs | 3 + crates/nu-protocol/src/pipeline_data.rs | 11 +- 6 files changed, 135 insertions(+), 20 deletions(-) create mode 100644 crates/nu-command/src/core_commands/metadata.rs diff --git a/crates/nu-command/src/core_commands/for_.rs b/crates/nu-command/src/core_commands/for_.rs index 4a0f54f78c..574b24f258 100644 --- a/crates/nu-command/src/core_commands/for_.rs +++ b/crates/nu-command/src/core_commands/for_.rs @@ -168,24 +168,23 @@ impl Command for For { span, }), }, - // FIXME? Numbered `for` is kinda strange, but was supported in previous nushell - // Example { - // description: "Number each item and echo a message", - // example: "for $it in ['bob' 'fred'] --numbered { $\"($it.index) is ($it.item)\" }", - // result: Some(Value::List { - // vals: vec![ - // Value::String { - // val: "0 is bob".into(), - // span, - // }, - // Value::String { - // val: "0 is fred".into(), - // span, - // }, - // ], - // span, - // }), - // }, + Example { + description: "Number each item and echo a message", + example: "for $it in ['bob' 'fred'] --numbered { $\"($it.index) is ($it.item)\" }", + result: Some(Value::List { + vals: vec![ + Value::String { + val: "0 is bob".into(), + span, + }, + Value::String { + val: "1 is fred".into(), + span, + }, + ], + span, + }), + }, ] } } diff --git a/crates/nu-command/src/core_commands/metadata.rs b/crates/nu-command/src/core_commands/metadata.rs new file mode 100644 index 0000000000..17f9310998 --- /dev/null +++ b/crates/nu-command/src/core_commands/metadata.rs @@ -0,0 +1,103 @@ +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, DataSource, Example, PipelineData, PipelineMetadata, Signature, Value, +}; + +#[derive(Clone)] +pub struct Metadata; + +impl Command for Metadata { + fn name(&self) -> &str { + "metadata" + } + + fn usage(&self) -> &str { + "Get the metadata for items in the stream" + } + + fn signature(&self) -> nu_protocol::Signature { + Signature::build("metadata").category(Category::Core) + } + + fn run( + &self, + engine_state: &EngineState, + _stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let head = call.head; + let ctrlc = engine_state.ctrlc.clone(); + + let metadata = input.metadata(); + + input.map( + move |x| { + let span = x.span(); + + let mut cols = vec![]; + let mut vals = vec![]; + + cols.push("span".into()); + if let Ok(span) = span { + vals.push(Value::Record { + cols: vec!["start".into(), "end".into()], + vals: vec![ + Value::Int { + val: span.start as i64, + span, + }, + Value::Int { + val: span.end as i64, + span, + }, + ], + span: head, + }); + } + + if let Some(x) = &metadata { + match x { + PipelineMetadata { + data_source: DataSource::Ls, + } => { + cols.push("source".into()); + vals.push(Value::String { + val: "ls".into(), + span: head, + }) + } + } + } + + Value::Record { + cols, + vals, + span: head, + } + }, + ctrlc, + ) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Get the metadata of a value", + example: "3 | metadata", + result: None, + }] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(Metadata {}) + } +} diff --git a/crates/nu-command/src/core_commands/mod.rs b/crates/nu-command/src/core_commands/mod.rs index dc58df2f73..c15be09e60 100644 --- a/crates/nu-command/src/core_commands/mod.rs +++ b/crates/nu-command/src/core_commands/mod.rs @@ -13,6 +13,7 @@ mod hide; mod history; mod if_; mod let_; +mod metadata; mod module; mod source; mod use_; @@ -33,6 +34,7 @@ pub use hide::Hide; pub use history::History; pub use if_::If; pub use let_::Let; +pub use metadata::Metadata; pub use module::Module; pub use source::Source; pub use use_::Use; diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 1bfe1b8ca9..dcf090eedf 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -38,6 +38,7 @@ pub fn create_default_context() -> EngineState { History, If, Let, + Metadata, Module, Source, Use, diff --git a/crates/nu-command/src/example_test.rs b/crates/nu-command/src/example_test.rs index 66c3a0e53c..b44f6a0978 100644 --- a/crates/nu-command/src/example_test.rs +++ b/crates/nu-command/src/example_test.rs @@ -16,6 +16,8 @@ use super::{Ansi, Date, From, Into, Math, Path, Random, Split, Str, StrCollect, #[cfg(test)] pub fn test_examples(cmd: impl Command + 'static) { + use crate::BuildString; + let examples = cmd.examples(); let mut engine_state = Box::new(EngineState::new()); @@ -25,6 +27,7 @@ pub fn test_examples(cmd: impl Command + 'static) { let mut working_set = StateWorkingSet::new(&*engine_state); working_set.add_decl(Box::new(Str)); working_set.add_decl(Box::new(StrCollect)); + working_set.add_decl(Box::new(BuildString)); working_set.add_decl(Box::new(From)); working_set.add_decl(Box::new(To)); working_set.add_decl(Box::new(Into)); diff --git a/crates/nu-protocol/src/pipeline_data.rs b/crates/nu-protocol/src/pipeline_data.rs index f8769c56e7..d26a5c1d19 100644 --- a/crates/nu-protocol/src/pipeline_data.rs +++ b/crates/nu-protocol/src/pipeline_data.rs @@ -37,12 +37,12 @@ pub enum PipelineData { Stream(ValueStream, Option), } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PipelineMetadata { pub data_source: DataSource, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum DataSource { Ls, } @@ -52,6 +52,13 @@ impl PipelineData { PipelineData::Value(Value::Nothing { span }, None) } + pub fn metadata(&self) -> Option { + match self { + PipelineData::Stream(_, x) => x.clone(), + PipelineData::Value(_, x) => x.clone(), + } + } + pub fn into_value(self, span: Span) -> Value { match self { PipelineData::Value(Value::Nothing { .. }, ..) => Value::nothing(span),