diff --git a/Cargo.toml b/Cargo.toml index 6e691a8..933b039 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,15 @@ members = [ "dirname", "echo", "env", + "stdlib", "wc", "whoami", ] +resolver = "3" + [workspace.dependencies] -clap = { version = "4.0", features = ["derive"] } +clap = { version = "4.0", features = ["cargo", "derive"] } +serde = { version = "1.0" } +serde_json = { version = "1.0" } +tabled = { version = "0.20" } diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..ddd0188 --- /dev/null +++ b/Justfile @@ -0,0 +1,10 @@ + +build: + cargo build +lint: + cargo fmt + cargo clippy -- -D warnings +test: + cargo test + cargo fmt + cargo clippy -- -D warnings diff --git a/README.md b/README.md index afa7bd9..63feb6d 100644 --- a/README.md +++ b/README.md @@ -6,113 +6,123 @@ A Rust implementation of the stalwart GNU [coreutils](https://github.com/coreuti ## Why -This began as, and continues to be, a learning exercise to better understand the [Rust](https://www.rust-lang.org/) programming language. A 100+ program spin on the [Advent of Code](https://en.wikipedia.org/wiki/Advent_of_Code). +This began as, and continues to be, a learning exercise to better understand the [Rust](https://www.rust-lang.org/) programming language. A 100+ program spin on the [Advent of Code](https://en.wikipedia.org/wiki/Advent_of_Code), allowing me to re-imagine a modern coreutils and explore system programming and best practices. + +## Design goals + +- all commands should support output formatting: plain, table, json and yaml to allow them to be more easily consumed by automation, CI, and AI. + +## TODO + +- Finish refactor of completed commands (to use stdlib and follow modern approach) +- for easier testing, a way to temporarily add the project binaries into the current path. + - add `just shell` target ## Status | util | status | | ---- | ------ | | arch | :white_check_mark: | -| b2sum | :white_check_mark: | -| base32 | :white_check_mark: | -| base64 | :white_check_mark: | -| basename | :white_check_mark: | -| cat | :white_check_mark: | -| chcon | | -| chgrp | | -| chmod | | -| chown | | -| chroot | | -| cksum | | -| comm | | -| cp | | -| csplit | | -| cut | | -| date | | -| dd | | -| df | | -| dir | | -| dircolors | | -| dirname | :white_check_mark: | -| du | | -| echo | :white_check_mark: | -| env | :white_check_mark: | -| expand | | -| expr | | -| factor | | -| false | | -| fmt | | -| fold | | -| groups | | -| head | | -| hostid | | -| id | | -| install | | -| join | | -| link | | -| ln | | -| logname | | -| ls | | -| md5sum | | -| mkdir | | -| mkfifo | | -| mknod | | -| mktemp | | -| mv | | -| nice | | -| nl | | -| nohup | | -| nproc | | -| numfmt | | -| od | | -| paste | | -| pathchk | | -| pinky | | -| pr | | -| printenv | | -| printf | | -| ptx | | -| pwd | | -| readlink | | -| realpath | | -| rm | | -| rmdir | | -| runcon | | -| seq | | -| sha1sum | | -| sha224sum | | -| sha256sum | | -| sha384sum | | -| sha512sum | | -| shred | | -| shuf | | -| sleep | | -| sort | | -| split | | -| stat | | -| stdbuf | | -| stty | | -| sum | | -| sync | | -| tac | | -| tail | | -| tee | | -| test | | -| timeout | | -| touch | | -| tr | | -| true | | -| truncate | | -| tsort | | -| tty | | -| uname | | -| unexpand | | -| uniq | | -| unlink | | -| uptime | | -| users | | -| vdir | | -| wc | :white_check_mark: | -| who | | -| whoami | :white_check_mark: | -| yes | | +| b2sum | :white_large_square: | +| base32 | :white_large_square: | +| base64 | :white_large_square: | +| basename | :white_large_square: | +| cat | :white_large_square: | +| chcon | :white_large_square: | +| chgrp | :white_large_square: | +| chmod | :white_large_square: | +| chown | :white_large_square: | +| chroot | :white_large_square: | +| cksum | :white_large_square: | +| comm | :white_large_square: | +| cp | :white_large_square: | +| csplit | :white_large_square: | +| cut | :white_large_square: | +| date | :white_large_square: | +| dd | :white_large_square: | +| df | :white_large_square: | +| dir | :white_large_square: | +| dircolors | :white_large_square: | +| dirname | :white_large_square: | +| du | :white_large_square: | +| echo | :white_large_square: | +| env | :white_large_square: | +| expand | :white_large_square: | +| expr | :white_large_square: | +| factor | :white_large_square: | +| false | :white_large_square: | +| fmt | :white_large_square: | +| fold | :white_large_square: | +| groups | :white_large_square: | +| head | :white_large_square: | +| hostid | :white_large_square: | +| id | :white_large_square: | +| install | :white_large_square: | +| join | :white_large_square: | +| link | :white_large_square: | +| ln | :white_large_square: | +| logname | :white_large_square: | +| ls | :white_large_square: | +| md5sum | :white_large_square: | +| mkdir | :white_large_square: | +| mkfifo | :white_large_square: | +| mknod | :white_large_square: | +| mktemp | :white_large_square: | +| mv | :white_large_square: | +| nice | :white_large_square: | +| nl | :white_large_square: | +| nohup | :white_large_square: | +| nproc | :white_large_square: | +| numfmt | :white_large_square: | +| od | :white_large_square: | +| paste | :white_large_square: | +| pathchk | :white_large_square: | +| pinky | :white_large_square: | +| pr | :white_large_square: | +| printenv | :white_large_square: | +| printf | :white_large_square: | +| ptx | :white_large_square: | +| pwd | :white_large_square: | +| readlink | :white_large_square: | +| realpath | :white_large_square: | +| rm | :white_large_square: | +| rmdir | :white_large_square: | +| runcon | :white_large_square: | +| seq | :white_large_square: | +| sha1sum | :white_large_square: | +| sha224sum | :white_large_square: | +| sha256sum | :white_large_square: | +| sha384sum | :white_large_square: | +| sha512sum | :white_large_square: | +| shred | :white_large_square: | +| shuf | :white_large_square: | +| sleep | :white_large_square: | +| sort | :white_large_square: | +| split | :white_large_square: | +| stat | :white_large_square: | +| stdbuf | :white_large_square: | +| stty | :white_large_square: | +| sum | :white_large_square: | +| sync | :white_large_square: | +| tac | :white_large_square: | +| tail | :white_large_square: | +| tee | :white_large_square: | +| test | :white_large_square: | +| timeout | :white_large_square: | +| touch | :white_large_square: | +| tr | :white_large_square: | +| true | :white_large_square: | +| truncate | :white_large_square: | +| tsort | :white_large_square: | +| tty | :white_large_square: | +| uname | :white_large_square: | +| unexpand | :white_large_square: | +| uniq | :white_large_square: | +| unlink | :white_large_square: | +| uptime | :white_large_square: | +| users | :white_large_square: | +| vdir | :white_large_square: | +| wc | :white_large_square: | +| who | :white_large_square: | +| whoami | :white_large_square: | +| yes | :white_large_square: | diff --git a/arch/Cargo.toml b/arch/Cargo.toml index 3da11fd..a25432e 100644 --- a/arch/Cargo.toml +++ b/arch/Cargo.toml @@ -8,4 +8,7 @@ edition = "2021" [dependencies] platform-info = "1.0.1" clap = { workspace = true } - +serde = { workspace = true } +serde_json = { workspace = true } +stdlib = { path = "../stdlib" } +tabled = { workspace = true } diff --git a/arch/src/main.rs b/arch/src/main.rs index 04ee5b9..fb84627 100644 --- a/arch/src/main.rs +++ b/arch/src/main.rs @@ -1,15 +1,34 @@ -use clap::Parser; use platform_info::*; +use serde_json::json; +use tabled::{builder::Builder, settings::Style}; -/// Print machine architecture -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct Args {} +use stdlib::clap_base_command; fn main() { + let matches = clap_base_command().get_matches(); + let arch = run(); - println!("{}", arch); + if let Some(output) = matches.get_one::("output") { + match output.as_str() { + "table" => { + let mut builder = Builder::new(); + builder.push_column(["Architecture"]); + builder.push_record([arch]); + let mut table = builder.build(); + println!("{}", table.with(Style::rounded())); + } + "json" => { + let output = json!({ + "architecture": arch, + }); + + println!("{}", serde_json::to_string(&output).unwrap()); + } + "yaml" => println!("architecture: \"{arch}\""), + _ => println!("{arch}"), + } + } } fn run() -> String { @@ -23,7 +42,7 @@ mod tests { #[test] fn test_architecture() { - // assert that we got _a_ architecture back + // assert that we got _an_ architecture back assert_ne!(run().len(), 0); } } diff --git a/basename/src/main.rs b/basename/src/main.rs index 527bfdf..3468ca4 100644 --- a/basename/src/main.rs +++ b/basename/src/main.rs @@ -46,8 +46,7 @@ fn run(args: Args) -> Vec { let path = shellexpand::tilde(&arg); let basename = get_basename(&path); - if args.suffix.is_some() { - let suffix = args.suffix.as_ref().unwrap(); + if let Some(suffix) = &args.suffix { let bms = basename.strip_suffix(suffix).unwrap_or(&basename); basenames.push(bms.to_string()); } else { diff --git a/cat/src/main.rs b/cat/src/main.rs index dfd3237..9e567c4 100644 --- a/cat/src/main.rs +++ b/cat/src/main.rs @@ -212,6 +212,6 @@ fn count_character(character_count: &mut usize, args: &Args) { } fn push_caret(stdout: &mut T, _stderr: &mut std::io::StderrLock, notation: u8) { - stdout.write_all(&[b'^']).unwrap(); + stdout.write_all(b"^").unwrap(); stdout.write_all(&[notation]).unwrap(); } diff --git a/stdlib/Cargo.toml b/stdlib/Cargo.toml new file mode 100644 index 0000000..85ae322 --- /dev/null +++ b/stdlib/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "stdlib" +version = "0.1.0" +edition = "2024" + +[dependencies] +clap = { workspace = true } diff --git a/stdlib/src/lib.rs b/stdlib/src/lib.rs new file mode 100644 index 0000000..7065564 --- /dev/null +++ b/stdlib/src/lib.rs @@ -0,0 +1,11 @@ +use clap::{Command, arg, command, crate_version}; + +/// Returns the base clap command with common arguments and flags. +pub fn clap_base_command() -> Command { + command!() + .arg( + arg!(-o --output "Format to output to (plain, table, json, yaml)") + .default_value("plain"), + ) + .version(crate_version!()) +} diff --git a/whoami/src/main.rs b/whoami/src/main.rs index 325cd23..71f16d3 100644 --- a/whoami/src/main.rs +++ b/whoami/src/main.rs @@ -8,8 +8,8 @@ fn main() { let user = get_user_by_uid(get_current_uid()); if let Some(u) = user { let name = u.name().to_str(); - if name.is_some() { - println!("{}", name.unwrap()); + if let Some(name) = name { + println!("{name}"); } } }