From 69d9c6e4a5fd6272fecf0556a73e5cc7dd31d82e Mon Sep 17 00:00:00 2001 From: sisungo Date: Fri, 28 Jun 2024 22:23:50 +0800 Subject: [PATCH] Implement `rev` (#56) * Implement `rev` * Use uucore facility for error reporting --- Cargo.lock | 9 +++++ Cargo.toml | 2 + src/uu/rev/Cargo.toml | 17 ++++++++ src/uu/rev/rev.md | 7 ++++ src/uu/rev/src/main.rs | 1 + src/uu/rev/src/rev.rs | 81 +++++++++++++++++++++++++++++++++++++++ tests/by-util/test_rev.rs | 12 ++++++ tests/tests.rs | 4 ++ 8 files changed, 133 insertions(+) create mode 100644 src/uu/rev/Cargo.toml create mode 100644 src/uu/rev/rev.md create mode 100644 src/uu/rev/src/main.rs create mode 100644 src/uu/rev/src/rev.rs create mode 100644 tests/by-util/test_rev.rs diff --git a/Cargo.lock b/Cargo.lock index 6f38306..f2aac43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -644,6 +644,7 @@ dependencies = [ "uu_ctrlaltdel", "uu_lscpu", "uu_mountpoint", + "uu_rev", "uucore", "xattr", ] @@ -674,6 +675,14 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_rev" +version = "0.0.1" +dependencies = [ + "clap", + "uucore", +] + [[package]] name = "uucore" version = "0.0.27" diff --git a/Cargo.toml b/Cargo.toml index 1b35bd0..95d2a31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ feat_common_core = [ "mountpoint", "lscpu", "ctrlaltdel", + "rev", ] [workspace.dependencies] @@ -58,6 +59,7 @@ textwrap = { workspace = true } lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" } mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", path = "src/uu/mountpoint" } ctrlaltdel = { optional = true, version = "0.0.1", package = "uu_ctrlaltdel", path = "src/uu/ctrlaltdel" } +rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" } [dev-dependencies] pretty_assertions = "1" diff --git a/src/uu/rev/Cargo.toml b/src/uu/rev/Cargo.toml new file mode 100644 index 0000000..bc57017 --- /dev/null +++ b/src/uu/rev/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "uu_rev" +version = "0.0.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +uucore = { workspace = true } +clap = { workspace = true } + +[lib] +path = "src/rev.rs" + +[[bin]] +name = "rev" +path = "src/main.rs" diff --git a/src/uu/rev/rev.md b/src/uu/rev/rev.md new file mode 100644 index 0000000..01bf0a7 --- /dev/null +++ b/src/uu/rev/rev.md @@ -0,0 +1,7 @@ +# rev + +``` +rev [options] [file ...] +``` + +Reverse lines characterwise. \ No newline at end of file diff --git a/src/uu/rev/src/main.rs b/src/uu/rev/src/main.rs new file mode 100644 index 0000000..363375a --- /dev/null +++ b/src/uu/rev/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_rev); diff --git a/src/uu/rev/src/rev.rs b/src/uu/rev/src/rev.rs new file mode 100644 index 0000000..75e56fe --- /dev/null +++ b/src/uu/rev/src/rev.rs @@ -0,0 +1,81 @@ +// This file is part of the uutils util-linux package. +// +// 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::{Arg, ArgAction}; +use std::env; +use std::io::{BufRead, BufReader, Read, Write}; +use uucore::{error::UResult, format_usage, help_about, help_usage}; + +const ABOUT: &str = help_about!("rev.md"); +const USAGE: &str = help_usage!("rev.md"); + +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; + let files = matches.get_many::("file"); + + match files { + Some(files) => { + for path in files { + let file = match std::fs::File::open(path) { + Ok(val) => val, + Err(err) => { + uucore::error::set_exit_code(1); + uucore::show_error!("cannot open {}: {}", path, err); + continue; + } + }; + if let Err(err) = rev_stream(file) { + uucore::error::set_exit_code(1); + uucore::show_error!("cannot read {}: {}", path, err); + } + } + } + None => { + let stdin = std::io::stdin().lock(); + let _ = rev_stream(stdin); + } + } + + Ok(()) +} + +fn rev_stream(stream: impl Read) -> std::io::Result<()> { + let mut stdout = std::io::stdout().lock(); + let mut stream = BufReader::new(stream); + let mut buf = Vec::with_capacity(4096); + loop { + buf.clear(); + stream.read_until(b'\n', &mut buf)?; + if buf.last().copied() != Some(b'\n') { + buf.reverse(); + stdout.write_all(&buf)?; + break; + } else { + buf.pop(); + buf.reverse(); + buf.push(b'\n'); + stdout.write_all(&buf)?; + } + } + Ok(()) +} + +pub fn uu_app() -> Command { + Command::new(uucore::util_name()) + .version(crate_version!()) + .about(ABOUT) + .override_usage(format_usage(USAGE)) + .infer_long_args(true) + .arg( + Arg::new("file") + .value_name("FILE") + .help("Paths of files to reverse") + .index(1) + .action(ArgAction::Set) + .num_args(1..), + ) +} diff --git a/tests/by-util/test_rev.rs b/tests/by-util/test_rev.rs new file mode 100644 index 0000000..94285a4 --- /dev/null +++ b/tests/by-util/test_rev.rs @@ -0,0 +1,12 @@ +// This file is part of the uutils util-linux package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. +// spell-checker:ignore (words) symdir somefakedir + +use crate::common::util::TestScenario; + +#[test] +fn test_invalid_arg() { + new_ucmd!().arg("--definitely-invalid").fails().code_is(1); +} diff --git a/tests/tests.rs b/tests/tests.rs index a1574f5..1862b23 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -16,3 +16,7 @@ mod test_mountpoint; #[cfg(feature = "ctrlaltdel")] #[path = "by-util/test_ctrlaltdel.rs"] mod test_ctrlaltdel; + +#[cfg(feature = "rev")] +#[path = "by-util/test_rev.rs"] +mod test_rev;