diff --git a/Cargo.lock b/Cargo.lock index 72ae2956a9..60c133fff9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2700,7 +2700,7 @@ dependencies = [ "percent-encoding", "reedline", "rstest", - "sysinfo 0.28.0", + "sysinfo 0.28.2", "thiserror", ] @@ -2819,7 +2819,7 @@ dependencies = [ "serde_yaml", "sha2", "sqlparser", - "sysinfo 0.28.0", + "sysinfo 0.28.2", "tabled", "terminal_size 0.2.1", "thiserror", @@ -2848,7 +2848,7 @@ dependencies = [ "nu-protocol", "nu-utils", "serde", - "sysinfo 0.28.0", + "sysinfo 0.28.2", ] [[package]] @@ -5134,9 +5134,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.28.0" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727220a596b4ca0af040a07091e49f5c105ec8f2592674339a5bf35be592f76e" +checksum = "d3e847e2de7a137c8c2cede5095872dbb00f4f9bf34d061347e36b43322acd56" dependencies = [ "cfg-if", "core-foundation-sys", diff --git a/crates/nu-cli/Cargo.toml b/crates/nu-cli/Cargo.toml index e4fa1dfc40..8562730a09 100644 --- a/crates/nu-cli/Cargo.toml +++ b/crates/nu-cli/Cargo.toml @@ -36,7 +36,7 @@ once_cell = "1.17.0" log = "0.4" miette = { version = "5.5.0", features = ["fancy-no-backtrace"] } percent-encoding = "2" -sysinfo = "0.28.0" +sysinfo = "0.28.2" thiserror = "1.0.31" [features] diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index 5eea4ab136..b70fb2d2ee 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -85,7 +85,7 @@ percent-encoding = "2.2.0" reedline = { version = "0.16.0", features = ["bashisms", "sqlite"] } rusqlite = { version = "0.28.0", features = ["bundled"], optional = true } sqlparser = { version = "0.30.0", features = ["serde"], optional = true } -sysinfo = "0.28.0" +sysinfo = "0.28.2" tabled = "0.10.0" terminal_size = "0.2.1" thiserror = "1.0.31" diff --git a/crates/nu-command/src/system/sys.rs b/crates/nu-command/src/system/sys.rs index 26a7e23c66..1966bfd0c5 100644 --- a/crates/nu-command/src/system/sys.rs +++ b/crates/nu-command/src/system/sys.rs @@ -3,8 +3,10 @@ use chrono::Local; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, + Category, Example, IntoPipelineData, LazyRecord, PipelineData, ShellError, Signature, Span, + Type, Value, }; +use serde::{Deserialize, Serialize}; use std::time::{Duration, UNIX_EPOCH}; use sysinfo::{ ComponentExt, CpuExt, CpuRefreshKind, DiskExt, NetworkExt, System, SystemExt, UserExt, @@ -36,7 +38,13 @@ impl Command for Sys { call: &Call, _input: PipelineData, ) -> Result { - run_sys(call) + let span = call.span(); + let ret = Value::LazyRecord { + val: Box::new(SysResult { span }), + span, + }; + + Ok(ret.into_pipeline_data()) } fn examples(&self) -> Vec { @@ -60,51 +68,53 @@ impl Command for Sys { } } -fn run_sys(call: &Call) -> Result { - let span = call.head; - let mut sys = System::new(); +#[derive(Debug, Serialize, Deserialize)] +pub struct SysResult { + pub span: Span, +} - let mut headers = vec![]; - let mut values = vec![]; - - if let Some(value) = host(&mut sys, span) { - headers.push("host".into()); - values.push(value); - } - if let Some(value) = cpu(&mut sys, span) { - headers.push("cpu".into()); - values.push(value); - } - if let Some(value) = disks(&mut sys, span) { - headers.push("disks".into()); - values.push(value); - } - if let Some(value) = mem(&mut sys, span) { - headers.push("mem".into()); - values.push(value); - } - if let Some(value) = temp(&mut sys, span) { - headers.push("temp".into()); - values.push(value); - } - if let Some(value) = net(&mut sys, span) { - headers.push("net".into()); - values.push(value); +impl LazyRecord for SysResult { + fn column_names(&self) -> Vec<&'static str> { + vec!["host", "cpu", "disks", "mem", "temp", "net"] } - Ok(Value::Record { - cols: headers, - vals: values, - span, + fn get_column_value(&self, column: &str) -> Result { + 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 typetag_name(&self) -> &'static str { + "sys" + } + + fn typetag_deserialize(&self) { + unimplemented!("typetag_deserialize") } - .into_pipeline_data()) } pub fn trim_cstyle_null(s: String) -> String { s.trim_matches(char::from(0)).to_string() } -pub fn disks(sys: &mut System, span: Span) -> Option { +pub fn disks(span: Span) -> Value { + let mut sys = System::new(); sys.refresh_disks(); sys.refresh_disks_list(); @@ -157,14 +167,11 @@ pub fn disks(sys: &mut System, span: Span) -> Option { output.push(Value::Record { cols, vals, span }); } - if !output.is_empty() { - Some(Value::List { vals: output, span }) - } else { - None - } + Value::List { vals: output, span } } -pub fn net(sys: &mut System, span: Span) -> Option { +pub fn net(span: Span) -> Value { + let mut sys = System::new(); sys.refresh_networks(); sys.refresh_networks_list(); @@ -193,18 +200,17 @@ pub fn net(sys: &mut System, span: Span) -> Option { output.push(Value::Record { cols, vals, span }); } - if !output.is_empty() { - Some(Value::List { vals: output, span }) - } else { - None - } + Value::List { vals: output, span } } -pub fn cpu(sys: &mut System, span: Span) -> Option { - // FIXME: we must refresh the CPU *twice* System::MINIMUM_CPU_UPDATE_INTERVAL apart to - // get valid usage data, we're currently only doing it once. Consider using a LazyRecord - // to avoid slowing down all calls to `sys` +pub fn cpu(span: Span) -> Value { + let mut sys = System::new(); sys.refresh_cpu_specifics(CpuRefreshKind::everything()); + // We must refresh the CPU twice a while apart to get valid usage data. + // In theory we could just sleep MINIMUM_CPU_UPDATE_INTERVAL, but I've noticed that + // that gives poor results (error of ~5%). Decided to wait 2x that long, somewhat arbitrarily + std::thread::sleep(System::MINIMUM_CPU_UPDATE_INTERVAL * 2); + sys.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage()); let mut output = vec![]; for cpu in sys.cpus() { @@ -230,8 +236,12 @@ pub fn cpu(sys: &mut System, span: Span) -> Option { }); cols.push("cpu_usage".into()); + + // sysinfo CPU usage numbers are not very precise unless you wait a long time between refreshes. + // Round to 1DP (chosen somewhat arbitrarily) so people aren't misled by high-precision floats. + let rounded_usage = (cpu.cpu_usage() * 10.0).round() / 10.0; vals.push(Value::Float { - val: cpu.cpu_usage() as f64, + val: rounded_usage as f64, span, }); @@ -254,14 +264,11 @@ pub fn cpu(sys: &mut System, span: Span) -> Option { output.push(Value::Record { cols, vals, span }); } - if !output.is_empty() { - Some(Value::List { vals: output, span }) - } else { - None - } + Value::List { vals: output, span } } -pub fn mem(sys: &mut System, span: Span) -> Option { +pub fn mem(span: Span) -> Value { + let mut sys = System::new(); sys.refresh_memory(); let mut cols = vec![]; @@ -318,10 +325,11 @@ pub fn mem(sys: &mut System, span: Span) -> Option { span, }); - Some(Value::Record { cols, vals, span }) + Value::Record { cols, vals, span } } -pub fn host(sys: &mut System, span: Span) -> Option { +pub fn host(span: Span) -> Value { + let mut sys = System::new(); sys.refresh_users_list(); let mut cols = vec![]; @@ -414,10 +422,11 @@ pub fn host(sys: &mut System, span: Span) -> Option { vals.push(Value::List { vals: users, span }); } - Some(Value::Record { cols, vals, span }) + Value::Record { cols, vals, span } } -pub fn temp(sys: &mut System, span: Span) -> Option { +pub fn temp(span: Span) -> Value { + let mut sys = System::new(); sys.refresh_components(); sys.refresh_components_list(); @@ -455,9 +464,5 @@ pub fn temp(sys: &mut System, span: Span) -> Option { output.push(Value::Record { cols, vals, span }); } - if !output.is_empty() { - Some(Value::List { vals: output, span }) - } else { - None - } + Value::List { vals: output, span } } diff --git a/crates/nu-engine/Cargo.toml b/crates/nu-engine/Cargo.toml index 38e47b3ec7..78ad7f99a6 100644 --- a/crates/nu-engine/Cargo.toml +++ b/crates/nu-engine/Cargo.toml @@ -18,7 +18,7 @@ nu-utils = { path = "../nu-utils", version = "0.76.1" } chrono = { version="0.4.23", features = ["std"], default-features = false } serde = {version = "1.0.143", default-features = false } -sysinfo ="0.28.0" +sysinfo ="0.28.2" [features] plugin = []