From 9b78ffa812545b106bce31345d8b94d313142eec Mon Sep 17 00:00:00 2001 From: Krysztal112233 Date: Sat, 23 Mar 2024 02:06:23 +0800 Subject: [PATCH] free: Implement almost all functions of the `free` commands (#35) --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/uu/free/Cargo.toml | 1 + src/uu/free/src/free.rs | 169 ++++++++++++++++++++++++++++++++++------ 4 files changed, 154 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c15e299..5920c062 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,12 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" + [[package]] name = "cfg-if" version = "1.0.0" @@ -693,6 +699,7 @@ dependencies = [ name = "uu_free" version = "0.0.1" dependencies = [ + "bytesize", "clap", "uucore", ] diff --git a/Cargo.toml b/Cargo.toml index 32530d46..19c5b57b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ xattr = "1.3.1" tempfile = "3.9.0" rand = { version = "0.8", features = ["small_rng"] } utmpx = "0.1" +bytesize = "1.3.0" [dependencies] clap = { workspace = true } diff --git a/src/uu/free/Cargo.toml b/src/uu/free/Cargo.toml index c44339dc..1c6866f0 100644 --- a/src/uu/free/Cargo.toml +++ b/src/uu/free/Cargo.toml @@ -15,6 +15,7 @@ categories = ["command-line-utilities"] [dependencies] uucore = { workspace = true } clap = { workspace = true } +bytesize = { workspace = true } [lib] path = "src/free.rs" diff --git a/src/uu/free/src/free.rs b/src/uu/free/src/free.rs index 4806d9e7..a0e1e99f 100644 --- a/src/uu/free/src/free.rs +++ b/src/uu/free/src/free.rs @@ -3,8 +3,21 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +use bytesize::ByteSize; +use bytesize::GB; +use bytesize::GIB; +use bytesize::KIB; +use bytesize::MB; +use bytesize::MIB; +use bytesize::PB; +use bytesize::PIB; +use bytesize::TB; +use bytesize::TIB; +use clap::arg; use clap::Arg; use clap::ArgAction; +use clap::ArgGroup; +use clap::ArgMatches; use clap::{crate_version, Command}; use std::env; use std::fs; @@ -15,6 +28,7 @@ use uucore::{error::UResult, format_usage, help_about, help_usage}; const ABOUT: &str = help_about!("free.md"); const USAGE: &str = help_usage!("free.md"); +/// The unit of number is [UnitMultiplier::Bytes] struct MemInfo { total: u64, free: u64, @@ -71,6 +85,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?; let wide = matches.get_flag("wide"); + let human = matches.get_flag("human"); + + let si = matches.get_flag("si"); + + let convert = detect_unit(&matches); + match parse_meminfo() { Ok(mem_info) => { let buff_cache = if wide { @@ -79,35 +99,64 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { mem_info.buffers + mem_info.cached }; let cache = if wide { mem_info.cached } else { 0 }; - let used = mem_info.total - mem_info.free; + let used = mem_info.total - mem_info.available; if wide { - println!(" total used free shared buffers cache available"); - println!( - "Mem: {:11} {:11} {:11} {:11} {:11} {:11} {:11}", - mem_info.total, - used, - mem_info.free, - mem_info.shared, - buff_cache, - cache + mem_info.reclaimable, - mem_info.available - ); + wide_header(); + if human { + println!( + "{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}", + "Mem:", + humanized(mem_info.total, si), + humanized(used, si), + humanized(mem_info.free, si), + humanized(mem_info.shared, si), + humanized(buff_cache, si), + humanized(cache + mem_info.reclaimable, si), + humanized(mem_info.available, si), + ) + } else { + println!( + "{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}", + "Mem:", + convert(mem_info.total), + convert(used), + convert(mem_info.free), + convert(mem_info.shared), + convert(buff_cache), + convert(cache + mem_info.reclaimable), + convert(mem_info.available), + ) + } } else { - println!(" total used free shared buff/cache available"); - println!( - "Mem: {:11} {:11} {:11} {:11} {:11} {:11}", - mem_info.total, - used, - mem_info.free, - mem_info.shared, - buff_cache + mem_info.reclaimable, - mem_info.available - ); + header(); + if human { + println!( + "{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}", + "Mem:", + humanized(mem_info.total, si), + humanized(used, si), + humanized(mem_info.free, si), + humanized(mem_info.shared, si), + humanized(buff_cache + mem_info.reclaimable, si), + humanized(mem_info.available, si), + ) + } else { + println!( + "{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}", + "Mem:", + convert(mem_info.total), + convert(used), + convert(mem_info.free), + convert(mem_info.shared), + convert(buff_cache + mem_info.reclaimable), + convert(mem_info.available), + ) + } } println!( - "Swap: {:11} {:11} {:11}", - mem_info.swap_total, mem_info.swap_used, mem_info.swap_free + "{:8}{:>12}{:>12}{:>12}", + "Swap:", mem_info.swap_total, mem_info.swap_used, mem_info.swap_free ); } Err(e) => { @@ -125,6 +174,26 @@ pub fn uu_app() -> Command { .about(ABOUT) .override_usage(format_usage(USAGE)) .infer_long_args(true) + .disable_help_flag(true) + .group(ArgGroup::new("unit").args([ + "bytes", "kilo", "mega", "giga", "tera", "peta", "kibi", "mebi", "gibi", "tebi", "pebi", + ])) + .args([ + arg!(-b --bytes "show output in bytes").action(ArgAction::SetTrue), + arg!( --kilo "show output in kilobytes").action(ArgAction::SetFalse), + arg!( --mega "show output in megabytes").action(ArgAction::SetTrue), + arg!( --giga "show output in gigabytes").action(ArgAction::SetTrue), + arg!( --tera "show output in terabytes").action(ArgAction::SetTrue), + arg!( --peta "show output in petabytes").action(ArgAction::SetTrue), + arg!(-k --kibi "show output in kibibytes").action(ArgAction::SetTrue), + arg!(-m --mebi "show output in mebibytes").action(ArgAction::SetTrue), + arg!(-g --gibi "show output in gibibytes").action(ArgAction::SetTrue), + arg!( --tebi "show output in tebibytes").action(ArgAction::SetTrue), + arg!( --pebi "show output in pebibytes").action(ArgAction::SetTrue), + arg!(-h --human "show human-readable output").action(ArgAction::SetTrue), + arg!( --si "use powers of 1000 not 1024").action(ArgAction::SetFalse), + // arg!(-L --line "show output on a single line"), + ]) .arg( Arg::new("wide") .short('w') @@ -132,6 +201,12 @@ pub fn uu_app() -> Command { .help("wide output") .action(ArgAction::SetTrue), ) + .arg( + Arg::new("help") + .long("help") + .action(ArgAction::Help) + .help("display this help and exit"), + ) } fn parse_meminfo_value(value: &str) -> Result { @@ -153,3 +228,49 @@ fn parse_meminfo_value(value: &str) -> Result { }) }) } + +// Here's the `-h` `--human` flag processing logic +fn humanized(kib: u64, si: bool) -> String { + let binding = ByteSize::kib(kib).to_string_as(si); + let split: Vec<&str> = binding.split(' ').collect(); + + // TODO: finish the logic of automatic scale. + let num_string = String::from(split[0]); + + let unit_string = { + let mut tmp = String::from(split[1]); + tmp.pop(); + tmp + }; + format!("{}{}", num_string, unit_string) +} + +fn detect_unit(arg: &ArgMatches) -> fn(u64) -> u64 { + match arg { + _ if arg.get_flag("bytes") => |kib: u64| ByteSize::kib(kib).0, + _ if arg.get_flag("mega") => |kib: u64| ByteSize::kib(kib).0 / MB, + _ if arg.get_flag("giga") => |kib: u64| ByteSize::kib(kib).0 / GB, + _ if arg.get_flag("tera") => |kib: u64| ByteSize::kib(kib).0 / TB, + _ if arg.get_flag("peta") => |kib: u64| ByteSize::kib(kib).0 / PB, + _ if arg.get_flag("kibi") => |kib: u64| ByteSize::kib(kib).0 / KIB, + _ if arg.get_flag("mebi") => |kib: u64| ByteSize::kib(kib).0 / MIB, + _ if arg.get_flag("gibi") => |kib: u64| ByteSize::kib(kib).0 / GIB, + _ if arg.get_flag("tebi") => |kib: u64| ByteSize::kib(kib).0 / TIB, + _ if arg.get_flag("pebi") => |kib: u64| ByteSize::kib(kib).0 / PIB, + _ => |kib: u64| kib, + } +} + +fn wide_header() { + println!( + "{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}", + " ", "total", "used", "free", "shared", "buffers", "cache", "available", + ); +} + +fn header() { + println!( + "{:8}{:>12}{:>12}{:>12}{:>12}{:>12}{:>12}", + " ", "total", "used", "free", "shared", "buff/cache", "available", + ) +}