mirror of
https://github.com/nushell/nushell.git
synced 2025-08-14 00:48:24 +02:00
Merge branch 'main' into ecow-record
This commit is contained in:
@ -150,7 +150,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
head,
|
||||
),
|
||||
Ordering::Less => Value::binary(
|
||||
if end == isize::max_value() {
|
||||
if end == isize::MAX {
|
||||
val.iter()
|
||||
.skip(start as usize)
|
||||
.copied()
|
||||
|
@ -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,207 +53,119 @@ 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());
|
||||
|
||||
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())
|
||||
}));
|
||||
// only get information requested
|
||||
let sys = System::new_with_specifics(rk);
|
||||
|
||||
let system = system_opt.get_system();
|
||||
// get the process information for the nushell pid
|
||||
let pinfo = system.process(pid);
|
||||
let pid = Pid::from(std::process::id() as usize);
|
||||
let ppid = {
|
||||
sys.process(pid)
|
||||
.and_then(|p| p.parent())
|
||||
.map(|p| Value::int(p.as_u32().into(), span))
|
||||
.unwrap_or(Value::nothing(span))
|
||||
};
|
||||
|
||||
Ok(pinfo
|
||||
.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())
|
||||
}));
|
||||
let system = Value::record(
|
||||
record! {
|
||||
"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),
|
||||
},
|
||||
span,
|
||||
);
|
||||
|
||||
let system = system_opt.get_system();
|
||||
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)
|
||||
};
|
||||
|
||||
Ok(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),
|
||||
},
|
||||
self.span,
|
||||
))
|
||||
}
|
||||
"process" => {
|
||||
// only get information requested
|
||||
let system_opt = SystemOpt::from((system_option, || {
|
||||
RefreshKind::new().with_processes(ProcessRefreshKind::everything())
|
||||
}));
|
||||
let cwd = if let Some(path) = p.cwd() {
|
||||
Value::string(path.to_string_lossy().to_string(), span)
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
};
|
||||
|
||||
let system = system_opt.get_system();
|
||||
let pinfo = system.process(pid);
|
||||
let exe_path = if let Some(path) = p.exe() {
|
||||
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)
|
||||
} else {
|
||||
Value::nothing(self.span)
|
||||
}
|
||||
},
|
||||
"cwd" => {
|
||||
if let Some(path) = p.cwd() {
|
||||
Value::string(path.to_string_lossy().to_string(), self.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" => {
|
||||
let mut env_rec = Record::new();
|
||||
for val in p.environ() {
|
||||
if let Some((key, value)) = val.split_once('=') {
|
||||
let is_env_var_a_list = {
|
||||
{
|
||||
#[cfg(target_family = "windows")]
|
||||
{
|
||||
key == "Path" || key == "PATHEXT" || key == "PSMODULEPATH" || key == "PSModulePath"
|
||||
}
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
{
|
||||
key == "PATH" || key == "DYLD_FALLBACK_LIBRARY_PATH"
|
||||
}
|
||||
}
|
||||
};
|
||||
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));
|
||||
} else {
|
||||
env_rec.push(key.to_string(), Value::string(value.to_string(), self.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();
|
||||
|
||||
Ok(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),
|
||||
},
|
||||
self.span,
|
||||
))
|
||||
let environment = {
|
||||
let mut env_rec = Record::new();
|
||||
for val in p.environ() {
|
||||
if let Some((key, value)) = val.split_once('=') {
|
||||
let is_env_var_a_list = {
|
||||
{
|
||||
#[cfg(target_family = "windows")]
|
||||
{
|
||||
key == "Path"
|
||||
|| key == "PATHEXT"
|
||||
|| key == "PSMODULEPATH"
|
||||
|| key == "PSModulePath"
|
||||
}
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
{
|
||||
key == "PATH" || key == "DYLD_FALLBACK_LIBRARY_PATH"
|
||||
}
|
||||
}
|
||||
};
|
||||
if is_env_var_a_list {
|
||||
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(), span));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::IncompatibleParametersSingle {
|
||||
msg: format!("Unknown column: {}", column),
|
||||
span: self.span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::record(env_rec, span)
|
||||
};
|
||||
|
||||
impl<'a> LazyRecord<'a> for LazySystemInfoRecord {
|
||||
fn column_names(&'a self) -> Vec<&'a str> {
|
||||
vec!["thread_id", "pid", "ppid", "process", "system"]
|
||||
}
|
||||
Value::record(
|
||||
record! {
|
||||
"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,
|
||||
},
|
||||
span,
|
||||
)
|
||||
} else {
|
||||
Value::nothing(span)
|
||||
};
|
||||
|
||||
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 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use nu_engine::{command_prelude::*, current_dir};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_utils::filesystem::{have_permission, PermissionResult};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -20,6 +20,7 @@ impl Command for Cd {
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("cd")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||
.switch("physical", "use the physical directory structure; resolve symbolic links before processing instances of ..", Some('P'))
|
||||
.optional("path", SyntaxShape::Directory, "The path to change to.")
|
||||
.input_output_types(vec![
|
||||
(Type::Nothing, Type::Nothing),
|
||||
@ -36,8 +37,9 @@ impl Command for Cd {
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let physical = call.has_flag(engine_state, stack, "physical")?;
|
||||
let path_val: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let cwd = engine_state.cwd(Some(stack))?;
|
||||
|
||||
let path_val = {
|
||||
if let Some(path) = path_val {
|
||||
@ -53,54 +55,53 @@ impl Command for Cd {
|
||||
let (path, span) = match path_val {
|
||||
Some(v) => {
|
||||
if v.item == "-" {
|
||||
let oldpwd = stack.get_env_var(engine_state, "OLDPWD");
|
||||
|
||||
if let Some(oldpwd) = oldpwd {
|
||||
let path = oldpwd.to_path()?;
|
||||
let path = match nu_path::canonicalize_with(path.clone(), &cwd) {
|
||||
Ok(p) => p,
|
||||
Err(_) => {
|
||||
return Err(ShellError::DirectoryNotFound {
|
||||
dir: path.to_string_lossy().to_string(),
|
||||
span: v.span,
|
||||
});
|
||||
}
|
||||
};
|
||||
(path.to_string_lossy().to_string(), v.span)
|
||||
if let Some(oldpwd) = stack.get_env_var(engine_state, "OLDPWD") {
|
||||
(oldpwd.to_path()?, v.span)
|
||||
} else {
|
||||
(cwd.to_string_lossy().to_string(), v.span)
|
||||
(cwd, v.span)
|
||||
}
|
||||
} else {
|
||||
// Trim whitespace from the end of path.
|
||||
let path_no_whitespace =
|
||||
&v.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
|
||||
|
||||
let path = match nu_path::canonicalize_with(path_no_whitespace, &cwd) {
|
||||
Ok(p) => {
|
||||
if !p.is_dir() {
|
||||
// If `--physical` is specified, canonicalize the path; otherwise expand the path.
|
||||
let path = if physical {
|
||||
if let Ok(path) = nu_path::canonicalize_with(path_no_whitespace, &cwd) {
|
||||
if !path.is_dir() {
|
||||
return Err(ShellError::NotADirectory { span: v.span });
|
||||
};
|
||||
p
|
||||
}
|
||||
|
||||
// if canonicalize failed, let's check to see if it's abbreviated
|
||||
Err(_) => {
|
||||
path
|
||||
} else {
|
||||
return Err(ShellError::DirectoryNotFound {
|
||||
dir: path_no_whitespace.to_string(),
|
||||
span: v.span,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let path = nu_path::expand_path_with(path_no_whitespace, &cwd, true);
|
||||
if !path.exists() {
|
||||
return Err(ShellError::DirectoryNotFound {
|
||||
dir: path_no_whitespace.to_string(),
|
||||
span: v.span,
|
||||
});
|
||||
};
|
||||
if !path.is_dir() {
|
||||
return Err(ShellError::NotADirectory { span: v.span });
|
||||
};
|
||||
path
|
||||
};
|
||||
(path.to_string_lossy().to_string(), v.span)
|
||||
(path, v.span)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let path = nu_path::expand_tilde("~");
|
||||
(path.to_string_lossy().to_string(), call.head)
|
||||
(path, call.head)
|
||||
}
|
||||
};
|
||||
|
||||
let path_value = Value::string(path.clone(), span);
|
||||
|
||||
// Set OLDPWD.
|
||||
// We're using `Stack::get_env_var()` instead of `EngineState::cwd()` to avoid a conversion roundtrip.
|
||||
if let Some(oldpwd) = stack.get_env_var(engine_state, "PWD") {
|
||||
stack.add_env_var("OLDPWD".into(), oldpwd)
|
||||
}
|
||||
@ -109,11 +110,15 @@ impl Command for Cd {
|
||||
//FIXME: this only changes the current scope, but instead this environment variable
|
||||
//should probably be a block that loads the information from the state in the overlay
|
||||
PermissionResult::PermissionOk => {
|
||||
stack.add_env_var("PWD".into(), path_value);
|
||||
stack.add_env_var("PWD".into(), Value::string(path.to_string_lossy(), span));
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError {
|
||||
msg: format!("Cannot change directory to {path}: {reason}"),
|
||||
msg: format!(
|
||||
"Cannot change directory to {}: {}",
|
||||
path.to_string_lossy(),
|
||||
reason
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::util::get_rest_for_glob_pattern;
|
||||
use crate::{DirBuilder, DirInfo, FileInfo};
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir};
|
||||
use nu_glob::Pattern;
|
||||
use nu_protocol::NuGlob;
|
||||
@ -98,6 +99,7 @@ impl Command for Du {
|
||||
let all = call.has_flag(engine_state, stack, "all")?;
|
||||
let deref = call.has_flag(engine_state, stack, "deref")?;
|
||||
let exclude = call.get_flag(engine_state, stack, "exclude")?;
|
||||
#[allow(deprecated)]
|
||||
let current_dir = current_dir(engine_state, stack)?;
|
||||
|
||||
let paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, env::current_dir};
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
use wax::{Glob as WaxGlob, WalkBehavior, WalkEntry};
|
||||
@ -178,6 +179,7 @@ impl Command for Glob {
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(deprecated)]
|
||||
let path = current_dir(engine_state, stack)?;
|
||||
let path = match nu_path::canonicalize_with(prefix, path) {
|
||||
Ok(path) => path,
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::util::get_rest_for_glob_pattern;
|
||||
use crate::{DirBuilder, DirInfo};
|
||||
use chrono::{DateTime, Local, LocalResult, TimeZone, Utc};
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, env::current_dir};
|
||||
use nu_glob::{MatchOptions, Pattern};
|
||||
use nu_path::expand_to_real_path;
|
||||
@ -91,6 +92,7 @@ impl Command for Ls {
|
||||
let use_mime_type = call.has_flag(engine_state, stack, "mime-type")?;
|
||||
let ctrl_c = engine_state.ctrlc.clone();
|
||||
let call_span = call.head;
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
|
||||
let args = Args {
|
||||
@ -429,7 +431,7 @@ fn ls_for_one_pattern(
|
||||
Err(err) => Some(Value::error(err, call_span)),
|
||||
}
|
||||
}
|
||||
_ => Some(Value::nothing(call_span)),
|
||||
Err(err) => Some(Value::error(err, call_span)),
|
||||
})))
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, env::current_dir};
|
||||
use std::path::PathBuf;
|
||||
|
||||
@ -90,6 +91,7 @@ impl Command for Mktemp {
|
||||
} else if directory || tmpdir {
|
||||
Some(std::env::temp_dir())
|
||||
} else {
|
||||
#[allow(deprecated)]
|
||||
Some(current_dir(engine_state, stack)?)
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::util::get_rest_for_glob_pattern;
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir, get_eval_block};
|
||||
use nu_protocol::{BufferedReader, DataSource, NuGlob, PipelineMetadata, RawStream};
|
||||
use std::{io::BufReader, path::Path};
|
||||
@ -51,6 +52,7 @@ impl Command for Open {
|
||||
let raw = call.has_flag(engine_state, stack, "raw")?;
|
||||
let call_span = call.head;
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||
let eval_block = get_eval_block(engine_state);
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::util::{get_rest_for_glob_pattern, try_interaction};
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, env::current_dir};
|
||||
use nu_glob::MatchOptions;
|
||||
use nu_path::expand_path_with;
|
||||
@ -130,6 +131,7 @@ fn rm(
|
||||
|
||||
let mut unique_argument_check = None;
|
||||
|
||||
#[allow(deprecated)]
|
||||
let currentdir_path = current_dir(engine_state, stack)?;
|
||||
|
||||
let home: Option<String> = nu_path::home_dir().map(|path| {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::progress_bar;
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir};
|
||||
use nu_path::expand_path_with;
|
||||
use nu_protocol::{
|
||||
@ -85,6 +86,7 @@ impl Command for Save {
|
||||
};
|
||||
|
||||
let span = call.head;
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
|
||||
let path_arg = call.req::<Spanned<PathBuf>>(engine_state, stack, 0)?;
|
||||
|
@ -1,4 +1,5 @@
|
||||
use filetime::FileTime;
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir};
|
||||
use nu_path::expand_path_with;
|
||||
use nu_protocol::NuGlob;
|
||||
@ -113,6 +114,7 @@ impl Command for Touch {
|
||||
})?;
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
|
||||
for (index, glob) in files.into_iter().enumerate() {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::util::get_rest_for_glob_pattern;
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir};
|
||||
use std::path::PathBuf;
|
||||
use uu_cp::{BackupMode, CopyMode, UpdateMode};
|
||||
@ -177,6 +178,7 @@ impl Command for UCp {
|
||||
let target_path = PathBuf::from(&nu_utils::strip_ansi_string_unlikely(
|
||||
target.item.to_string(),
|
||||
));
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let target_path = nu_path::expand_path_with(target_path, &cwd, target.item.is_expand());
|
||||
if target.item.as_ref().ends_with(PATH_SEPARATOR) && !target_path.is_dir() {
|
||||
|
@ -1,3 +1,4 @@
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir};
|
||||
|
||||
use uu_mkdir::mkdir;
|
||||
@ -58,6 +59,7 @@ impl Command for UMkdir {
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let mut directories = get_rest_for_glob_pattern(engine_state, stack, call, 0)?
|
||||
.into_iter()
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::util::get_rest_for_glob_pattern;
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir};
|
||||
use nu_path::expand_path_with;
|
||||
use nu_protocol::NuGlob;
|
||||
@ -77,6 +78,7 @@ impl Command for UMv {
|
||||
uu_mv::OverwriteMode::Force
|
||||
};
|
||||
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||
if paths.is_empty() {
|
||||
|
@ -5,6 +5,7 @@ use notify_debouncer_full::{
|
||||
EventKind, RecursiveMode, Watcher,
|
||||
},
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir, ClosureEval};
|
||||
use nu_protocol::{
|
||||
engine::{Closure, StateWorkingSet},
|
||||
@ -73,6 +74,7 @@ impl Command for Watch {
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let path_arg: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
|
||||
|
@ -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_iter()
|
||||
.map(move |(x, _)| Value::string(x, head))
|
||||
|
@ -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,19 +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_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(())
|
||||
}
|
||||
|
@ -129,10 +129,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,4 +1,5 @@
|
||||
use super::PathSubcommandArguments;
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir, current_dir_const};
|
||||
use nu_path::expand_path_with;
|
||||
use nu_protocol::engine::StateWorkingSet;
|
||||
@ -53,6 +54,7 @@ If you need to distinguish dirs and files, please use `path type`."#
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
#[allow(deprecated)]
|
||||
let args = Arguments {
|
||||
pwd: current_dir(engine_state, stack)?,
|
||||
not_follow_symlink: call.has_flag(engine_state, stack, "no-symlink")?,
|
||||
@ -74,6 +76,7 @@ If you need to distinguish dirs and files, please use `path type`."#
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
#[allow(deprecated)]
|
||||
let args = Arguments {
|
||||
pwd: current_dir_const(working_set)?,
|
||||
not_follow_symlink: call.has_flag_const(working_set, "no-symlink")?,
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::PathSubcommandArguments;
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{
|
||||
command_prelude::*,
|
||||
env::{current_dir_str, current_dir_str_const},
|
||||
@ -57,6 +58,7 @@ impl Command for SubCommand {
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
#[allow(deprecated)]
|
||||
let args = Arguments {
|
||||
strict: call.has_flag(engine_state, stack, "strict")?,
|
||||
cwd: current_dir_str(engine_state, stack)?,
|
||||
@ -79,6 +81,7 @@ impl Command for SubCommand {
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
#[allow(deprecated)]
|
||||
let args = Arguments {
|
||||
strict: call.has_flag_const(working_set, "strict")?,
|
||||
cwd: current_dir_str_const(working_set)?,
|
||||
|
@ -149,7 +149,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
),
|
||||
Ordering::Less => Value::string(
|
||||
{
|
||||
if end == isize::max_value() {
|
||||
if end == isize::MAX {
|
||||
if args.graphemes {
|
||||
s.graphemes(true)
|
||||
.skip(start as usize)
|
||||
@ -245,7 +245,7 @@ mod tests {
|
||||
expectation("andre", (0, -1)),
|
||||
// str substring [ -4 , _ ]
|
||||
// str substring -4 ,
|
||||
expectation("dres", (-4, isize::max_value())),
|
||||
expectation("dres", (-4, isize::MAX)),
|
||||
expectation("", (0, -110)),
|
||||
expectation("", (6, 0)),
|
||||
expectation("", (6, -1)),
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::run_external::create_external_command;
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::{command_prelude::*, current_dir};
|
||||
use nu_protocol::OutDest;
|
||||
|
||||
@ -62,6 +63,7 @@ fn exec(
|
||||
external_command.out = OutDest::Inherit;
|
||||
external_command.err = OutDest::Inherit;
|
||||
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let mut command = external_command.spawn_simple_command(&cwd.to_string_lossy())?;
|
||||
command.current_dir(cwd);
|
||||
|
@ -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(),
|
||||
span,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
fn clone_value(&self, span: Span) -> Value {
|
||||
Value::lazy_record(Box::new((*self).clone()), span)
|
||||
}
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn trim_cstyle_null(s: String) -> String {
|
||||
|
@ -229,6 +229,7 @@ fn which(
|
||||
|
||||
let mut output = vec![];
|
||||
|
||||
#[allow(deprecated)]
|
||||
let cwd = env::current_dir_str(engine_state, stack)?;
|
||||
let paths = env::path_str(engine_state, stack, call.head)?;
|
||||
|
||||
|
@ -394,10 +394,6 @@ fn handle_table_command(
|
||||
input.data = PipelineData::Empty;
|
||||
handle_record(input, cfg, val)
|
||||
}
|
||||
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.
|
||||
@ -942,7 +938,11 @@ fn render_path_name(
|
||||
|
||||
// clickable links don't work in remote SSH sessions
|
||||
let in_ssh_session = std::env::var("SSH_CLIENT").is_ok();
|
||||
let show_clickable_links = config.show_clickable_links_in_ls && !in_ssh_session && has_metadata;
|
||||
//TODO: Deprecated show_clickable_links_in_ls in favor of shell_integration_osc8
|
||||
let show_clickable_links = config.show_clickable_links_in_ls
|
||||
&& !in_ssh_session
|
||||
&& has_metadata
|
||||
&& config.shell_integration_osc8;
|
||||
|
||||
let ansi_style = style.map(Style::to_nu_ansi_term_style).unwrap_or_default();
|
||||
|
||||
|
@ -207,7 +207,15 @@ fn filesystem_change_directory_to_symlink_relative() {
|
||||
$env.PWD
|
||||
"
|
||||
);
|
||||
assert_eq!(PathBuf::from(actual.out), dirs.test().join("foo_link"));
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test().join("boo"),
|
||||
"
|
||||
cd -P ../foo_link
|
||||
$env.PWD
|
||||
"
|
||||
);
|
||||
assert_eq!(PathBuf::from(actual.out), dirs.test().join("foo"));
|
||||
})
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -91,3 +91,18 @@ fn let_glob_type() {
|
||||
let actual = nu!("let x: glob = 'aa'; $x | describe");
|
||||
assert_eq!(actual.out, "glob");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn let_raw_string() {
|
||||
let actual = nu!(r#"let x = r#'abcde""fghi"''''jkl'#; $x"#);
|
||||
assert_eq!(actual.out, r#"abcde""fghi"''''jkl"#);
|
||||
|
||||
let actual = nu!(r#"let x = r##'abcde""fghi"''''#jkl'##; $x"#);
|
||||
assert_eq!(actual.out, r#"abcde""fghi"''''#jkl"#);
|
||||
|
||||
let actual = nu!(r#"let x = r###'abcde""fghi"'''##'#jkl'###; $x"#);
|
||||
assert_eq!(actual.out, r#"abcde""fghi"'''##'#jkl"#);
|
||||
|
||||
let actual = nu!(r#"let x = r#'abc'#; $x"#);
|
||||
assert_eq!(actual.out, "abc");
|
||||
}
|
||||
|
@ -125,3 +125,18 @@ fn mut_glob_type() {
|
||||
let actual = nu!("mut x: glob = 'aa'; $x | describe");
|
||||
assert_eq!(actual.out, "glob");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mut_raw_string() {
|
||||
let actual = nu!(r#"mut x = r#'abcde""fghi"''''jkl'#; $x"#);
|
||||
assert_eq!(actual.out, r#"abcde""fghi"''''jkl"#);
|
||||
|
||||
let actual = nu!(r#"mut x = r##'abcde""fghi"''''#jkl'##; $x"#);
|
||||
assert_eq!(actual.out, r#"abcde""fghi"''''#jkl"#);
|
||||
|
||||
let actual = nu!(r#"mut x = r###'abcde""fghi"'''##'#jkl'###; $x"#);
|
||||
assert_eq!(actual.out, r#"abcde""fghi"'''##'#jkl"#);
|
||||
|
||||
let actual = nu!(r#"mut x = r#'abc'#; $x"#);
|
||||
assert_eq!(actual.out, "abc");
|
||||
}
|
||||
|
@ -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");
|
||||
|
Reference in New Issue
Block a user