mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 00:13:21 +01:00
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:
parent
ad6deadf24
commit
847646e44e
@ -267,24 +267,6 @@ fn nested_suggestions(
|
||||
|
||||
output
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
// Add all the columns as completion
|
||||
for column_name in val.column_names() {
|
||||
output.push(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: column_name.to_string(),
|
||||
description: None,
|
||||
style: None,
|
||||
extra: None,
|
||||
span: current_span,
|
||||
append_whitespace: false,
|
||||
},
|
||||
kind: Some(kind.clone()),
|
||||
});
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
Value::List { vals, .. } => {
|
||||
for column_name in get_columns(vals.as_slice()) {
|
||||
output.push(SemanticSuggestion {
|
||||
@ -321,17 +303,6 @@ fn recursive_value(val: &Value, sublevels: &[Vec<u8>]) -> Result<Value, Span> {
|
||||
Err(span)
|
||||
}
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
for col in val.column_names() {
|
||||
if col.as_bytes() == *sublevel {
|
||||
let val = val.get_column_value(col).map_err(|_| span)?;
|
||||
return recursive_value(&val, next_sublevels);
|
||||
}
|
||||
}
|
||||
|
||||
// Current sublevel value not found
|
||||
Err(span)
|
||||
}
|
||||
Value::List { vals, .. } => {
|
||||
for col in get_columns(vals.as_slice()) {
|
||||
if col.as_bytes() == *sublevel {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,10 +106,9 @@ impl<'a> StyleComputer<'a> {
|
||||
Value::Binary { .. } => TextStyle::with_style(Left, s),
|
||||
Value::CellPath { .. } => TextStyle::with_style(Left, s),
|
||||
Value::Record { .. } | Value::List { .. } => TextStyle::with_style(Left, s),
|
||||
Value::Closure { .. }
|
||||
| Value::Custom { .. }
|
||||
| Value::Error { .. }
|
||||
| Value::LazyRecord { .. } => TextStyle::basic_left(),
|
||||
Value::Closure { .. } | Value::Custom { .. } | Value::Error { .. } => {
|
||||
TextStyle::basic_left()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,10 +272,6 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
),
|
||||
Value::LazyRecord { val, .. } => match val.collect() {
|
||||
Ok(val) => debug_string_without_formatting(&val),
|
||||
Err(error) => format!("{error:?}"),
|
||||
},
|
||||
//TODO: It would be good to drill deeper into closures.
|
||||
Value::Closure { val, .. } => format!("<Closure {}>", val.block_id),
|
||||
Value::Nothing { .. } => String::new(),
|
||||
|
@ -1,5 +1,4 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::LazyRecord;
|
||||
use sysinfo::{MemoryRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
|
||||
|
||||
const ENV_PATH_SEPARATOR_CHAR: char = {
|
||||
@ -39,14 +38,10 @@ impl Command for DebugInfo {
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = Span::unknown();
|
||||
|
||||
let record = LazySystemInfoRecord { span };
|
||||
|
||||
Ok(Value::lazy_record(Box::new(record), span).into_pipeline_data())
|
||||
Ok(all_columns(call.head).into_pipeline_data())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -58,93 +53,52 @@ impl Command for DebugInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct LazySystemInfoRecord {
|
||||
span: Span,
|
||||
}
|
||||
fn all_columns(span: Span) -> Value {
|
||||
let rk = RefreshKind::new()
|
||||
.with_processes(ProcessRefreshKind::everything())
|
||||
.with_memory(MemoryRefreshKind::everything());
|
||||
|
||||
// only get information requested
|
||||
let sys = System::new_with_specifics(rk);
|
||||
|
||||
impl LazySystemInfoRecord {
|
||||
fn get_column_value_with_system(
|
||||
&self,
|
||||
column: &str,
|
||||
system_option: Option<&System>,
|
||||
) -> Result<Value, ShellError> {
|
||||
let pid = Pid::from(std::process::id() as usize);
|
||||
match column {
|
||||
"thread_id" => Ok(Value::int(get_thread_id() as i64, self.span)),
|
||||
"pid" => Ok(Value::int(pid.as_u32() as i64, self.span)),
|
||||
"ppid" => {
|
||||
// only get information requested
|
||||
let system_opt = SystemOpt::from((system_option, || {
|
||||
RefreshKind::new().with_processes(ProcessRefreshKind::everything())
|
||||
}));
|
||||
|
||||
let system = system_opt.get_system();
|
||||
// get the process information for the nushell pid
|
||||
let pinfo = system.process(pid);
|
||||
|
||||
Ok(pinfo
|
||||
let ppid = {
|
||||
sys.process(pid)
|
||||
.and_then(|p| p.parent())
|
||||
.map(|p| Value::int(p.as_u32() as i64, self.span))
|
||||
.unwrap_or(Value::nothing(self.span)))
|
||||
}
|
||||
"system" => {
|
||||
// only get information requested
|
||||
let system_opt = SystemOpt::from((system_option, || {
|
||||
RefreshKind::new().with_memory(MemoryRefreshKind::everything())
|
||||
}));
|
||||
.map(|p| Value::int(p.as_u32().into(), span))
|
||||
.unwrap_or(Value::nothing(span))
|
||||
};
|
||||
|
||||
let system = system_opt.get_system();
|
||||
|
||||
Ok(Value::record(
|
||||
let system = Value::record(
|
||||
record! {
|
||||
"total_memory" => Value::filesize(system.total_memory() as i64, self.span),
|
||||
"free_memory" => Value::filesize(system.free_memory() as i64, self.span),
|
||||
"used_memory" => Value::filesize(system.used_memory() as i64, self.span),
|
||||
"available_memory" => Value::filesize(system.available_memory() as i64, self.span),
|
||||
"total_memory" => Value::filesize(sys.total_memory() as i64, span),
|
||||
"free_memory" => Value::filesize(sys.free_memory() as i64, span),
|
||||
"used_memory" => Value::filesize(sys.used_memory() as i64, span),
|
||||
"available_memory" => Value::filesize(sys.available_memory() as i64, span),
|
||||
},
|
||||
self.span,
|
||||
))
|
||||
}
|
||||
"process" => {
|
||||
// only get information requested
|
||||
let system_opt = SystemOpt::from((system_option, || {
|
||||
RefreshKind::new().with_processes(ProcessRefreshKind::everything())
|
||||
}));
|
||||
span,
|
||||
);
|
||||
|
||||
let system = system_opt.get_system();
|
||||
let pinfo = system.process(pid);
|
||||
let process = if let Some(p) = sys.process(pid) {
|
||||
let root = if let Some(path) = p.exe().and_then(|p| p.parent()) {
|
||||
Value::string(path.to_string_lossy().to_string(), span)
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
};
|
||||
|
||||
if let Some(p) = pinfo {
|
||||
Ok(Value::record(
|
||||
record! {
|
||||
"memory" => Value::filesize(p.memory() as i64, self.span),
|
||||
"virtual_memory" => Value::filesize(p.virtual_memory() as i64, self.span),
|
||||
"status" => Value::string(p.status().to_string(), self.span),
|
||||
"root" => {
|
||||
if let Some(path) = p.exe().and_then(|p| p.parent()) {
|
||||
Value::string(path.to_string_lossy().to_string(), self.span)
|
||||
let cwd = if let Some(path) = p.cwd() {
|
||||
Value::string(path.to_string_lossy().to_string(), span)
|
||||
} else {
|
||||
Value::nothing(self.span)
|
||||
}
|
||||
},
|
||||
"cwd" => {
|
||||
if let Some(path) = p.cwd() {
|
||||
Value::string(path.to_string_lossy().to_string(), self.span)
|
||||
Value::nothing(span)
|
||||
};
|
||||
|
||||
let exe_path = if let Some(path) = p.exe() {
|
||||
Value::string(path.to_string_lossy().to_string(), span)
|
||||
} else {
|
||||
Value::nothing(self.span)
|
||||
}
|
||||
},
|
||||
"exe_path" => {
|
||||
if let Some(path)= p.exe() {
|
||||
Value::string(path.to_string_lossy().to_string(), self.span)
|
||||
}else{
|
||||
Value::nothing(self.span)
|
||||
}
|
||||
},
|
||||
"command" => Value::string(p.cmd().join(" "), self.span),
|
||||
"name" => Value::string(p.name().to_string(), self.span),
|
||||
"environment" => {
|
||||
Value::nothing(span)
|
||||
};
|
||||
|
||||
let environment = {
|
||||
let mut env_rec = Record::new();
|
||||
for val in p.environ() {
|
||||
if let Some((key, value)) = val.split_once('=') {
|
||||
@ -152,7 +106,10 @@ impl LazySystemInfoRecord {
|
||||
{
|
||||
#[cfg(target_family = "windows")]
|
||||
{
|
||||
key == "Path" || key == "PATHEXT" || key == "PSMODULEPATH" || key == "PSModulePath"
|
||||
key == "Path"
|
||||
|| key == "PATHEXT"
|
||||
|| key == "PSMODULEPATH"
|
||||
|| key == "PSModulePath"
|
||||
}
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
{
|
||||
@ -161,104 +118,54 @@ impl LazySystemInfoRecord {
|
||||
}
|
||||
};
|
||||
if is_env_var_a_list {
|
||||
let items = value.split(ENV_PATH_SEPARATOR_CHAR).map(|r| Value::string(r.to_string(), self.span)).collect::<Vec<_>>();
|
||||
env_rec.push(key.to_string(), Value::list(items, self.span));
|
||||
} else if key == "LS_COLORS" { // LS_COLORS is a special case, it's a colon separated list of key=value pairs
|
||||
let items = value.split(':').map(|r| Value::string(r.to_string(), self.span)).collect::<Vec<_>>();
|
||||
env_rec.push(key.to_string(), Value::list(items, self.span));
|
||||
let items = value
|
||||
.split(ENV_PATH_SEPARATOR_CHAR)
|
||||
.map(|r| Value::string(r.to_string(), span))
|
||||
.collect::<Vec<_>>();
|
||||
env_rec.push(key.to_string(), Value::list(items, span));
|
||||
} else if key == "LS_COLORS" {
|
||||
// LS_COLORS is a special case, it's a colon separated list of key=value pairs
|
||||
let items = value
|
||||
.split(':')
|
||||
.map(|r| Value::string(r.to_string(), span))
|
||||
.collect::<Vec<_>>();
|
||||
env_rec.push(key.to_string(), Value::list(items, span));
|
||||
} else {
|
||||
env_rec.push(key.to_string(), Value::string(value.to_string(), self.span));
|
||||
env_rec.push(key.to_string(), Value::string(value.to_string(), span));
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::record(env_rec, self.span)
|
||||
},
|
||||
},
|
||||
self.span,
|
||||
))
|
||||
} else {
|
||||
// If we can't get the process information, just return the system information
|
||||
// only get information requested
|
||||
let system_opt = SystemOpt::from((system_option, || {
|
||||
RefreshKind::new().with_memory(MemoryRefreshKind::everything())
|
||||
}));
|
||||
let system = system_opt.get_system();
|
||||
Value::record(env_rec, span)
|
||||
};
|
||||
|
||||
Ok(Value::record(
|
||||
Value::record(
|
||||
record! {
|
||||
"total_memory" => Value::filesize(system.total_memory() as i64, self.span),
|
||||
"free_memory" => Value::filesize(system.free_memory() as i64, self.span),
|
||||
"used_memory" => Value::filesize(system.used_memory() as i64, self.span),
|
||||
"available_memory" => Value::filesize(system.available_memory() as i64, self.span),
|
||||
"memory" => Value::filesize(p.memory() as i64, span),
|
||||
"virtual_memory" => Value::filesize(p.virtual_memory() as i64, span),
|
||||
"status" => Value::string(p.status().to_string(), span),
|
||||
"root" => root,
|
||||
"cwd" => cwd,
|
||||
"exe_path" => exe_path,
|
||||
"command" => Value::string(p.cmd().join(" "), span),
|
||||
"name" => Value::string(p.name(), span),
|
||||
"environment" => environment,
|
||||
},
|
||||
self.span,
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::IncompatibleParametersSingle {
|
||||
msg: format!("Unknown column: {}", column),
|
||||
span: self.span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
span,
|
||||
)
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
};
|
||||
|
||||
impl<'a> LazyRecord<'a> for LazySystemInfoRecord {
|
||||
fn column_names(&'a self) -> Vec<&'a str> {
|
||||
vec!["thread_id", "pid", "ppid", "process", "system"]
|
||||
}
|
||||
|
||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
||||
self.get_column_value_with_system(column, None)
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn clone_value(&self, span: Span) -> Value {
|
||||
Value::lazy_record(Box::new(LazySystemInfoRecord { span }), span)
|
||||
}
|
||||
|
||||
fn collect(&'a self) -> Result<Value, ShellError> {
|
||||
let rk = RefreshKind::new()
|
||||
.with_processes(ProcessRefreshKind::everything())
|
||||
.with_memory(MemoryRefreshKind::everything());
|
||||
// only get information requested
|
||||
let system = System::new_with_specifics(rk);
|
||||
|
||||
self.column_names()
|
||||
.into_iter()
|
||||
.map(|col| {
|
||||
let val = self.get_column_value_with_system(col, Some(&system))?;
|
||||
Ok((col.to_owned(), val))
|
||||
})
|
||||
.collect::<Result<Record, _>>()
|
||||
.map(|record| Value::record(record, self.span()))
|
||||
}
|
||||
}
|
||||
|
||||
enum SystemOpt<'a> {
|
||||
Ptr(&'a System),
|
||||
Owned(Box<System>),
|
||||
}
|
||||
|
||||
impl<'a> SystemOpt<'a> {
|
||||
fn get_system(&'a self) -> &'a System {
|
||||
match self {
|
||||
SystemOpt::Ptr(system) => system,
|
||||
SystemOpt::Owned(system) => system,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F: Fn() -> RefreshKind> From<(Option<&'a System>, F)> for SystemOpt<'a> {
|
||||
fn from((system_opt, refresh_kind_create): (Option<&'a System>, F)) -> Self {
|
||||
match system_opt {
|
||||
Some(system) => SystemOpt::<'a>::Ptr(system),
|
||||
None => SystemOpt::Owned(Box::new(System::new_with_specifics(refresh_kind_create()))),
|
||||
}
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"thread_id" => Value::int(get_thread_id() as i64, span),
|
||||
"pid" => Value::int(pid.as_u32().into(), span),
|
||||
"ppid" => ppid,
|
||||
"system" => system,
|
||||
"process" => process,
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_thread_id() -> u64 {
|
||||
|
@ -105,18 +105,6 @@ fn getcol(
|
||||
.into_pipeline_data(ctrlc)
|
||||
.set_metadata(metadata))
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
Ok({
|
||||
// Unfortunate casualty to LazyRecord's column_names not generating 'static strs
|
||||
let cols: Vec<_> =
|
||||
val.column_names().iter().map(|s| s.to_string()).collect();
|
||||
|
||||
cols.into_iter()
|
||||
.map(move |x| Value::string(x, head))
|
||||
.into_pipeline_data(ctrlc)
|
||||
.set_metadata(metadata)
|
||||
})
|
||||
}
|
||||
Value::Record { val, .. } => Ok(val
|
||||
.into_owned()
|
||||
.into_iter()
|
||||
|
@ -533,15 +533,6 @@ fn value_should_be_printed(
|
||||
Value::Record { val, .. } => {
|
||||
record_matches_term(val, columns_to_search, filter_config, term, span)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => match val.collect() {
|
||||
Ok(val) => match val {
|
||||
Value::Record { val, .. } => {
|
||||
record_matches_term(&val, columns_to_search, filter_config, term, span)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
Err(_) => false,
|
||||
},
|
||||
Value::Binary { .. } => false,
|
||||
});
|
||||
if invert {
|
||||
|
@ -44,12 +44,6 @@ impl Command for Items {
|
||||
match input {
|
||||
PipelineData::Empty => Ok(PipelineData::Empty),
|
||||
PipelineData::Value(value, ..) => {
|
||||
let value = if let Value::LazyRecord { val, .. } = value {
|
||||
val.collect()?
|
||||
} else {
|
||||
value
|
||||
};
|
||||
|
||||
let span = value.span();
|
||||
match value {
|
||||
Value::Record { val, .. } => {
|
||||
|
@ -161,20 +161,6 @@ fn values(
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_pipeline_data_with_metadata(metadata, ctrlc)),
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let record = match val.collect()? {
|
||||
Value::Record { val, .. } => val,
|
||||
_ => Err(ShellError::NushellFailedSpanned {
|
||||
msg: "`LazyRecord::collect()` promises `Value::Record`".into(),
|
||||
label: "Violating lazy record found here".into(),
|
||||
span,
|
||||
})?,
|
||||
};
|
||||
Ok(record
|
||||
.into_owned()
|
||||
.into_values()
|
||||
.into_pipeline_data_with_metadata(metadata, ctrlc))
|
||||
}
|
||||
// Propagate errors
|
||||
Value::Error { error, .. } => Err(*error),
|
||||
other => Err(ShellError::OnlySupportsThisInputType {
|
||||
|
@ -135,10 +135,6 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
|
||||
}
|
||||
nu_json::Value::Object(m)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let collected = val.collect()?;
|
||||
value_to_json_value(&collected)?
|
||||
}
|
||||
Value::Custom { val, .. } => {
|
||||
let collected = val.to_base_value(span)?;
|
||||
value_to_json_value(&collected)?
|
||||
|
@ -246,9 +246,6 @@ pub(crate) fn write_value(
|
||||
Value::Custom { val, .. } => {
|
||||
write_value(out, &val.to_base_value(span)?, depth)?;
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
write_value(out, &val.collect()?, depth)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -130,10 +130,6 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
||||
.map(|(x, y)| format!("{}: {}", x, local_into_string(y, ", ", config)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(separator),
|
||||
Value::LazyRecord { val, .. } => match val.collect() {
|
||||
Ok(val) => local_into_string(val, separator, config),
|
||||
Err(error) => format!("{error:?}"),
|
||||
},
|
||||
Value::Closure { val, .. } => format!("<Closure {}>", val.block_id),
|
||||
Value::Nothing { .. } => String::new(),
|
||||
Value::Error { error, .. } => format!("{error:?}"),
|
||||
|
@ -62,10 +62,6 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result<toml::Value, ShellErr
|
||||
}
|
||||
toml::Value::Table(m)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let collected = val.collect()?;
|
||||
helper(engine_state, &collected)?
|
||||
}
|
||||
Value::List { vals, .. } => toml::Value::Array(toml_list(engine_state, vals)?),
|
||||
Value::Closure { .. } => {
|
||||
let code = engine_state.get_span_contents(span);
|
||||
|
@ -62,10 +62,6 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
|
||||
}
|
||||
serde_yaml::Value::Mapping(m)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let collected = val.collect()?;
|
||||
value_to_yaml_value(&collected)?
|
||||
}
|
||||
Value::List { vals, .. } => {
|
||||
let mut out = vec![];
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
use chrono::{DateTime, Local};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::LazyRecord;
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
use sysinfo::{
|
||||
Components, CpuRefreshKind, Disks, Networks, System, Users, MINIMUM_CPU_UPDATE_INTERVAL,
|
||||
@ -32,10 +31,7 @@ impl Command for Sys {
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = call.span();
|
||||
let ret = Value::lazy_record(Box::new(SysResult { span }), span);
|
||||
|
||||
Ok(ret.into_pipeline_data())
|
||||
Ok(all_columns(call.head).into_pipeline_data())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -64,36 +60,18 @@ pub struct SysResult {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl LazyRecord<'_> for SysResult {
|
||||
fn column_names(&self) -> Vec<&'static str> {
|
||||
vec!["host", "cpu", "disks", "mem", "temp", "net"]
|
||||
}
|
||||
|
||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
||||
let span = self.span;
|
||||
|
||||
match column {
|
||||
"host" => Ok(host(span)),
|
||||
"cpu" => Ok(cpu(span)),
|
||||
"disks" => Ok(disks(span)),
|
||||
"mem" => Ok(mem(span)),
|
||||
"temp" => Ok(temp(span)),
|
||||
"net" => Ok(net(span)),
|
||||
_ => Err(ShellError::LazyRecordAccessFailed {
|
||||
message: format!("Could not find column '{column}'"),
|
||||
column_name: column.to_string(),
|
||||
fn all_columns(span: Span) -> Value {
|
||||
Value::record(
|
||||
record! {
|
||||
"host" => host(span),
|
||||
"cpu" => cpu(span),
|
||||
"disks" => disks(span),
|
||||
"mem" => mem(span),
|
||||
"temp" => temp(span),
|
||||
"net" => net(span),
|
||||
},
|
||||
span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn clone_value(&self, span: Span) -> Value {
|
||||
Value::lazy_record(Box::new((*self).clone()), span)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn trim_cstyle_null(s: String) -> String {
|
||||
|
@ -394,10 +394,6 @@ fn handle_table_command(
|
||||
input.data = PipelineData::Empty;
|
||||
handle_record(input, cfg, val.into_owned())
|
||||
}
|
||||
PipelineData::Value(Value::LazyRecord { val, .. }, ..) => {
|
||||
input.data = val.collect()?.into_pipeline_data();
|
||||
handle_table_command(input, cfg)
|
||||
}
|
||||
PipelineData::Value(Value::Error { error, .. }, ..) => {
|
||||
// Propagate this error outward, so that it goes to stderr
|
||||
// instead of stdout.
|
||||
|
@ -98,21 +98,6 @@ fn insert_uses_enumerate_index() {
|
||||
assert_eq!(actual.out, "[[index, a, b]; [0, 7, 8], [1, 6, 8]]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_support_lazy_record() {
|
||||
let actual =
|
||||
nu!(r#"let x = (lazy make -c ["h"] -g {|a| $a | str upcase}); $x | insert a 10 | get a"#);
|
||||
assert_eq!(actual.out, "10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lazy_record_test_values() {
|
||||
let actual = nu!(
|
||||
r#"lazy make --columns ["haskell", "futures", "nushell"] --get-value { |lazything| $lazything + "!" } | values | length"#
|
||||
);
|
||||
assert_eq!(actual.out, "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deep_cell_path_creates_all_nested_records() {
|
||||
let actual = nu!("{a: {}} | insert a.b.c 0 | get a.b.c");
|
||||
|
@ -103,13 +103,6 @@ fn update_uses_enumerate_index() {
|
||||
assert_eq!(actual.out, "[[index, a]; [0, 8], [1, 8]]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_support_lazy_record() {
|
||||
let actual =
|
||||
nu!(r#"let x = (lazy make -c ["h"] -g {|a| $a | str upcase}); $x | update h 10 | get h"#);
|
||||
assert_eq!(actual.out, "10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_replacement_closure() {
|
||||
let actual = nu!("[1, 2] | update 1 {|i| $i + 1 } | to nuon");
|
||||
|
@ -112,17 +112,6 @@ fn upsert_past_end_of_list_stream() {
|
||||
.contains("can't insert at index (the next available index is 3)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upsert_support_lazy_record() {
|
||||
let actual =
|
||||
nu!(r#"let x = (lazy make -c ["h"] -g {|a| $a | str upcase}); $x | upsert h 10 | get h"#);
|
||||
assert_eq!(actual.out, "10");
|
||||
|
||||
let actual =
|
||||
nu!(r#"let x = (lazy make -c ["h"] -g {|a| $a | str upcase}); $x | upsert aa 10 | get aa"#);
|
||||
assert_eq!(actual.out, "10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deep_cell_path_creates_all_nested_records() {
|
||||
let actual = nu!("{a: {}} | upsert a.b.c 0 | get a.b.c");
|
||||
|
@ -112,10 +112,6 @@ pub fn collect_input(value: Value) -> Result<(Vec<String>, Vec<Vec<Value>>)> {
|
||||
|
||||
Ok((vec![String::from("")], lines))
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let materialized = val.collect()?;
|
||||
collect_input(materialized)
|
||||
}
|
||||
Value::Nothing { .. } => Ok((vec![], vec![])),
|
||||
Value::Custom { val, .. } => {
|
||||
let materialized = val.to_base_value(span)?;
|
||||
|
@ -2,8 +2,6 @@ use nu_protocol::{CustomValue, IntoSpanned, ShellError, Spanned, Value};
|
||||
|
||||
/// Do something with all [`CustomValue`]s recursively within a `Value`. This is not limited to
|
||||
/// plugin custom values.
|
||||
///
|
||||
/// `LazyRecord`s will be collected to plain values for completeness.
|
||||
pub fn with_custom_values_in<E>(
|
||||
value: &mut Value,
|
||||
mut f: impl FnMut(Spanned<&mut Box<dyn CustomValue>>) -> Result<(), E>,
|
||||
@ -18,13 +16,6 @@ where
|
||||
// Operate on a CustomValue.
|
||||
f(val.into_spanned(span))
|
||||
}
|
||||
// LazyRecord would be a problem for us, since it could return something else the
|
||||
// next time, and we have to collect it anyway to serialize it. Collect it in place,
|
||||
// and then use the result
|
||||
Value::LazyRecord { val, .. } => {
|
||||
*value = val.collect()?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
})
|
||||
@ -33,31 +24,7 @@ where
|
||||
#[test]
|
||||
fn find_custom_values() {
|
||||
use nu_plugin_protocol::test_util::test_plugin_custom_value;
|
||||
use nu_protocol::{engine::Closure, record, LazyRecord, Span};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Lazy;
|
||||
impl<'a> LazyRecord<'a> for Lazy {
|
||||
fn column_names(&'a self) -> Vec<&'a str> {
|
||||
vec!["custom", "plain"]
|
||||
}
|
||||
|
||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
||||
Ok(match column {
|
||||
"custom" => Value::test_custom_value(Box::new(test_plugin_custom_value())),
|
||||
"plain" => Value::test_int(42),
|
||||
_ => unimplemented!(),
|
||||
})
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
Span::test_data()
|
||||
}
|
||||
|
||||
fn clone_value(&self, span: Span) -> Value {
|
||||
Value::lazy_record(Box::new(self.clone()), span)
|
||||
}
|
||||
}
|
||||
use nu_protocol::{engine::Closure, record};
|
||||
|
||||
let mut cv = Value::test_custom_value(Box::new(test_plugin_custom_value()));
|
||||
|
||||
@ -73,7 +40,6 @@ fn find_custom_values() {
|
||||
captures: vec![(0, cv.clone()), (1, Value::test_string("foo"))]
|
||||
}
|
||||
),
|
||||
"lazy" => Value::test_lazy_record(Box::new(Lazy)),
|
||||
});
|
||||
|
||||
// Do with_custom_values_in, and count the number of custom values found
|
||||
@ -83,7 +49,7 @@ fn find_custom_values() {
|
||||
Ok(())
|
||||
})
|
||||
.expect("error");
|
||||
assert_eq!(4, found, "found in value");
|
||||
assert_eq!(3, found, "found in value");
|
||||
|
||||
// Try it on bare custom value too
|
||||
found = 0;
|
||||
|
@ -179,11 +179,6 @@ impl PluginCustomValue {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
// Collect LazyRecord before proceeding
|
||||
Value::LazyRecord { ref val, .. } => {
|
||||
*value = val.collect()?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
})
|
||||
@ -205,11 +200,6 @@ impl PluginCustomValue {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
// Collect LazyRecord before proceeding
|
||||
Value::LazyRecord { ref val, .. } => {
|
||||
*value = val.collect()?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
})
|
||||
@ -224,11 +214,6 @@ impl PluginCustomValue {
|
||||
*value = val.to_base_value(span)?;
|
||||
Ok(())
|
||||
}
|
||||
// Collect LazyRecord before proceeding
|
||||
Value::LazyRecord { ref val, .. } => {
|
||||
*value = val.collect()?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
})
|
||||
|
@ -344,9 +344,6 @@ impl PluginTest {
|
||||
// All equal, and same length
|
||||
Ok(true)
|
||||
}
|
||||
// Must collect lazy records to compare.
|
||||
(Value::LazyRecord { val: a_val, .. }, _) => self.value_eq(&a_val.collect()?, b),
|
||||
(_, Value::LazyRecord { val: b_val, .. }) => self.value_eq(a, &b_val.collect()?),
|
||||
// Fall back to regular eq.
|
||||
_ => Ok(a == b),
|
||||
}
|
||||
|
@ -381,11 +381,6 @@ pub(crate) fn render_examples(
|
||||
plugin.custom_value_to_base_value(engine, val.into_spanned(span))?;
|
||||
Ok::<_, ShellError>(())
|
||||
}
|
||||
// Collect LazyRecord before proceeding
|
||||
Value::LazyRecord { ref val, .. } => {
|
||||
*value = val.collect()?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
})?;
|
||||
|
@ -1178,16 +1178,6 @@ pub enum ShellError {
|
||||
span: Option<Span>,
|
||||
},
|
||||
|
||||
/// An attempt to access a record column failed.
|
||||
#[error("Access failure: {message}")]
|
||||
#[diagnostic(code(nu::shell::lazy_record_access_failed))]
|
||||
LazyRecordAccessFailed {
|
||||
message: String,
|
||||
column_name: String,
|
||||
#[label("Could not access '{column_name}' on this record")]
|
||||
span: Span,
|
||||
},
|
||||
|
||||
/// Operation interrupted by user
|
||||
#[error("Operation interrupted by user")]
|
||||
InterruptedByUser {
|
||||
|
@ -1,29 +0,0 @@
|
||||
use crate::{Record, ShellError, Span, Value};
|
||||
use std::fmt;
|
||||
|
||||
// Trait definition for a lazy record (where columns are evaluated on-demand)
|
||||
// typetag is needed to make this implement Serialize+Deserialize... even though we should never actually serialize a LazyRecord.
|
||||
// To serialize a LazyRecord, collect it into a Value::Record with collect() first.
|
||||
pub trait LazyRecord<'a>: fmt::Debug + Send + Sync {
|
||||
// All column names
|
||||
fn column_names(&'a self) -> Vec<&'a str>;
|
||||
|
||||
// Get 1 specific column value
|
||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError>;
|
||||
|
||||
fn span(&self) -> Span;
|
||||
|
||||
// Convert the lazy record into a regular Value::Record by collecting all its columns
|
||||
fn collect(&'a self) -> Result<Value, ShellError> {
|
||||
self.column_names()
|
||||
.into_iter()
|
||||
.map(|col| {
|
||||
let val = self.get_column_value(col)?;
|
||||
Ok((col.to_owned(), val))
|
||||
})
|
||||
.collect::<Result<Record, _>>()
|
||||
.map(|record| Value::record(record, self.span()))
|
||||
}
|
||||
|
||||
fn clone_value(&self, span: Span) -> Value;
|
||||
}
|
@ -4,7 +4,6 @@ mod filesize;
|
||||
mod from;
|
||||
mod from_value;
|
||||
mod glob;
|
||||
mod lazy_record;
|
||||
mod range;
|
||||
|
||||
pub mod record;
|
||||
@ -13,7 +12,6 @@ pub use duration::*;
|
||||
pub use filesize::*;
|
||||
pub use from_value::FromValue;
|
||||
pub use glob::*;
|
||||
pub use lazy_record::LazyRecord;
|
||||
pub use range::{FloatRange, IntRange, Range};
|
||||
pub use record::Record;
|
||||
|
||||
@ -164,13 +162,6 @@ pub enum Value {
|
||||
#[serde(rename = "span")]
|
||||
internal_span: Span,
|
||||
},
|
||||
#[serde(skip)]
|
||||
LazyRecord {
|
||||
val: Box<dyn for<'a> LazyRecord<'a>>,
|
||||
// note: spans are being refactored out of Value
|
||||
// please use .span() instead of matching this span value
|
||||
internal_span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl Clone for Value {
|
||||
@ -212,7 +203,6 @@ impl Clone for Value {
|
||||
val: val.clone(),
|
||||
internal_span: *internal_span,
|
||||
},
|
||||
Value::LazyRecord { val, internal_span } => val.clone_value(*internal_span),
|
||||
Value::List {
|
||||
vals,
|
||||
internal_span,
|
||||
@ -672,24 +662,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner [`LazyRecord`] trait object or an error if this `Value` is not a lazy record
|
||||
pub fn as_lazy_record(&self) -> Result<&dyn for<'a> LazyRecord<'a>, ShellError> {
|
||||
if let Value::LazyRecord { val, .. } = self {
|
||||
Ok(val.as_ref())
|
||||
} else {
|
||||
self.cant_convert_to("lazy record")
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwraps the inner [`LazyRecord`] trait object or returns an error if this `Value` is not a lazy record
|
||||
pub fn into_lazy_record(self) -> Result<Box<dyn for<'a> LazyRecord<'a>>, ShellError> {
|
||||
if let Value::LazyRecord { val, .. } = self {
|
||||
Ok(val)
|
||||
} else {
|
||||
self.cant_convert_to("lazy record")
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the span for the current value
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
@ -709,7 +681,6 @@ impl Value {
|
||||
| Value::Binary { internal_span, .. }
|
||||
| Value::CellPath { internal_span, .. }
|
||||
| Value::Custom { internal_span, .. }
|
||||
| Value::LazyRecord { internal_span, .. }
|
||||
| Value::Error { internal_span, .. } => *internal_span,
|
||||
}
|
||||
}
|
||||
@ -727,7 +698,6 @@ impl Value {
|
||||
| Value::String { internal_span, .. }
|
||||
| Value::Glob { internal_span, .. }
|
||||
| Value::Record { internal_span, .. }
|
||||
| Value::LazyRecord { internal_span, .. }
|
||||
| Value::List { internal_span, .. }
|
||||
| Value::Closure { internal_span, .. }
|
||||
| Value::Nothing { internal_span, .. }
|
||||
@ -784,10 +754,6 @@ impl Value {
|
||||
None => Type::List(Box::new(Type::Any)),
|
||||
}
|
||||
}
|
||||
Value::LazyRecord { val, .. } => match val.collect() {
|
||||
Ok(val) => val.get_type(),
|
||||
Err(..) => Type::Error,
|
||||
},
|
||||
Value::Nothing { .. } => Type::Nothing,
|
||||
Value::Closure { .. } => Type::Closure,
|
||||
Value::Error { .. } => Type::Error,
|
||||
@ -893,10 +859,6 @@ impl Value {
|
||||
.collect::<Vec<_>>()
|
||||
.join(separator)
|
||||
),
|
||||
Value::LazyRecord { val, .. } => val
|
||||
.collect()
|
||||
.unwrap_or_else(|err| Value::error(err, span))
|
||||
.to_expanded_string(separator, config),
|
||||
Value::Closure { val, .. } => format!("<Closure {}>", val.block_id),
|
||||
Value::Nothing { .. } => String::new(),
|
||||
Value::Error { error, .. } => format!("{error:?}"),
|
||||
@ -919,7 +881,6 @@ impl Value {
|
||||
/// - "[list {n} items]"
|
||||
/// - "[record {n} fields]"
|
||||
pub fn to_abbreviated_string(&self, config: &Config) -> String {
|
||||
let span = self.span();
|
||||
match self {
|
||||
Value::Date { val, .. } => match &config.datetime_table_format {
|
||||
Some(format) => self.format_datetime(val, format),
|
||||
@ -945,10 +906,6 @@ impl Value {
|
||||
val.len(),
|
||||
if val.len() == 1 { "" } else { "s" }
|
||||
),
|
||||
Value::LazyRecord { val, .. } => val
|
||||
.collect()
|
||||
.unwrap_or_else(|err| Value::error(err, span))
|
||||
.to_abbreviated_string(config),
|
||||
val => val.to_expanded_string(", ", config),
|
||||
}
|
||||
}
|
||||
@ -1087,7 +1044,7 @@ impl Value {
|
||||
}
|
||||
// Records (and tables) are the only built-in which support column names,
|
||||
// so only use this message for them.
|
||||
Value::Record { .. } | Value::LazyRecord { .. } => {
|
||||
Value::Record { .. } => {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message:"Can't access record values with a row index. Try specifying a column name instead".into(),
|
||||
span: *origin_span,
|
||||
@ -1137,32 +1094,6 @@ impl Value {
|
||||
});
|
||||
}
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let columns = val.column_names();
|
||||
|
||||
if let Some(col) = columns.iter().rev().find(|&col| {
|
||||
if insensitive {
|
||||
col.eq_ignore_case(column_name)
|
||||
} else {
|
||||
col == column_name
|
||||
}
|
||||
}) {
|
||||
current = val.get_column_value(col)?;
|
||||
} else if *optional {
|
||||
return Ok(Value::nothing(*origin_span)); // short-circuit
|
||||
} else if let Some(suggestion) = did_you_mean(&columns, column_name) {
|
||||
return Err(ShellError::DidYouMean {
|
||||
suggestion,
|
||||
span: *origin_span,
|
||||
});
|
||||
} else {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
col_name: column_name.clone(),
|
||||
span: *origin_span,
|
||||
src_span: span,
|
||||
});
|
||||
}
|
||||
}
|
||||
// String access of Lists always means Table access.
|
||||
// Create a List which contains each matching value for contained
|
||||
// records in the source list.
|
||||
@ -1327,11 +1258,6 @@ impl Value {
|
||||
record.to_mut().push(col_name, new_col);
|
||||
}
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
// convert to Record first.
|
||||
*self = val.collect()?;
|
||||
self.upsert_data_at_cell_path(cell_path, new_val)?;
|
||||
}
|
||||
Value::Error { error, .. } => return Err(*error.clone()),
|
||||
v => {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
@ -1443,11 +1369,6 @@ impl Value {
|
||||
});
|
||||
}
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
// convert to Record first.
|
||||
*self = val.collect()?;
|
||||
self.update_data_at_cell_path(cell_path, new_val)?;
|
||||
}
|
||||
Value::Error { error, .. } => return Err(*error.clone()),
|
||||
v => {
|
||||
return Err(ShellError::CantFindColumn {
|
||||
@ -1532,11 +1453,6 @@ impl Value {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
// convert to Record first.
|
||||
*self = val.collect()?;
|
||||
self.remove_data_at_cell_path(cell_path)
|
||||
}
|
||||
v => Err(ShellError::CantFindColumn {
|
||||
col_name: col_name.clone(),
|
||||
span: *span,
|
||||
@ -1616,11 +1532,6 @@ impl Value {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
// convert to Record first.
|
||||
*self = val.collect()?;
|
||||
self.remove_data_at_cell_path(cell_path)
|
||||
}
|
||||
v => Err(ShellError::CantFindColumn {
|
||||
col_name: col_name.clone(),
|
||||
span: *span,
|
||||
@ -1739,11 +1650,6 @@ impl Value {
|
||||
record.to_mut().push(col_name, new_col);
|
||||
}
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
// convert to Record first.
|
||||
*self = val.collect()?;
|
||||
self.insert_data_at_cell_path(cell_path, new_val, v_span)?;
|
||||
}
|
||||
other => {
|
||||
return Err(ShellError::UnsupportedInput {
|
||||
msg: "table or record".into(),
|
||||
@ -1797,8 +1703,6 @@ impl Value {
|
||||
///
|
||||
/// If the closure returns `Err`, the traversal will stop.
|
||||
///
|
||||
/// If collecting lazy records to check them as well is desirable, make sure to do it in your
|
||||
/// closure. The traversal continues on whatever modifications you make during the closure.
|
||||
/// Captures of closure values are currently visited, as they are values owned by the closure.
|
||||
pub fn recurse_mut<E>(
|
||||
&mut self,
|
||||
@ -1837,7 +1741,7 @@ impl Value {
|
||||
| Value::Binary { .. }
|
||||
| Value::CellPath { .. } => Ok(()),
|
||||
// These could potentially contain values, but we expect the closure to handle them
|
||||
Value::LazyRecord { .. } | Value::Custom { .. } => Ok(()),
|
||||
Value::Custom { .. } => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1998,13 +1902,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lazy_record(val: Box<dyn for<'a> LazyRecord<'a>>, span: Span) -> Value {
|
||||
Value::LazyRecord {
|
||||
val,
|
||||
internal_span: span,
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: Only use this for test data, *not* live data, as it will point into unknown source
|
||||
/// when used in errors.
|
||||
pub fn test_bool(val: bool) -> Value {
|
||||
@ -2101,17 +1998,11 @@ impl Value {
|
||||
Value::custom(val, Span::test_data())
|
||||
}
|
||||
|
||||
/// Note: Only use this for test data, *not* live data, as it will point into unknown source
|
||||
/// when used in errors.
|
||||
pub fn test_lazy_record(val: Box<dyn for<'a> LazyRecord<'a>>) -> Value {
|
||||
Value::lazy_record(val, Span::test_data())
|
||||
}
|
||||
|
||||
/// Note: Only use this for test data, *not* live data,
|
||||
/// as it will point into unknown source when used in errors.
|
||||
///
|
||||
/// Returns a `Vec` containing one of each value case (`Value::Int`, `Value::String`, etc.)
|
||||
/// except for `Value::LazyRecord` and `Value::CustomValue`.
|
||||
/// except for `Value::CustomValue`.
|
||||
pub fn test_values() -> Vec<Value> {
|
||||
vec![
|
||||
Value::test_bool(false),
|
||||
@ -2127,7 +2018,6 @@ impl Value {
|
||||
Value::test_float(0.0),
|
||||
Value::test_string(String::new()),
|
||||
Value::test_record(Record::new()),
|
||||
// Value::test_lazy_record(Box::new(todo!())),
|
||||
Value::test_list(Vec::new()),
|
||||
Value::test_closure(Closure {
|
||||
block_id: 0,
|
||||
@ -2181,7 +2071,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Less),
|
||||
Value::Glob { .. } => Some(Ordering::Less),
|
||||
Value::Record { .. } => Some(Ordering::Less),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Less),
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2201,7 +2090,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Less),
|
||||
Value::Glob { .. } => Some(Ordering::Less),
|
||||
Value::Record { .. } => Some(Ordering::Less),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Less),
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2221,7 +2109,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Less),
|
||||
Value::Glob { .. } => Some(Ordering::Less),
|
||||
Value::Record { .. } => Some(Ordering::Less),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Less),
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2241,7 +2128,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Less),
|
||||
Value::Glob { .. } => Some(Ordering::Less),
|
||||
Value::Record { .. } => Some(Ordering::Less),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Less),
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2261,7 +2147,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Less),
|
||||
Value::Glob { .. } => Some(Ordering::Less),
|
||||
Value::Record { .. } => Some(Ordering::Less),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Less),
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2281,7 +2166,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Less),
|
||||
Value::Glob { .. } => Some(Ordering::Less),
|
||||
Value::Record { .. } => Some(Ordering::Less),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Less),
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2301,7 +2185,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Less),
|
||||
Value::Glob { .. } => Some(Ordering::Less),
|
||||
Value::Record { .. } => Some(Ordering::Less),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Less),
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2321,7 +2204,6 @@ impl PartialOrd for Value {
|
||||
Value::String { val: rhs, .. } => lhs.partial_cmp(rhs),
|
||||
Value::Glob { val: rhs, .. } => lhs.partial_cmp(rhs),
|
||||
Value::Record { .. } => Some(Ordering::Less),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Less),
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2341,7 +2223,6 @@ impl PartialOrd for Value {
|
||||
Value::String { val: rhs, .. } => lhs.partial_cmp(rhs),
|
||||
Value::Glob { val: rhs, .. } => lhs.partial_cmp(rhs),
|
||||
Value::Record { .. } => Some(Ordering::Less),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Less),
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2387,13 +2268,6 @@ impl PartialOrd for Value {
|
||||
// that the shorter sequence is less than the longer one
|
||||
lhs.len().partial_cmp(&rhs.len())
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
if let Ok(rhs) = val.collect() {
|
||||
self.partial_cmp(&rhs)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Value::List { .. } => Some(Ordering::Less),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2413,7 +2287,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Greater),
|
||||
Value::Glob { .. } => Some(Ordering::Greater),
|
||||
Value::Record { .. } => Some(Ordering::Greater),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Greater),
|
||||
Value::List { vals: rhs, .. } => lhs.partial_cmp(rhs),
|
||||
Value::Closure { .. } => Some(Ordering::Less),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2433,7 +2306,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Greater),
|
||||
Value::Glob { .. } => Some(Ordering::Greater),
|
||||
Value::Record { .. } => Some(Ordering::Greater),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Greater),
|
||||
Value::List { .. } => Some(Ordering::Greater),
|
||||
Value::Closure { val: rhs, .. } => lhs.block_id.partial_cmp(&rhs.block_id),
|
||||
Value::Nothing { .. } => Some(Ordering::Less),
|
||||
@ -2453,7 +2325,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Greater),
|
||||
Value::Glob { .. } => Some(Ordering::Greater),
|
||||
Value::Record { .. } => Some(Ordering::Greater),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Greater),
|
||||
Value::List { .. } => Some(Ordering::Greater),
|
||||
Value::Closure { .. } => Some(Ordering::Greater),
|
||||
Value::Nothing { .. } => Some(Ordering::Equal),
|
||||
@ -2473,7 +2344,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Greater),
|
||||
Value::Glob { .. } => Some(Ordering::Greater),
|
||||
Value::Record { .. } => Some(Ordering::Greater),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Greater),
|
||||
Value::List { .. } => Some(Ordering::Greater),
|
||||
Value::Closure { .. } => Some(Ordering::Greater),
|
||||
Value::Nothing { .. } => Some(Ordering::Greater),
|
||||
@ -2493,7 +2363,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Greater),
|
||||
Value::Glob { .. } => Some(Ordering::Greater),
|
||||
Value::Record { .. } => Some(Ordering::Greater),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Greater),
|
||||
Value::List { .. } => Some(Ordering::Greater),
|
||||
Value::Closure { .. } => Some(Ordering::Greater),
|
||||
Value::Nothing { .. } => Some(Ordering::Greater),
|
||||
@ -2513,7 +2382,6 @@ impl PartialOrd for Value {
|
||||
Value::String { .. } => Some(Ordering::Greater),
|
||||
Value::Glob { .. } => Some(Ordering::Greater),
|
||||
Value::Record { .. } => Some(Ordering::Greater),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Greater),
|
||||
Value::List { .. } => Some(Ordering::Greater),
|
||||
Value::Closure { .. } => Some(Ordering::Greater),
|
||||
Value::Nothing { .. } => Some(Ordering::Greater),
|
||||
@ -2523,13 +2391,6 @@ impl PartialOrd for Value {
|
||||
Value::Custom { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Custom { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs),
|
||||
(Value::LazyRecord { val, .. }, rhs) => {
|
||||
if let Ok(val) = val.collect() {
|
||||
val.partial_cmp(rhs)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,10 +221,6 @@ fn value_to_string(
|
||||
collection.join(&format!(",{sep}{nl}"))
|
||||
))
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let collected = val.collect()?;
|
||||
value_to_string(&collected, span, depth + 1, indent)
|
||||
}
|
||||
// All strings outside data structures are quoted because they are in 'command position'
|
||||
// (could be mistaken for commands by the Nu parser)
|
||||
Value::String { val, .. } => Ok(escape_quote_string(val)),
|
||||
|
Loading…
Reference in New Issue
Block a user