Skip to content

Commit

Permalink
Add --data flag to metadata and set-metadata to allow persisting me…
Browse files Browse the repository at this point in the history
…tadata

This should help work around issues like nushell#7368 without having to
propagate metadata everywhere (which would be a much larger refactor).
  • Loading branch information
KoviRobi committed Nov 13, 2023
1 parent 0b25385 commit f05ef69
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 36 deletions.
76 changes: 43 additions & 33 deletions crates/nu-command/src/debug/metadata.rs
Expand Up @@ -27,6 +27,11 @@ impl Command for Metadata {
SyntaxShape::Any,
"the expression you want metadata for",
)
.switch(
"data",
"also add the data to the output record, under the key `data`",
Some('d'),
)
.category(Category::Debug)
}

Expand All @@ -39,6 +44,7 @@ impl Command for Metadata {
) -> Result<PipelineData, ShellError> {
let arg = call.positional_nth(0);
let head = call.head;
let include_data = call.has_flag("data");

match arg {
Some(Expression {
Expand All @@ -55,41 +61,35 @@ impl Command for Metadata {
let origin = stack.get_var_with_origin(*var_id, *span)?;

Ok(
build_metadata_record(&origin, input.metadata().as_ref(), head)
build_metadata_record(Some(&origin), input, include_data, head)
.into_pipeline_data(),
)
}
_ => {
let val: Value = call.req(engine_state, stack, 0)?;
Ok(build_metadata_record(&val, input.metadata().as_ref(), head)
.into_pipeline_data())
Ok(
build_metadata_record(Some(&val), input, include_data, head)
.into_pipeline_data(),
)
}
}
} else {
let val: Value = call.req(engine_state, stack, 0)?;
Ok(build_metadata_record(&val, input.metadata().as_ref(), head)
.into_pipeline_data())
Ok(
build_metadata_record(Some(&val), input, include_data, head)
.into_pipeline_data(),
)
}
}
Some(_) => {
let val: Value = call.req(engine_state, stack, 0)?;
Ok(build_metadata_record(&val, input.metadata().as_ref(), head)
.into_pipeline_data())
Ok(
build_metadata_record(Some(&val), input, include_data, head)
.into_pipeline_data(),
)
}
None => {
let mut record = Record::new();
if let Some(x) = input.metadata().as_ref() {
match x {
PipelineMetadata {
data_source: DataSource::Ls,
} => record.push("source", Value::string("ls", head)),
PipelineMetadata {
data_source: DataSource::HtmlThemes,
} => record.push("source", Value::string("into html --list", head)),
}
}

Ok(Value::record(record, head).into_pipeline_data())
Ok(build_metadata_record(None, input, include_data, head).into_pipeline_data())
}
}
}
Expand All @@ -106,26 +106,32 @@ impl Command for Metadata {
example: "ls | metadata",
result: None,
},
Example {
description: "Get the metadata of the input, along with the data",
example: "ls | metadata --data",
result: None,
},
]
}
}

