forked from nushell/nushell
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add let-with-metadata and set-metadata to allow persisting metadata
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
Showing
8 changed files
with
264 additions
and
53 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
crates/nu-command/src/core_commands/let_with_metadata.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
use nu_engine::eval_expression_with_input; | ||
use nu_protocol::ast::Call; | ||
use nu_protocol::engine::{Command, EngineState, Stack}; | ||
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type}; | ||
|
||
#[derive(Clone)] | ||
pub struct LetWithMetadata; | ||
|
||
impl Command for LetWithMetadata { | ||
fn name(&self) -> &str { | ||
"let-with-metadata" | ||
} | ||
|
||
fn usage(&self) -> &str { | ||
"Create a variable and give it a value." | ||
} | ||
|
||
fn signature(&self) -> nu_protocol::Signature { | ||
Signature::build("let-with-metadata") | ||
.input_output_types(vec![(Type::Nothing, Type::Nothing)]) | ||
.allow_variants_without_examples(true) | ||
.required("var_name", SyntaxShape::VarWithOptType, "variable name") | ||
.required( | ||
"metadata_var_name", | ||
SyntaxShape::VarWithOptType, | ||
"metadata variable name", | ||
) | ||
.required( | ||
"initial_value", | ||
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)), | ||
"equals sign followed by value", | ||
) | ||
.category(Category::Core) | ||
} | ||
|
||
fn extra_usage(&self) -> &str { | ||
r#"This command is a parser keyword. For details, check: | ||
https://www.nushell.sh/book/thinking_in_nu.html"# | ||
} | ||
|
||
fn is_parser_keyword(&self) -> bool { | ||
true | ||
} | ||
|
||
fn search_terms(&self) -> Vec<&str> { | ||
vec!["set", "const"] | ||
} | ||
|
||
fn run( | ||
&self, | ||
engine_state: &EngineState, | ||
stack: &mut Stack, | ||
call: &Call, | ||
input: PipelineData, | ||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { | ||
let var_id = call | ||
.positional_nth(0) | ||
.expect("checked through parser") | ||
.as_var() | ||
.expect("internal error: missing variable"); | ||
|
||
let metadata_var_id = call | ||
.positional_nth(1) | ||
.expect("checked through parser") | ||
.as_var() | ||
.expect("internal error: missing variable"); | ||
|
||
let keyword_expr = call | ||
.positional_nth(2) | ||
.expect("checked through parser") | ||
.as_keyword() | ||
.expect("internal error: missing keyword"); | ||
|
||
let rhs = eval_expression_with_input( | ||
engine_state, | ||
stack, | ||
keyword_expr, | ||
input, | ||
call.redirect_stdout, | ||
call.redirect_stderr, | ||
)? | ||
.0; | ||
|
||
let metadata = rhs.metadata(); | ||
let val = rhs.into_value(call.head.clone()); | ||
stack.add_var(var_id, val.clone()); | ||
stack.add_var( | ||
metadata_var_id, | ||
crate::metadata::build_metadata_record(val.span().ok(), &metadata, call.head), | ||
); | ||
|
||
Ok(PipelineData::empty()) | ||
} | ||
|
||
fn examples(&self) -> Vec<Example> { | ||
vec![ | ||
Example { | ||
description: "Set a variable to a value", | ||
example: "let-with-metadata x md = $in", | ||
result: None, | ||
}, | ||
Example { | ||
description: "Set a variable to the result of an expression", | ||
example: "let-with-metadata x md = ls", | ||
result: None, | ||
}, | ||
] | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use nu_protocol::engine::CommandType; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_examples() { | ||
use crate::test_examples; | ||
|
||
test_examples(LetWithMetadata {}) | ||
} | ||
|
||
#[test] | ||
fn test_command_type() { | ||
assert!(matches!( | ||
LetWithMetadata.command_type(), | ||
CommandType::Keyword | ||
)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
}; | ||
|
||
#[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, | ||
"metadata variable name", | ||
) | ||
.category(Category::Core) | ||
} | ||
|
||
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-with-metadata a md = ls; $a | set-metadata $md", | ||
result: None, | ||
}] | ||
} | ||
} | ||
|
||
pub fn get_source(arg: Value) -> Option<PipelineMetadata> { | ||
let source_val = match arg { | ||
Value::Record { cols, vals, .. } => cols | ||
.iter() | ||
.zip(vals) | ||
.find_map(|(col, val)| (col == "source").then_some(val)), | ||
_ => return None, | ||
}?; | ||
|
||
Some(source_val.as_string().ok()?.parse().ok()?) | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_examples() { | ||
use crate::test_examples; | ||
|
||
test_examples(SetMetadata {}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.