mirror of
https://github.com/nushell/nushell.git
synced 2025-04-30 16:14:27 +02:00
Error make (#948)
* Add `error make` and improve `metadata` * Allow metadata to work on just a pipeline
This commit is contained in:
parent
e45e8109aa
commit
8a93548de2
125
crates/nu-command/src/core_commands/error_make.rs
Normal file
125
crates/nu-command/src/core_commands/error_make.rs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ErrorMake;
|
||||||
|
|
||||||
|
impl Command for ErrorMake {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"error make"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("error make")
|
||||||
|
.optional("error-struct", SyntaxShape::Record, "the error to create")
|
||||||
|
.category(Category::Core)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Create an error."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let span = call.head;
|
||||||
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let arg: Option<Value> = call.opt(engine_state, stack, 0)?;
|
||||||
|
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
Ok(make_error(&arg)
|
||||||
|
.map(|err| Value::Error { error: err })
|
||||||
|
.unwrap_or_else(|| Value::Error {
|
||||||
|
error: ShellError::SpannedLabeledError(
|
||||||
|
"Creating error value not supported.".into(),
|
||||||
|
"unsupported error format".into(),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.into_pipeline_data())
|
||||||
|
} else {
|
||||||
|
input.map(
|
||||||
|
move |value| {
|
||||||
|
make_error(&value)
|
||||||
|
.map(|err| Value::Error { error: err })
|
||||||
|
.unwrap_or_else(|| Value::Error {
|
||||||
|
error: ShellError::SpannedLabeledError(
|
||||||
|
"Creating error value not supported.".into(),
|
||||||
|
"unsupported error format".into(),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
ctrlc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Creates a labeled error",
|
||||||
|
example: r#"{msg: "The message" , label: {start: 0, end: 141, text: "Helpful message here"}} | error make"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Creates a labeled error, using a call argument",
|
||||||
|
example: r#"error make {msg: "The message" , label: {start: 0, end: 141, text: "Helpful message here"}}"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Create a custom error for a custom command",
|
||||||
|
example: r#"def foo [x] {
|
||||||
|
let span = (metadata $x).span;
|
||||||
|
error make {msg: "this is fishy", label: {text: "fish right here", start: $span.start, end: $span.end } }
|
||||||
|
}"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_error(value: &Value) -> Option<ShellError> {
|
||||||
|
if let Value::Record { .. } = &value {
|
||||||
|
let msg = value.get_data_by_key("msg");
|
||||||
|
let label = value.get_data_by_key("label");
|
||||||
|
|
||||||
|
match (msg, &label) {
|
||||||
|
(Some(Value::String { val: message, .. }), Some(label)) => {
|
||||||
|
let label_start = label.get_data_by_key("start");
|
||||||
|
let label_end = label.get_data_by_key("end");
|
||||||
|
let label_text = label.get_data_by_key("text");
|
||||||
|
|
||||||
|
match (label_start, label_end, label_text) {
|
||||||
|
(
|
||||||
|
Some(Value::Int { val: start, .. }),
|
||||||
|
Some(Value::Int { val: end, .. }),
|
||||||
|
Some(Value::String {
|
||||||
|
val: label_text, ..
|
||||||
|
}),
|
||||||
|
) => Some(ShellError::SpannedLabeledError(
|
||||||
|
message,
|
||||||
|
label_text,
|
||||||
|
Span {
|
||||||
|
start: start as usize,
|
||||||
|
end: end as usize,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
use nu_protocol::ast::Call;
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::ast::{Call, Expr, Expression};
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, DataSource, Example, PipelineData, PipelineMetadata, Signature, Value,
|
Category, DataSource, Example, IntoPipelineData, PipelineData, PipelineMetadata, Signature,
|
||||||
|
Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -17,47 +19,61 @@ impl Command for Metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("metadata").category(Category::Core)
|
Signature::build("metadata")
|
||||||
|
.optional(
|
||||||
|
"expression",
|
||||||
|
SyntaxShape::Any,
|
||||||
|
"the expression you want metadata for",
|
||||||
|
)
|
||||||
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let arg = call.positional.get(0);
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
|
|
||||||
let metadata = input.metadata();
|
match arg {
|
||||||
|
Some(Expression {
|
||||||
input.map(
|
expr: Expr::FullCellPath(full_cell_path),
|
||||||
move |x| {
|
span,
|
||||||
let span = x.span();
|
..
|
||||||
|
}) => {
|
||||||
|
if full_cell_path.tail.is_empty() {
|
||||||
|
match &full_cell_path.head {
|
||||||
|
Expression {
|
||||||
|
expr: Expr::Var(var_id),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let origin = stack.get_var_with_origin(*var_id, *span)?;
|
||||||
|
|
||||||
|
Ok(build_metadata_record(&origin, &input.metadata(), head)
|
||||||
|
.into_pipeline_data())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let val: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
Ok(build_metadata_record(&val, &input.metadata(), head)
|
||||||
|
.into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let val: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
Ok(build_metadata_record(&val, &input.metadata(), head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
let val: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
Ok(build_metadata_record(&val, &input.metadata(), head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
None => {
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
if let Some(x) = &input.metadata() {
|
||||||
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 {
|
match x {
|
||||||
PipelineMetadata {
|
PipelineMetadata {
|
||||||
data_source: DataSource::Ls,
|
data_source: DataSource::Ls,
|
||||||
@ -71,22 +87,72 @@ impl Command for Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Record {
|
Ok(Value::Record {
|
||||||
cols,
|
cols,
|
||||||
vals,
|
vals,
|
||||||
span: head,
|
span: head,
|
||||||
}
|
}
|
||||||
},
|
.into_pipeline_data())
|
||||||
ctrlc,
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
description: "Get the metadata of a value",
|
Example {
|
||||||
example: "3 | metadata",
|
description: "Get the metadata of a variable",
|
||||||
result: None,
|
example: "metadata $a",
|
||||||
}]
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Get the metadata of the input",
|
||||||
|
example: "ls | metadata",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_metadata_record(arg: &Value, metadata: &Option<PipelineMetadata>, head: Span) -> Value {
|
||||||
|
let mut cols = vec![];
|
||||||
|
let mut vals = vec![];
|
||||||
|
|
||||||
|
if let Ok(span) = arg.span() {
|
||||||
|
cols.push("span".into());
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ mod def_env;
|
|||||||
mod describe;
|
mod describe;
|
||||||
mod do_;
|
mod do_;
|
||||||
mod echo;
|
mod echo;
|
||||||
|
mod error_make;
|
||||||
mod export;
|
mod export;
|
||||||
mod export_def;
|
mod export_def;
|
||||||
mod export_def_env;
|
mod export_def_env;
|
||||||
@ -30,6 +31,7 @@ pub use def_env::DefEnv;
|
|||||||
pub use describe::Describe;
|
pub use describe::Describe;
|
||||||
pub use do_::Do;
|
pub use do_::Do;
|
||||||
pub use echo::Echo;
|
pub use echo::Echo;
|
||||||
|
pub use error_make::ErrorMake;
|
||||||
pub use export::ExportCommand;
|
pub use export::ExportCommand;
|
||||||
pub use export_def::ExportDef;
|
pub use export_def::ExportDef;
|
||||||
pub use export_def_env::ExportDefEnv;
|
pub use export_def_env::ExportDefEnv;
|
||||||
|
@ -33,6 +33,7 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
|
|||||||
Do,
|
Do,
|
||||||
Du,
|
Du,
|
||||||
Echo,
|
Echo,
|
||||||
|
ErrorMake,
|
||||||
ExportCommand,
|
ExportCommand,
|
||||||
ExportDef,
|
ExportDef,
|
||||||
ExportDefEnv,
|
ExportDefEnv,
|
||||||
|
@ -65,6 +65,14 @@ impl Stack {
|
|||||||
Err(ShellError::VariableNotFoundAtRuntime(span))
|
Err(ShellError::VariableNotFoundAtRuntime(span))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_var_with_origin(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
|
||||||
|
if let Some(v) = self.vars.get(&var_id) {
|
||||||
|
return Ok(v.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ShellError::VariableNotFoundAtRuntime(span))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_var(&mut self, var_id: VarId, value: Value) {
|
pub fn add_var(&mut self, var_id: VarId, value: Value) {
|
||||||
self.vars.insert(var_id, value);
|
self.vars.insert(var_id, value);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user