Add chgrp
applet; Add args
module for commonly used args to increase
consistency
This commit is contained in:
parent
f68ae6df91
commit
9df197a4b9
44
src/args/mod.rs
Normal file
44
src/args/mod.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use clap::{Arg, ArgAction};
|
||||
|
||||
pub fn verbose() -> Arg {
|
||||
Arg::new("verbose")
|
||||
.help("output a diagnostic for every file processed")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue)
|
||||
}
|
||||
|
||||
pub fn header() -> Arg {
|
||||
Arg::new("HEADER")
|
||||
.help(
|
||||
"Each file is preceded by a header consisting of the string \
|
||||
\"==> XXX <==\" where \"XXX\" is the name of the file.",
|
||||
)
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue)
|
||||
}
|
||||
|
||||
pub fn changes() -> Arg {
|
||||
Arg::new("changes")
|
||||
.help("report only when a change is made")
|
||||
.short('c')
|
||||
.long("changes")
|
||||
.conflicts_with("verbose")
|
||||
.action(ArgAction::SetTrue)
|
||||
}
|
||||
|
||||
pub fn recursive() -> Arg {
|
||||
Arg::new("recursive")
|
||||
.help("operate on files and directories recursively")
|
||||
.short('R')
|
||||
.long("recursive")
|
||||
.action(ArgAction::SetTrue)
|
||||
}
|
||||
|
||||
pub fn color() -> Arg {
|
||||
Arg::new("color")
|
||||
.short('c')
|
||||
.long("color")
|
||||
.value_parser(["always", "ansi", "auto", "never"])
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
use super::Cmd;
|
||||
use crate::args;
|
||||
use clap::{value_parser, Arg, ArgAction, Command};
|
||||
use data_encoding::BASE32;
|
||||
use std::{
|
||||
@ -16,41 +17,7 @@ impl Cmd for Base32 {
|
||||
Command::new("base32")
|
||||
.author("Nathan Fisher")
|
||||
.about("Base32 encode/decode data and print to standard output")
|
||||
.args([
|
||||
Arg::new("INPUT")
|
||||
.help("The input file to use")
|
||||
.num_args(1..),
|
||||
Arg::new("DECODE")
|
||||
.help("Decode rather than encode")
|
||||
.short('d')
|
||||
.long("decode")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("IGNORE")
|
||||
.help("Ignore whitespace when decoding")
|
||||
.short('i')
|
||||
.long("ignore-space")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("WRAP")
|
||||
.help("Wrap encoded lines after n characters")
|
||||
.short('w')
|
||||
.long("wrap")
|
||||
.value_parser(value_parser!(usize))
|
||||
.default_value("76"),
|
||||
Arg::new("color")
|
||||
.short('c')
|
||||
.long("color")
|
||||
.value_parser(["always", "ansi", "auto", "never"]),
|
||||
Arg::new("VERBOSE")
|
||||
.help("Display a header naming each file")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("QUIET")
|
||||
.help("Do not display header, even with multiple files")
|
||||
.short('q')
|
||||
.long("quiet")
|
||||
.action(ArgAction::SetTrue),
|
||||
])
|
||||
.args(args())
|
||||
}
|
||||
|
||||
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
@ -75,7 +42,7 @@ impl Cmd for Base32 {
|
||||
_ => ColorChoice::Never,
|
||||
};
|
||||
for (index, file) in files.into_iter().enumerate() {
|
||||
if { len > 1 || matches.get_flag("VERBOSE") } && !matches.get_flag("QUIET") {
|
||||
if { len > 1 || matches.get_flag("verbose") } && !matches.get_flag("QUIET") {
|
||||
let mut stdout = StandardStream::stdout(color);
|
||||
stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
|
||||
match index {
|
||||
@ -109,6 +76,37 @@ impl Cmd for Base32 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn args() -> [Arg; 7] {
|
||||
[
|
||||
Arg::new("INPUT")
|
||||
.help("The input file to use")
|
||||
.num_args(1..),
|
||||
Arg::new("DECODE")
|
||||
.help("Decode rather than encode")
|
||||
.short('d')
|
||||
.long("decode")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("IGNORE")
|
||||
.help("Ignore whitespace when decoding")
|
||||
.short('i')
|
||||
.long("ignore-space")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("WRAP")
|
||||
.help("Wrap encoded lines after n characters")
|
||||
.short('w')
|
||||
.long("wrap")
|
||||
.value_parser(value_parser!(usize))
|
||||
.default_value("76"),
|
||||
args::color(),
|
||||
args::verbose(),
|
||||
Arg::new("QUIET")
|
||||
.help("Do not display header, even with multiple files")
|
||||
.short('q')
|
||||
.long("quiet")
|
||||
.action(ArgAction::SetTrue),
|
||||
]
|
||||
}
|
||||
|
||||
fn decode_base32(mut contents: String, ignore: bool) -> Result<(), Box<dyn Error>> {
|
||||
if ignore {
|
||||
contents.retain(|c| !c.is_whitespace());
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::Cmd;
|
||||
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
|
||||
use super::{base32::args, Cmd};
|
||||
use clap::{ArgMatches, Command};
|
||||
use data_encoding::BASE64;
|
||||
use std::{
|
||||
error::Error,
|
||||
@ -16,41 +16,7 @@ impl Cmd for Base64 {
|
||||
Command::new("base64")
|
||||
.author("Nathan Fisher")
|
||||
.about("Base64 encode/decode data and print to standard output")
|
||||
.args([
|
||||
Arg::new("INPUT")
|
||||
.help("The input file to use")
|
||||
.num_args(0..),
|
||||
Arg::new("DECODE")
|
||||
.help("Decode rather than encode")
|
||||
.short('d')
|
||||
.long("decode")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("IGNORE")
|
||||
.help("Ignore whitespace when decoding")
|
||||
.short('i')
|
||||
.long("ignore-space")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("WRAP")
|
||||
.help("Wrap encoded lines after n characters")
|
||||
.short('w')
|
||||
.long("wrap")
|
||||
.default_value("76")
|
||||
.value_parser(value_parser!(usize)),
|
||||
Arg::new("color")
|
||||
.short('c')
|
||||
.long("color")
|
||||
.value_parser(["always", "ansi", "auto", "never"]),
|
||||
Arg::new("VERBOSE")
|
||||
.help("Display a header naming each file")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("QUIET")
|
||||
.help("Do not display header, even with multiple files")
|
||||
.short('q')
|
||||
.long("quiet")
|
||||
.action(ArgAction::SetTrue),
|
||||
])
|
||||
.args(args())
|
||||
}
|
||||
|
||||
fn run(&self, matches: Option<&ArgMatches>) -> Result<(), Box<dyn Error>> {
|
||||
|
@ -1,5 +1,8 @@
|
||||
use super::{Cmd, Feedback};
|
||||
use crate::mode::{Mode, Parser};
|
||||
use crate::{
|
||||
args,
|
||||
mode::{Mode, Parser},
|
||||
};
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use std::{
|
||||
error::Error,
|
||||
@ -20,32 +23,17 @@ impl Cmd for Chmod {
|
||||
.author("Nathan Fisher")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.args([
|
||||
Arg::new("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.help("output a diagnostic for every file processed")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("changes")
|
||||
.short('c')
|
||||
.long("changes")
|
||||
.help("like verbose but report only when a change is made")
|
||||
.conflicts_with("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::verbose(),
|
||||
args::changes(),
|
||||
Arg::new("quiet")
|
||||
.short('f')
|
||||
.long("silent")
|
||||
.visible_alias("quiet")
|
||||
.help("suppress most error messages")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("recursive")
|
||||
.short('R')
|
||||
.long("recursive")
|
||||
.visible_short_alias('r')
|
||||
.help("change files and directories recursively")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::recursive(),
|
||||
Arg::new("mode")
|
||||
.value_name("MODE")
|
||||
.value_delimiter(',')
|
||||
.num_args(1)
|
||||
.required(true),
|
||||
Arg::new("file")
|
||||
|
158
src/cmd/chown/chgrp.rs
Normal file
158
src/cmd/chown/chgrp.rs
Normal file
@ -0,0 +1,158 @@
|
||||
use super::{Group, Recurse, Traversal};
|
||||
use crate::cmd::{Cmd, Feedback};
|
||||
use crate::pw;
|
||||
use clap::{Arg, ArgGroup, Command, ValueHint};
|
||||
use std::{
|
||||
error::Error,
|
||||
fs::File,
|
||||
io,
|
||||
os::{fd::AsRawFd, unix::prelude::MetadataExt},
|
||||
path::PathBuf,
|
||||
};
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Chgrp;
|
||||
|
||||
impl Cmd for Chgrp {
|
||||
fn cli(&self) -> clap::Command {
|
||||
Command::new("chgrp")
|
||||
.about("change group ownership")
|
||||
.author("Nathan Fisher")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.arg(
|
||||
Arg::new("group")
|
||||
.value_name("GROUP")
|
||||
.value_hint(ValueHint::Username)
|
||||
.num_args(1)
|
||||
.required(true),
|
||||
)
|
||||
.args(super::args())
|
||||
.group(
|
||||
ArgGroup::new("links")
|
||||
.args(["cli-traverse", "full-traverse", "no-traverse"])
|
||||
.requires("recursive")
|
||||
.multiple(false),
|
||||
)
|
||||
}
|
||||
|
||||
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let Some(matches) = matches else {
|
||||
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "no input")));
|
||||
};
|
||||
let recurse = Recurse::from_matches(matches);
|
||||
let feedback = Feedback::from_matches(matches);
|
||||
let group = if let Some(grp) = matches.get_one::<String>("group") {
|
||||
let gid = pw::get_gid_for_groupname(grp)
|
||||
.ok_or(io::Error::new(io::ErrorKind::Other, "cannot get gid"))?;
|
||||
Group { name: grp, gid }
|
||||
} else {
|
||||
return Err(Box::new(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"no user specified",
|
||||
)));
|
||||
};
|
||||
if let Some(files) = matches.get_many::<String>("file") {
|
||||
for f in files {
|
||||
let action = Action {
|
||||
path: PathBuf::from(f),
|
||||
group: group.clone(),
|
||||
feedback,
|
||||
};
|
||||
if let Some(r) = recurse {
|
||||
if action.path.is_dir() {
|
||||
action.recurse(recurse)?;
|
||||
} else if action.path.is_symlink() {
|
||||
if r.traversal != Traversal::NoLinks {
|
||||
action.recurse(recurse)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
action.apply()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<crate::Path> {
|
||||
Some(crate::Path::Bin)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Action<'a> {
|
||||
path: PathBuf,
|
||||
group: Group<'a>,
|
||||
feedback: Option<Feedback>,
|
||||
}
|
||||
|
||||
impl Action<'_> {
|
||||
fn apply(&self) -> Result<(), Box<dyn Error>> {
|
||||
let fd = File::open(&self.path)?;
|
||||
let meta = fd.metadata()?;
|
||||
let uid = meta.uid();
|
||||
let gid = meta.gid();
|
||||
unsafe {
|
||||
if libc::fchown(fd.as_raw_fd(), uid, self.group.gid) != 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
}
|
||||
drop(fd);
|
||||
if let Some(feedback) = self.feedback {
|
||||
match feedback {
|
||||
Feedback::Full => {
|
||||
if self.group.gid != gid {
|
||||
self.display_changes(gid)?;
|
||||
} else {
|
||||
self.display_retained();
|
||||
}
|
||||
}
|
||||
Feedback::Changes => {
|
||||
if self.group.gid != gid {
|
||||
self.display_changes(gid)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_changes(&self, gid: u32) -> Result<(), std::str::Utf8Error> {
|
||||
let groupname = pw::get_groupname_for_gid(gid)?;
|
||||
println!(
|
||||
"{} changed from {groupname} to {}",
|
||||
&self.path.display(),
|
||||
&self.group.name
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_retained(&self) {
|
||||
println!("{} retained as {}", &self.path.display(), &self.group.name);
|
||||
}
|
||||
|
||||
fn into_child(&self, entry: DirEntry) -> Result<Self, Box<dyn Error>> {
|
||||
let path = entry.path().to_path_buf();
|
||||
Ok(Self {
|
||||
path,
|
||||
group: self.group.clone(),
|
||||
feedback: self.feedback,
|
||||
})
|
||||
}
|
||||
|
||||
fn recurse(&self, recurse: Option<Recurse>) -> Result<(), Box<dyn Error>> {
|
||||
let walker = WalkDir::new(&self.path)
|
||||
.max_open(1)
|
||||
.same_file_system(recurse.map_or(false, |x| x.same_filesystem))
|
||||
.follow_links(recurse.map_or(false, |x| match x.traversal {
|
||||
Traversal::FullLinks => true,
|
||||
_ => false,
|
||||
}));
|
||||
for entry in walker {
|
||||
let entry = entry?;
|
||||
let action = self.into_child(entry)?;
|
||||
action.apply()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use super::{Cmd, Feedback};
|
||||
use crate::pw;
|
||||
use crate::{args, pw};
|
||||
use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command, ValueHint};
|
||||
use std::{
|
||||
error::Error,
|
||||
@ -10,6 +10,9 @@ use std::{
|
||||
};
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
mod chgrp;
|
||||
pub use chgrp::Chgrp;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Chown;
|
||||
|
||||
@ -19,54 +22,14 @@ impl Cmd for Chown {
|
||||
.about("change file owner and group")
|
||||
.author("Nathan Fisher")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.args([
|
||||
.arg(
|
||||
Arg::new("user")
|
||||
.value_name("OWNER[:GROUP]")
|
||||
.value_hint(ValueHint::Username)
|
||||
.num_args(1)
|
||||
.required(true),
|
||||
Arg::new("changes")
|
||||
.help("report only when a change is made")
|
||||
.short('c')
|
||||
.long("changes")
|
||||
.conflicts_with("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("verbose")
|
||||
.help("output a diagnostic for every file processed")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("recursive")
|
||||
.help("operate on files and directories recursively")
|
||||
.short('R')
|
||||
.long("recursive")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("cli-traverse")
|
||||
.help(
|
||||
"if a command line argument is a symbolic link to a directory, traverse it",
|
||||
)
|
||||
.short('H')
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("full-traverse")
|
||||
.help("traverse every symbolic link encountered in a directory")
|
||||
.short('L')
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("no-traverse")
|
||||
.help("do not traverse any symbolic links (default)")
|
||||
.short('P')
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("same-filesystem")
|
||||
.help("do not cross filesystem boundaries (requires recursive)")
|
||||
.short('s')
|
||||
.requires("recursive")
|
||||
.long("same-filesystem")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("file")
|
||||
.value_name("FILE")
|
||||
.value_hint(ValueHint::AnyPath)
|
||||
.num_args(1..)
|
||||
.required(true),
|
||||
])
|
||||
.args(args())
|
||||
.group(
|
||||
ArgGroup::new("links")
|
||||
.args(["cli-traverse", "full-traverse", "no-traverse"])
|
||||
@ -127,6 +90,37 @@ impl Cmd for Chown {
|
||||
}
|
||||
}
|
||||
|
||||
fn args() -> [Arg; 8] {
|
||||
[
|
||||
args::changes(),
|
||||
args::verbose(),
|
||||
args::recursive(),
|
||||
Arg::new("cli-traverse")
|
||||
.help("if a command line argument is a symbolic link to a directory, traverse it")
|
||||
.short('H')
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("full-traverse")
|
||||
.help("traverse every symbolic link encountered in a directory")
|
||||
.short('L')
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("no-traverse")
|
||||
.help("do not traverse any symbolic links (default)")
|
||||
.short('P')
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("same-filesystem")
|
||||
.help("do not cross filesystem boundaries (requires recursive)")
|
||||
.short('s')
|
||||
.requires("recursive")
|
||||
.long("same-filesystem")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("file")
|
||||
.value_name("FILE")
|
||||
.value_hint(ValueHint::AnyPath)
|
||||
.num_args(1..)
|
||||
.required(true),
|
||||
]
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum Traversal {
|
||||
CliLinks,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::Cmd;
|
||||
use crate::Path;
|
||||
use crate::{args, Path};
|
||||
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
|
||||
use std::{
|
||||
env,
|
||||
@ -20,7 +20,7 @@ impl Cmd for Head {
|
||||
.long_about(
|
||||
"Print the first 10 lines of each FILE to standard output.\n\
|
||||
With more than one FILE, precede each with a header giving the file name.\n\n\
|
||||
With no FILE, or when FILE is -, read standard input."
|
||||
With no FILE, or when FILE is -, read standard input.",
|
||||
)
|
||||
.args([
|
||||
Arg::new("FILES")
|
||||
@ -36,11 +36,7 @@ impl Cmd for Head {
|
||||
.short('q')
|
||||
.long("quiet")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("HEADER")
|
||||
.help("Each file is preceded by a header consisting of the string \"==> XXX <==\" where \"XXX\" is the name of the file.")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::header(),
|
||||
Arg::new("LINES")
|
||||
.help("Count n number of lines (or bytes if -c is specified).")
|
||||
.short('n')
|
||||
@ -48,15 +44,12 @@ impl Cmd for Head {
|
||||
.allow_negative_numbers(false)
|
||||
.conflicts_with("num")
|
||||
.value_parser(value_parser!(usize)),
|
||||
Arg::new("color")
|
||||
.short('C')
|
||||
.long("color")
|
||||
.value_parser(["always", "ansi", "auto", "never"]),
|
||||
args::color(),
|
||||
Arg::new("num")
|
||||
.short('1')
|
||||
.short_aliases(['2', '3', '4', '5', '6', '7', '8', '9'])
|
||||
.hide(true)
|
||||
.action(ArgAction::Append)
|
||||
.action(ArgAction::Append),
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::Cmd;
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use crate::args;
|
||||
use clap::{Arg, Command};
|
||||
use std::{fs, io};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -14,10 +15,7 @@ impl Cmd for Link {
|
||||
.args([
|
||||
Arg::new("file1").required(true).index(1),
|
||||
Arg::new("file2").required(true).index(2),
|
||||
Arg::new("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::verbose(),
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::Cmd;
|
||||
use crate::mode::Parser;
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use crate::{args, mode::Parser};
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use std::{error::Error, ffi::CString, io};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -30,11 +30,7 @@ impl Cmd for MkFifo {
|
||||
.long("mode")
|
||||
.value_name("MODE")
|
||||
.num_args(1),
|
||||
Arg::new("verbose")
|
||||
.help("print a diagnostic for every pipe created")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::verbose(),
|
||||
Arg::new("file").num_args(1..).required(true),
|
||||
])
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
use super::Cmd;
|
||||
use crate::mode::{get_umask, Parser};
|
||||
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
|
||||
use crate::{
|
||||
args,
|
||||
mode::{get_umask, Parser},
|
||||
};
|
||||
use clap::{value_parser, Arg, ArgMatches, Command};
|
||||
use std::{convert::Infallible, error::Error, ffi::CString, io, str::FromStr};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -19,10 +22,7 @@ impl Cmd for MkNod {
|
||||
.help("set file permission bits to MODE, not a=rw - umask")
|
||||
.value_name("MODE")
|
||||
.num_args(1),
|
||||
Arg::new("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::verbose(),
|
||||
Arg::new("file").value_name("NAME").required(true),
|
||||
Arg::new("type")
|
||||
.value_name("TYPE")
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clap::ArgMatches;
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
mod base32;
|
||||
pub mod base32;
|
||||
mod base64;
|
||||
mod basename;
|
||||
mod bootstrap;
|
||||
@ -68,6 +68,7 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
|
||||
"basename" => Some(Box::new(basename::Basename::default())),
|
||||
"bootstrap" => Some(Box::new(bootstrap::Bootstrap::default())),
|
||||
"chmod" => Some(Box::new(chmod::Chmod::default())),
|
||||
"chgrp" => Some(Box::new(chown::Chgrp::default())),
|
||||
"chown" => Some(Box::new(chown::Chown::default())),
|
||||
"clear" => Some(Box::new(clear::Clear::default())),
|
||||
"cut" => Some(Box::new(cut::Cut::default())),
|
||||
@ -100,12 +101,13 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
|
||||
}
|
||||
}
|
||||
|
||||
pub static COMMANDS: [&str; 34] = [
|
||||
pub static COMMANDS: [&str; 35] = [
|
||||
"base32",
|
||||
"base64",
|
||||
"basename",
|
||||
"bootstrap",
|
||||
"chmod",
|
||||
"chgrp",
|
||||
"chown",
|
||||
"clear",
|
||||
"cut",
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::Cmd;
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use crate::args;
|
||||
use clap::{Arg, Command};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufRead, BufReader, ErrorKind, Write},
|
||||
@ -15,15 +16,8 @@ impl Cmd for Rev {
|
||||
.about("reverse lines characterwise")
|
||||
.author("Nathan Fisher")
|
||||
.args([
|
||||
Arg::new("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.help("print a header between each file")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("color")
|
||||
.short('c')
|
||||
.long("color")
|
||||
.value_parser(["always", "ansi", "auto", "never"]),
|
||||
args::header(),
|
||||
args::color(),
|
||||
Arg::new("file")
|
||||
.help("if file is '-' read from stdin")
|
||||
.num_args(0..),
|
||||
@ -51,7 +45,7 @@ impl Cmd for Rev {
|
||||
_ => ColorChoice::Never,
|
||||
};
|
||||
for (index, file) in files.into_iter().enumerate() {
|
||||
if matches.get_flag("verbose") {
|
||||
if matches.get_flag("header") {
|
||||
let mut stdout = StandardStream::stdout(color);
|
||||
stdout.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?;
|
||||
match index {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::Cmd;
|
||||
use crate::fs::FileType;
|
||||
use crate::{args, fs::FileType};
|
||||
use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command, ValueHint};
|
||||
use std::{
|
||||
error::Error,
|
||||
@ -46,17 +46,8 @@ impl Cmd for Rm {
|
||||
.long("force")
|
||||
.help("ignore nonexistent files and arguments, never prompt")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("recursive")
|
||||
.short('r')
|
||||
.long("recursive")
|
||||
.visible_short_alias('R')
|
||||
.help("remove directories and their contents recursively")
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.help("explain what is being done")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::recursive(),
|
||||
args::verbose(),
|
||||
Arg::new("file")
|
||||
.value_name("FILE")
|
||||
.value_hint(ValueHint::AnyPath)
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::Cmd;
|
||||
use crate::args;
|
||||
use clap::{Arg, ArgAction, Command, ValueHint};
|
||||
use std::{error::Error, fs, io, path::Path};
|
||||
|
||||
@ -22,11 +23,7 @@ impl Cmd for Rmdir {
|
||||
.value_name("DIRECTORY")
|
||||
.value_hint(ValueHint::DirPath)
|
||||
.required(true),
|
||||
Arg::new("verbose")
|
||||
.help("output a diagnostic for every directory processed")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::verbose(),
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::Cmd;
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use crate::args;
|
||||
use clap::{Arg, Command};
|
||||
use std::{ffi::CString, io, process};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -12,11 +13,7 @@ impl Cmd for Unlink {
|
||||
.author("Nathan Fisher")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.args([
|
||||
Arg::new("verbose")
|
||||
.help("display user feedback upon success")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
args::verbose(),
|
||||
Arg::new("file")
|
||||
.value_name("FILE")
|
||||
.required(true)
|
||||
|
@ -1,13 +1,15 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
use std::{env, path::PathBuf, process, string::ToString};
|
||||
|
||||
pub mod args;
|
||||
mod cmd;
|
||||
pub use cmd::Cmd;
|
||||
pub mod fs;
|
||||
pub mod math;
|
||||
pub mod mode;
|
||||
pub mod pw;
|
||||
|
||||
pub use cmd::Cmd;
|
||||
|
||||
/// Defines the location relative to the binary where a command will be installed
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Path {
|
||||
|
Loading…
Reference in New Issue
Block a user