forked from extern/nushell
Create Record
type (#10103)
# Description This PR creates a new `Record` type to reduce duplicate code and possibly bugs as well. (This is an edited version of #9648.) - `Record` implements `FromIterator` and `IntoIterator` and so can be iterated over or collected into. For example, this helps with conversions to and from (hash)maps. (Also, no more `cols.iter().zip(vals)`!) - `Record` has a `push(col, val)` function to help insure that the number of columns is equal to the number of values. I caught a few potential bugs thanks to this (e.g. in the `ls` command). - Finally, this PR also adds a `record!` macro that helps simplify record creation. It is used like so: ```rust record! { "key1" => some_value, "key2" => Value::string("text", span), "key3" => Value::int(optional_int.unwrap_or(0), span), "key4" => Value::bool(config.setting, span), } ``` Since macros hinder formatting, etc., the right hand side values should be relatively short and sweet like the examples above. Where possible, prefer `record!` or `.collect()` on an iterator instead of multiple `Record::push`s, since the first two automatically set the record capacity and do less work overall. # User-Facing Changes Besides the changes in `nu-protocol` the only other breaking changes are to `nu-table::{ExpandedTable::build_map, JustTable::kv_table}`.
This commit is contained in:
@ -2,7 +2,8 @@ use nu_engine::{eval_block, eval_expression, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Block, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
record, Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -91,11 +92,13 @@ impl Command for For {
|
||||
stack.add_var(
|
||||
var_id,
|
||||
if numbered {
|
||||
Value::Record {
|
||||
cols: vec!["index".into(), "item".into()],
|
||||
vals: vec![Value::int(idx as i64, head), x],
|
||||
span: head,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"index" => Value::int(idx as i64, head),
|
||||
"item" => x,
|
||||
},
|
||||
head,
|
||||
)
|
||||
} else {
|
||||
x
|
||||
},
|
||||
@ -135,11 +138,13 @@ impl Command for For {
|
||||
stack.add_var(
|
||||
var_id,
|
||||
if numbered {
|
||||
Value::Record {
|
||||
cols: vec!["index".into(), "item".into()],
|
||||
vals: vec![Value::int(idx as i64, head), x],
|
||||
span: head,
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"index" => Value::int(idx as i64, head),
|
||||
"item" => x,
|
||||
},
|
||||
head,
|
||||
)
|
||||
} else {
|
||||
x
|
||||
},
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Block, Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
record, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -137,15 +137,14 @@ fn intercept_block_control(error: ShellError) -> Result<ShellError, ShellError>
|
||||
|
||||
/// Convert from `error` to [`Value::Record`] so the error information can be easily accessed in catch.
|
||||
fn err_to_record(error: ShellError, head: Span) -> Value {
|
||||
let cols = vec!["msg".to_string(), "debug".to_string(), "raw".to_string()];
|
||||
let vals = vec![
|
||||
Value::string(error.to_string(), head),
|
||||
Value::string(format!("{error:?}"), head),
|
||||
Value::Error {
|
||||
error: Box::new(error),
|
||||
Value::record(
|
||||
record! {
|
||||
"msg" => Value::string(error.to_string(), head),
|
||||
"debug" => Value::string(format!("{error:?}"), head),
|
||||
"raw" => Value::error(error),
|
||||
},
|
||||
];
|
||||
Value::record(cols, vals, head)
|
||||
head,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Type, Value,
|
||||
};
|
||||
use shadow_rs::shadow;
|
||||
|
||||
@ -63,74 +63,64 @@ pub fn version(
|
||||
// - build_rust_channel
|
||||
// - features
|
||||
// - installed_plugins
|
||||
let mut cols = Vec::with_capacity(12);
|
||||
let mut vals = Vec::with_capacity(12);
|
||||
let mut record = Record::with_capacity(12);
|
||||
|
||||
cols.push("version".to_string());
|
||||
vals.push(Value::string(env!("CARGO_PKG_VERSION"), call.head));
|
||||
record.push(
|
||||
"version",
|
||||
Value::string(env!("CARGO_PKG_VERSION"), call.head),
|
||||
);
|
||||
|
||||
cols.push("branch".to_string());
|
||||
vals.push(Value::string(build::BRANCH, call.head));
|
||||
record.push("branch", Value::string(build::BRANCH, call.head));
|
||||
|
||||
let commit_hash = option_env!("NU_COMMIT_HASH");
|
||||
if let Some(commit_hash) = commit_hash {
|
||||
cols.push("commit_hash".to_string());
|
||||
vals.push(Value::string(commit_hash, call.head));
|
||||
record.push("commit_hash", Value::string(commit_hash, call.head));
|
||||
}
|
||||
|
||||
let build_os = Some(build::BUILD_OS).filter(|x| !x.is_empty());
|
||||
if let Some(build_os) = build_os {
|
||||
cols.push("build_os".to_string());
|
||||
vals.push(Value::string(build_os, call.head));
|
||||
record.push("build_os", Value::string(build_os, call.head));
|
||||
}
|
||||
|
||||
let build_target = Some(build::BUILD_TARGET).filter(|x| !x.is_empty());
|
||||
if let Some(build_target) = build_target {
|
||||
cols.push("build_target".to_string());
|
||||
vals.push(Value::string(build_target, call.head));
|
||||
record.push("build_target", Value::string(build_target, call.head));
|
||||
}
|
||||
|
||||
let rust_version = Some(build::RUST_VERSION).filter(|x| !x.is_empty());
|
||||
if let Some(rust_version) = rust_version {
|
||||
cols.push("rust_version".to_string());
|
||||
vals.push(Value::string(rust_version, call.head));
|
||||
record.push("rust_version", Value::string(rust_version, call.head));
|
||||
}
|
||||
|
||||
let rust_channel = Some(build::RUST_CHANNEL).filter(|x| !x.is_empty());
|
||||
if let Some(rust_channel) = rust_channel {
|
||||
cols.push("rust_channel".to_string());
|
||||
vals.push(Value::string(rust_channel, call.head));
|
||||
record.push("rust_channel", Value::string(rust_channel, call.head));
|
||||
}
|
||||
|
||||
let cargo_version = Some(build::CARGO_VERSION).filter(|x| !x.is_empty());
|
||||
if let Some(cargo_version) = cargo_version {
|
||||
cols.push("cargo_version".to_string());
|
||||
vals.push(Value::string(cargo_version, call.head));
|
||||
record.push("cargo_version", Value::string(cargo_version, call.head));
|
||||
}
|
||||
|
||||
let build_time = Some(build::BUILD_TIME).filter(|x| !x.is_empty());
|
||||
if let Some(build_time) = build_time {
|
||||
cols.push("build_time".to_string());
|
||||
vals.push(Value::string(build_time, call.head));
|
||||
record.push("build_time", Value::string(build_time, call.head));
|
||||
}
|
||||
|
||||
let build_rust_channel = Some(build::BUILD_RUST_CHANNEL).filter(|x| !x.is_empty());
|
||||
if let Some(build_rust_channel) = build_rust_channel {
|
||||
cols.push("build_rust_channel".to_string());
|
||||
vals.push(Value::string(build_rust_channel, call.head));
|
||||
record.push(
|
||||
"build_rust_channel",
|
||||
Value::string(build_rust_channel, call.head),
|
||||
);
|
||||
}
|
||||
|
||||
cols.push("allocator".to_string());
|
||||
vals.push(Value::String {
|
||||
val: global_allocator().to_string(),
|
||||
span: call.head,
|
||||
});
|
||||
record.push("allocator", Value::string(global_allocator(), call.head));
|
||||
|
||||
cols.push("features".to_string());
|
||||
vals.push(Value::String {
|
||||
val: features_enabled().join(", "),
|
||||
span: call.head,
|
||||
});
|
||||
record.push(
|
||||
"features",
|
||||
Value::string(features_enabled().join(", "), call.head),
|
||||
);
|
||||
|
||||
// Get a list of command names and check for plugins
|
||||
let installed_plugins = engine_state
|
||||
@ -139,18 +129,12 @@ pub fn version(
|
||||
.map(|x| x.name())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
cols.push("installed_plugins".to_string());
|
||||
vals.push(Value::String {
|
||||
val: installed_plugins.join(", "),
|
||||
span: call.head,
|
||||
});
|
||||
record.push(
|
||||
"installed_plugins",
|
||||
Value::string(installed_plugins.join(", "), call.head),
|
||||
);
|
||||
|
||||
Ok(Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
Ok(Value::record(record, call.head).into_pipeline_data())
|
||||
}
|
||||
|
||||
fn global_allocator() -> &'static str {
|
||||
|
Reference in New Issue
Block a user