From fc7bf39d1b791fc12ade0fcf5678af0a77d52977 Mon Sep 17 00:00:00 2001 From: Yang Hau Date: Thu, 25 Jul 2024 04:54:17 +0800 Subject: [PATCH] lscpu: Add option --json closes #15 --- Cargo.lock | 58 +++++++++++++++++++++++++++++++++ Cargo.toml | 5 ++- src/uu/lscpu/Cargo.toml | 2 ++ src/uu/lscpu/src/lscpu.rs | 64 +++++++++++++++++++++++++++++++++---- tests/by-util/test_lscpu.rs | 9 ++++++ 5 files changed, 131 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6102ba1..9579989 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,6 +235,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "lazy_static" version = "1.4.0" @@ -523,6 +529,43 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -541,6 +584,17 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "syn" +version = "2.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sysinfo" version = "0.30.13" @@ -639,6 +693,8 @@ dependencies = [ "rand", "regex", "rlimit", + "serde", + "serde_json", "tempfile", "textwrap", "uu_ctrlaltdel", @@ -663,6 +719,8 @@ version = "0.0.1" dependencies = [ "clap", "regex", + "serde", + "serde_json", "sysinfo", "uucore", ] diff --git a/Cargo.toml b/Cargo.toml index e8ada22..45e8fa9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,8 @@ textwrap = { version = "0.16.0", features = ["terminal_size"] } xattr = "1.3.1" tempfile = "3.9.0" rand = { version = "0.8", features = ["small_rng"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" [dependencies] clap = { workspace = true } @@ -54,7 +56,8 @@ clap_mangen = { workspace = true } uucore = { workspace = true } phf = { workspace = true } textwrap = { workspace = true } - +serde = { workspace = true } +serde_json = { workspace = true } # lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" } diff --git a/src/uu/lscpu/Cargo.toml b/src/uu/lscpu/Cargo.toml index c59e07e..dff4b76 100644 --- a/src/uu/lscpu/Cargo.toml +++ b/src/uu/lscpu/Cargo.toml @@ -15,3 +15,5 @@ regex = { workspace = true } sysinfo = { workspace = true } uucore = { workspace = true } clap = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } diff --git a/src/uu/lscpu/src/lscpu.rs b/src/uu/lscpu/src/lscpu.rs index 2faacf9..de0a211 100644 --- a/src/uu/lscpu/src/lscpu.rs +++ b/src/uu/lscpu/src/lscpu.rs @@ -3,32 +3,78 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use clap::{crate_version, Command}; +use clap::{crate_version, Arg, ArgAction, Command}; use regex::Regex; -use std::fs; +use serde::Serialize; +use std::{fs, str::FromStr}; use sysinfo::System; use uucore::{error::UResult, format_usage, help_about, help_usage}; +mod options { + pub const JSON: &str = "json"; +} + const ABOUT: &str = help_about!("lscpu.md"); const USAGE: &str = help_usage!("lscpu.md"); +#[derive(Serialize)] +struct CpuInfos { + lscpu: Vec, +} + +#[derive(Serialize)] +struct CpuInfo { + field: String, + data: String, +} + +impl CpuInfos { + fn new() -> CpuInfos { + CpuInfos { + lscpu: Vec::::new(), + } + } + fn push(&mut self, field: &str, data: &str) { + let cpu_info = CpuInfo { + field: String::from_str(field).unwrap(), + data: String::from_str(data).unwrap(), + }; + self.lscpu.push(cpu_info); + } + fn to_json(&self) -> String { + serde_json::to_string_pretty(self).unwrap() + } +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let _matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; + let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; + + let json = matches.get_flag(options::JSON); + let system = System::new_all(); let _cpu = system.global_cpu_info(); - println!("Architecture: {}", get_architecture()); - println!("CPU(s): {}", system.cpus().len()); + let mut cpu_infos = CpuInfos::new(); + cpu_infos.push("Architecture", &get_architecture()); + cpu_infos.push("CPU(s)", &format!("{}", system.cpus().len())); // Add more CPU information here... if let Ok(contents) = fs::read_to_string("/proc/cpuinfo") { let re = Regex::new(r"^model name\s+:\s+(.*)$").unwrap(); // Assuming all CPUs have the same model name if let Some(cap) = re.captures_iter(&contents).next() { - println!("Model name: {}", &cap[1]); + cpu_infos.push("Model name", &cap[1]); }; } + + if json { + println!("{}", cpu_infos.to_json()); + } else { + for elt in cpu_infos.lscpu { + println!("{}: {}", elt.field, elt.data); + } + } Ok(()) } @@ -48,4 +94,10 @@ pub fn uu_app() -> Command { .about(ABOUT) .override_usage(format_usage(USAGE)) .infer_long_args(true) + .arg( + Arg::new(options::JSON) + .long("json") + .help("Use JSON output format for the default summary or extended output (see --extended). For backward compatibility, JSON output follows the default summary behavior for non-terminals (e.g., pipes) where subsections are missing. See also --hierarchic.") + .action(ArgAction::SetTrue), + ) } diff --git a/tests/by-util/test_lscpu.rs b/tests/by-util/test_lscpu.rs index 7ca9b77..852198c 100644 --- a/tests/by-util/test_lscpu.rs +++ b/tests/by-util/test_lscpu.rs @@ -4,9 +4,18 @@ // file that was distributed with this source code. use crate::common::util::TestScenario; +use serde_json::{self, Value}; #[test] #[ignore] fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails().code_is(1); } + +#[test] +fn test_json() { + let result = new_ucmd!().arg("--json").succeeds(); + let stdout_bytes = result.stdout(); + let res: Result = serde_json::from_slice(&stdout_bytes); + assert!(res.is_ok(), "invalid json output"); +}