Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
10 changes: 10 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

build:
cargo build
lint:
cargo fmt
cargo clippy -- -D warnings
test:
cargo test
cargo fmt
cargo clippy -- -D warnings
218 changes: 114 additions & 104 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
5 changes: 4 additions & 1 deletion arch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
33 changes: 26 additions & 7 deletions arch/src/main.rs
Original file line number Diff line number Diff line change
@@ -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::<String>("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 {
Expand All @@ -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);
}
}
3 changes: 1 addition & 2 deletions basename/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ fn run(args: Args) -> Vec<String> {
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 {
Expand Down
2 changes: 1 addition & 1 deletion cat/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,6 @@ fn count_character(character_count: &mut usize, args: &Args) {
}

fn push_caret<T: Write>(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();
}
7 changes: 7 additions & 0 deletions stdlib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "stdlib"
version = "0.1.0"
edition = "2024"

[dependencies]
clap = { workspace = true }
11 changes: 11 additions & 0 deletions stdlib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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> "Format to output to (plain, table, json, yaml)")
.default_value("plain"),
)
.version(crate_version!())
}
4 changes: 2 additions & 2 deletions whoami/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}");
}
}
}
Loading