mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 10:15:41 +02: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:
@ -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 {
|
||||
|
Reference in New Issue
Block a user