forked from extern/nushell
Remove lazy records (#12682)
# Description Removes lazy records from the language, following from the reasons outlined in #12622. Namely, this should make semantics more clear and will eliminate concerns regarding maintainability. # User-Facing Changes - Breaking change: `lazy make` is removed. - Breaking change: `describe --collect-lazyrecords` flag is removed. - `sys` and `debug info` now return regular records. # After Submitting - Update nushell book if necessary. - Explore new `sys` and `debug info` APIs to prevent them from taking too long (e.g., subcommands or taking an optional column/cell-path argument).
This commit is contained in:
@ -26,7 +26,6 @@ impl Command for Describe {
|
||||
"show detailed information about the value",
|
||||
Some('d'),
|
||||
)
|
||||
.switch("collect-lazyrecords", "collect lazy records", Some('l'))
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
@ -44,21 +43,7 @@ impl Command for Describe {
|
||||
let options = Options {
|
||||
no_collect: call.has_flag(engine_state, stack, "no-collect")?,
|
||||
detailed: call.has_flag(engine_state, stack, "detailed")?,
|
||||
collect_lazyrecords: call.has_flag(engine_state, stack, "collect-lazyrecords")?,
|
||||
};
|
||||
if options.collect_lazyrecords {
|
||||
nu_protocol::report_error_new(
|
||||
engine_state,
|
||||
&ShellError::GenericError {
|
||||
error: "Deprecated flag".into(),
|
||||
msg: "the `--collect-lazyrecords` flag is deprecated, since lazy records will be removed in 0.94.0"
|
||||
.into(),
|
||||
span: Some(call.head),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
run(Some(engine_state), call, input, options)
|
||||
}
|
||||
|
||||
@ -71,7 +56,6 @@ impl Command for Describe {
|
||||
let options = Options {
|
||||
no_collect: call.has_flag_const(working_set, "no-collect")?,
|
||||
detailed: call.has_flag_const(working_set, "detailed")?,
|
||||
collect_lazyrecords: call.has_flag_const(working_set, "collect-lazyrecords")?,
|
||||
};
|
||||
run(None, call, input, options)
|
||||
}
|
||||
@ -89,13 +73,11 @@ impl Command for Describe {
|
||||
"{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| print $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
|
||||
result: Some(Value::test_record(record!(
|
||||
"type" => Value::test_string("record"),
|
||||
"lazy" => Value::test_bool(false),
|
||||
"columns" => Value::test_record(record!(
|
||||
"shell" => Value::test_string("string"),
|
||||
"uwu" => Value::test_string("bool"),
|
||||
"features" => Value::test_record(record!(
|
||||
"type" => Value::test_string("record"),
|
||||
"lazy" => Value::test_bool(false),
|
||||
"columns" => Value::test_record(record!(
|
||||
"bugs" => Value::test_string("bool"),
|
||||
"multiplatform" => Value::test_string("bool"),
|
||||
@ -168,7 +150,6 @@ impl Command for Describe {
|
||||
struct Options {
|
||||
no_collect: bool,
|
||||
detailed: bool,
|
||||
collect_lazyrecords: bool,
|
||||
}
|
||||
|
||||
fn run(
|
||||
@ -243,7 +224,7 @@ fn run(
|
||||
if options.no_collect {
|
||||
Value::string("any", head)
|
||||
} else {
|
||||
describe_value(input.into_value(head), head, engine_state, options)?
|
||||
describe_value(input.into_value(head), head, engine_state, )
|
||||
}
|
||||
},
|
||||
"metadata" => metadata_to_value(metadata, head),
|
||||
@ -264,7 +245,7 @@ fn run(
|
||||
if !options.detailed {
|
||||
Value::string(value.get_type().to_string(), head)
|
||||
} else {
|
||||
describe_value(value, head, engine_state, options)?
|
||||
describe_value(value, head, engine_state)
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -288,9 +269,8 @@ fn describe_value(
|
||||
value: Value,
|
||||
head: nu_protocol::Span,
|
||||
engine_state: Option<&EngineState>,
|
||||
options: Options,
|
||||
) -> Result<Value, ShellError> {
|
||||
Ok(match value {
|
||||
) -> Value {
|
||||
match value {
|
||||
Value::Custom { val, .. } => Value::record(
|
||||
record!(
|
||||
"type" => Value::string("custom", head),
|
||||
@ -320,14 +300,12 @@ fn describe_value(
|
||||
std::mem::take(v),
|
||||
head,
|
||||
engine_state,
|
||||
options,
|
||||
)?);
|
||||
));
|
||||
}
|
||||
|
||||
Value::record(
|
||||
record!(
|
||||
"type" => Value::string("record", head),
|
||||
"lazy" => Value::bool(false, head),
|
||||
"columns" => Value::record(val, head),
|
||||
),
|
||||
head,
|
||||
@ -338,11 +316,9 @@ fn describe_value(
|
||||
"type" => Value::string("list", head),
|
||||
"length" => Value::int(vals.len() as i64, head),
|
||||
"values" => Value::list(vals.into_iter().map(|v|
|
||||
Ok(compact_primitive_description(
|
||||
describe_value(v, head, engine_state, options)?
|
||||
))
|
||||
compact_primitive_description(describe_value(v, head, engine_state))
|
||||
)
|
||||
.collect::<Result<Vec<Value>, ShellError>>()?, head),
|
||||
.collect(), head),
|
||||
),
|
||||
head,
|
||||
),
|
||||
@ -394,42 +370,7 @@ fn describe_value(
|
||||
),
|
||||
head,
|
||||
),
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let mut record = Record::new();
|
||||
|
||||
record.push("type", Value::string("record", head));
|
||||
record.push("lazy", Value::bool(true, head));
|
||||
|
||||
if options.collect_lazyrecords {
|
||||
let collected = val.collect()?;
|
||||
if let Value::Record { val, .. } =
|
||||
describe_value(collected, head, engine_state, options)?
|
||||
{
|
||||
let mut val = Record::clone(&val);
|
||||
|
||||
for (_k, v) in val.iter_mut() {
|
||||
*v = compact_primitive_description(describe_value(
|
||||
std::mem::take(v),
|
||||
head,
|
||||
engine_state,
|
||||
options,
|
||||
)?);
|
||||
}
|
||||
|
||||
record.push("length", Value::int(val.len() as i64, head));
|
||||
record.push("columns", Value::record(val, head));
|
||||
} else {
|
||||
let cols = val.column_names();
|
||||
record.push("length", Value::int(cols.len() as i64, head));
|
||||
}
|
||||
} else {
|
||||
let cols = val.column_names();
|
||||
record.push("length", Value::int(cols.len() as i64, head));
|
||||
}
|
||||
|
||||
Value::record(record, head)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn metadata_to_value(metadata: Option<Box<PipelineMetadata>>, head: nu_protocol::Span) -> Value {
|
||||
|
@ -1,179 +0,0 @@
|
||||
use nu_engine::{command_prelude::*, eval_block};
|
||||
use nu_protocol::{debugger::WithoutDebug, engine::Closure, LazyRecord};
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LazyMake;
|
||||
|
||||
impl Command for LazyMake {
|
||||
fn name(&self) -> &str {
|
||||
"lazy make"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("lazy make")
|
||||
.input_output_types(vec![(Type::Nothing, Type::record())])
|
||||
.required_named(
|
||||
"columns",
|
||||
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
||||
"Closure that gets called when the LazyRecord needs to list the available column names",
|
||||
Some('c')
|
||||
)
|
||||
.required_named(
|
||||
"get-value",
|
||||
SyntaxShape::Closure(Some(vec![SyntaxShape::String])),
|
||||
"Closure to call when a value needs to be produced on demand",
|
||||
Some('g')
|
||||
)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create a lazy record."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
"Lazy records are special records that only evaluate their values once the property is requested.
|
||||
For example, when printing a lazy record, all of its fields will be collected. But when accessing
|
||||
a specific property, only it will be evaluated.
|
||||
|
||||
Note that this is unrelated to the lazyframes feature bundled with dataframes."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["deferred", "record", "procedural"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
nu_protocol::report_error_new(
|
||||
engine_state,
|
||||
&ShellError::GenericError {
|
||||
error: "Deprecated command".into(),
|
||||
msg: "warning: lazy records and the `lazy make` command will be removed in 0.94.0"
|
||||
.into(),
|
||||
span: Some(call.head),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
},
|
||||
);
|
||||
|
||||
let span = call.head;
|
||||
let columns: Vec<Spanned<String>> = call
|
||||
.get_flag(engine_state, stack, "columns")?
|
||||
.expect("required flag");
|
||||
|
||||
let get_value: Closure = call
|
||||
.get_flag(engine_state, stack, "get-value")?
|
||||
.expect("required flag");
|
||||
|
||||
let mut unique = HashMap::with_capacity(columns.len());
|
||||
|
||||
for col in &columns {
|
||||
match unique.entry(&col.item) {
|
||||
Entry::Occupied(entry) => {
|
||||
return Err(ShellError::ColumnDefinedTwice {
|
||||
col_name: col.item.clone(),
|
||||
second_use: col.span,
|
||||
first_use: *entry.get(),
|
||||
});
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(col.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let stack = stack.clone().reset_out_dest().capture();
|
||||
|
||||
Ok(Value::lazy_record(
|
||||
Box::new(NuLazyRecord {
|
||||
engine_state: engine_state.clone(),
|
||||
stack: Arc::new(Mutex::new(stack)),
|
||||
columns: columns.into_iter().map(|s| s.item).collect(),
|
||||
get_value,
|
||||
span,
|
||||
}),
|
||||
span,
|
||||
)
|
||||
.into_pipeline_data())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
// TODO: Figure out how to "test" these examples, or leave results as None
|
||||
Example {
|
||||
description: "Create a lazy record",
|
||||
example: r#"lazy make --columns ["haskell", "futures", "nushell"] --get-value { |lazything| $lazything + "!" }"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Test the laziness of lazy records",
|
||||
example: r#"lazy make --columns ["hello"] --get-value { |key| print $"getting ($key)!"; $key | str upcase }"#,
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct NuLazyRecord {
|
||||
engine_state: EngineState,
|
||||
stack: Arc<Mutex<Stack>>,
|
||||
columns: Vec<String>,
|
||||
get_value: Closure,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for NuLazyRecord {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("NuLazyRecord").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LazyRecord<'a> for NuLazyRecord {
|
||||
fn column_names(&'a self) -> Vec<&'a str> {
|
||||
self.columns.iter().map(|column| column.as_str()).collect()
|
||||
}
|
||||
|
||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
||||
let block = self.engine_state.get_block(self.get_value.block_id);
|
||||
let mut stack = self.stack.lock().expect("lock must not be poisoned");
|
||||
let column_value = Value::string(column, self.span);
|
||||
|
||||
if let Some(var) = block.signature.get_positional(0) {
|
||||
if let Some(var_id) = &var.var_id {
|
||||
stack.add_var(*var_id, column_value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let pipeline_result = eval_block::<WithoutDebug>(
|
||||
&self.engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
PipelineData::Value(column_value, None),
|
||||
);
|
||||
|
||||
pipeline_result.map(|data| match data {
|
||||
PipelineData::Value(value, ..) => value,
|
||||
// TODO: Proper error handling.
|
||||
_ => Value::nothing(self.span),
|
||||
})
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn clone_value(&self, span: Span) -> Value {
|
||||
Value::lazy_record(Box::new((*self).clone()), span)
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ mod hide;
|
||||
mod hide_env;
|
||||
mod if_;
|
||||
mod ignore;
|
||||
mod lazy_make;
|
||||
mod let_;
|
||||
mod loop_;
|
||||
mod match_;
|
||||
@ -58,7 +57,6 @@ pub use hide::Hide;
|
||||
pub use hide_env::HideEnv;
|
||||
pub use if_::If;
|
||||
pub use ignore::Ignore;
|
||||
pub use lazy_make::LazyMake;
|
||||
pub use let_::Let;
|
||||
pub use loop_::Loop;
|
||||
pub use match_::Match;
|
||||
|
@ -43,7 +43,6 @@ pub fn create_default_context() -> EngineState {
|
||||
OverlayList,
|
||||
OverlayNew,
|
||||
OverlayHide,
|
||||
LazyMake,
|
||||
Let,
|
||||
Loop,
|
||||
Match,
|
||||
|
@ -306,10 +306,6 @@ impl<'a> std::fmt::Debug for DebuggableValue<'a> {
|
||||
Value::Custom { val, .. } => {
|
||||
write!(f, "CustomValue({:?})", val)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let rec = val.collect().map_err(|_| std::fmt::Error)?;
|
||||
write!(f, "LazyRecord({:?})", DebuggableValue(&rec))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user