fn build_metadata_record(arg: &Value, metadata: Option<&PipelineMetadata>, head: Span) -> Value {
fn build_metadata_record(arg: Option<&Value>, pipeline: PipelineData, include_data: bool, head: Span) -> Value {
let mut record = Record::new();

let span = arg.span();
record.push(
"span",
Value::record(
record! {
"start" => Value::int(span.start as i64,span),
"end" => Value::int(span.end as i64, span),
},
head,
),
);
if let Some(span) = arg.map(Value::span) {
record.push(
"span",
Value::record(
record! {
"start" => Value::int(span.start as i64,span),
"end" => Value::int(span.end as i64, span),
},
head,
),
);
}

if let Some(x) = metadata {
if let Some(x) = pipeline.metadata().as_ref() {
match x {
PipelineMetadata {
data_source: DataSource::Ls,
Expand All @@ -136,6 +142,10 @@ fn build_metadata_record(arg: &Value, metadata: Option<&PipelineMetadata>, head:
}
}

if include_data {
record.push("data", pipeline.into_value(head));
}

Value::record(record, head)
}

Expand Down
2 changes: 2 additions & 0 deletions crates/nu-command/src/debug/mod.rs
Expand Up @@ -5,6 +5,7 @@ mod info;
mod inspect;
mod inspect_table;
mod metadata;
mod set_metadata;
mod timeit;
mod view;
mod view_files;
Expand All @@ -18,6 +19,7 @@ pub use info::DebugInfo;
pub use inspect::Inspect;
pub use inspect_table::build_table;
pub use metadata::Metadata;
pub use set_metadata::SetMetadata;
pub use timeit::TimeIt;
pub use view::View;
pub use view_files::ViewFiles;
Expand Down
75 changes: 75 additions & 0 deletions crates/nu-command/src/debug/set_metadata.rs
@@ -0,0 +1,75 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, PipelineMetadata, Signature, SyntaxShape, Type, Value, Record
};

#[derive(Clone)]
pub struct SetMetadata;

impl Command for SetMetadata {
fn name(&self) -> &str {
"set-metadata"
}

fn usage(&self) -> &str {
"Assigns the metadata from the metadata argument into the stream"
}

fn signature(&self) -> nu_protocol::Signature {
Signature::build("set-metadata")
.input_output_types(vec![(Type::Any, Type::Any)])
.allow_variants_without_examples(true)
.required(
"metadata_var_name",
SyntaxShape::Record(vec![]),
"metadata variable name",
)
.category(Category::Debug)
}

fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let val: Value = call.req(engine_state, stack, 0)?;
let metadata = get_source(val);
Ok(input.set_metadata(metadata))
}

fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Set the metadata of a variable",
example: "let a = (ls | metadata --data); $a.data | set-metadata $md",
result: None,
}]
}
}

pub fn get_source(arg: Value) -> Option<PipelineMetadata> {
let source_val = match arg {
Value::Record { val: Record{cols, vals}, .. } => cols
.iter()
.zip(vals)
.find_map(|(col, val)| (col == "source").then_some(val)),
_ => return None,
}?;

source_val.as_string().ok()?.parse().ok()
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_examples() {
use crate::test_examples;

test_examples(SetMetadata {})
}
}
1 change: 1 addition & 0 deletions crates/nu-command/src/default_context.rs
Expand Up @@ -139,6 +139,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
Explain,
Inspect,
Metadata,
SetMetadata,
TimeIt,
View,
ViewFiles,
Expand Down
4 changes: 2 additions & 2 deletions crates/nu-protocol/Cargo.toml
Expand Up @@ -29,13 +29,13 @@ serde = { version = "1.0", default-features = false }
serde_json = { version = "1.0", optional = true }
thiserror = "1.0"
typetag = "0.2"
strum = "0.25"
strum_macros = "0.25"

[features]
plugin = ["serde_json"]

[dev-dependencies]
serde_json = "1.0"
strum = "0.25"
strum_macros = "0.25"
nu-test-support = { path = "../nu-test-support", version = "0.86.1" }
rstest = "0.18"
20 changes: 19 additions & 1 deletion crates/nu-protocol/src/pipeline_data.rs
Expand Up @@ -58,9 +58,27 @@ pub struct PipelineMetadata {
pub data_source: DataSource,
}

#[derive(Debug, Clone)]
impl std::fmt::Display for PipelineMetadata {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.data_source.fmt(f)
}
}

impl std::str::FromStr for PipelineMetadata {
type Err = strum::ParseError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(PipelineMetadata {
data_source: s.parse()?,
})
}
}

#[derive(Debug, Clone, strum_macros::Display, strum_macros::EnumString)]
pub enum DataSource {
#[strum(serialize = "ls")]
Ls,
#[strum(serialize = "to html --list")]
HtmlThemes,
}

Expand Down

0 comments on commit f05ef69

Please sign in to comment.