mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
pinky: move from getopts to clap (#2123)
This commit is contained in:
parent
b89978a4c9
commit
798a033311
3 changed files with 132 additions and 63 deletions
|
@ -17,6 +17,7 @@ path = "src/pinky.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["utmpx", "entries"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["utmpx", "entries"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
clap = "2.33.3"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "pinky"
|
name = "pinky"
|
||||||
|
|
|
@ -19,67 +19,110 @@ use std::io::BufReader;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use uucore::InvalidEncodingHandling;
|
use uucore::InvalidEncodingHandling;
|
||||||
|
|
||||||
static SYNTAX: &str = "[OPTION]... [USER]...";
|
|
||||||
static SUMMARY: &str = "A lightweight 'finger' program; print user information.";
|
|
||||||
|
|
||||||
const BUFSIZE: usize = 1024;
|
const BUFSIZE: usize = 1024;
|
||||||
|
|
||||||
|
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
static ABOUT: &str = "pinky - lightweight finger";
|
||||||
|
|
||||||
|
mod options {
|
||||||
|
pub const LONG_FORMAT: &str = "long_format";
|
||||||
|
pub const OMIT_HOME_DIR: &str = "omit_home_dir";
|
||||||
|
pub const OMIT_PROJECT_FILE: &str = "omit_project_file";
|
||||||
|
pub const OMIT_PLAN_FILE: &str = "omit_plan_file";
|
||||||
|
pub const SHORT_FORMAT: &str = "short_format";
|
||||||
|
pub const OMIT_HEADINGS: &str = "omit_headings";
|
||||||
|
pub const OMIT_NAME: &str = "omit_name";
|
||||||
|
pub const OMIT_NAME_HOST: &str = "omit_name_host";
|
||||||
|
pub const OMIT_NAME_HOST_TIME: &str = "omit_name_host_time";
|
||||||
|
pub const USER: &str = "user";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_usage() -> String {
|
||||||
|
format!("{0} [OPTION]... [USER]...", executable!())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_long_usage() -> String {
|
||||||
|
format!(
|
||||||
|
"A lightweight 'finger' program; print user information.\n\
|
||||||
|
The utmp file will be {}.",
|
||||||
|
utmpx::DEFAULT_FILE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
let args = args
|
let args = args
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let long_help = &format!(
|
let usage = get_usage();
|
||||||
"
|
let after_help = get_long_usage();
|
||||||
-l produce long format output for the specified USERs
|
|
||||||
-b omit the user's home directory and shell in long format
|
|
||||||
-h omit the user's project file in long format
|
|
||||||
-p omit the user's plan file in long format
|
|
||||||
-s do short format output, this is the default
|
|
||||||
-f omit the line of column headings in short format
|
|
||||||
-w omit the user's full name in short format
|
|
||||||
-i omit the user's full name and remote host in short format
|
|
||||||
-q omit the user's full name, remote host and idle time
|
|
||||||
in short format
|
|
||||||
--help display this help and exit
|
|
||||||
--version output version information and exit
|
|
||||||
|
|
||||||
The utmp file will be {}",
|
let matches = App::new(executable!())
|
||||||
utmpx::DEFAULT_FILE
|
.version(VERSION)
|
||||||
);
|
.about(ABOUT)
|
||||||
let mut opts = app!(SYNTAX, SUMMARY, &long_help);
|
.usage(&usage[..])
|
||||||
opts.optflag(
|
.after_help(&after_help[..])
|
||||||
"l",
|
.arg(
|
||||||
"",
|
Arg::with_name(options::LONG_FORMAT)
|
||||||
"produce long format output for the specified USERs",
|
.short("l")
|
||||||
);
|
.requires(options::USER)
|
||||||
opts.optflag(
|
.help("produce long format output for the specified USERs"),
|
||||||
"b",
|
)
|
||||||
"",
|
.arg(
|
||||||
"omit the user's home directory and shell in long format",
|
Arg::with_name(options::OMIT_HOME_DIR)
|
||||||
);
|
.short("b")
|
||||||
opts.optflag("h", "", "omit the user's project file in long format");
|
.help("omit the user's home directory and shell in long format"),
|
||||||
opts.optflag("p", "", "omit the user's plan file in long format");
|
)
|
||||||
opts.optflag("s", "", "do short format output, this is the default");
|
.arg(
|
||||||
opts.optflag("f", "", "omit the line of column headings in short format");
|
Arg::with_name(options::OMIT_PROJECT_FILE)
|
||||||
opts.optflag("w", "", "omit the user's full name in short format");
|
.short("h")
|
||||||
opts.optflag(
|
.help("omit the user's project file in long format"),
|
||||||
"i",
|
)
|
||||||
"",
|
.arg(
|
||||||
"omit the user's full name and remote host in short format",
|
Arg::with_name(options::OMIT_PLAN_FILE)
|
||||||
);
|
.short("p")
|
||||||
opts.optflag(
|
.help("omit the user's plan file in long format"),
|
||||||
"q",
|
)
|
||||||
"",
|
.arg(
|
||||||
"omit the user's full name, remote host and idle time in short format",
|
Arg::with_name(options::SHORT_FORMAT)
|
||||||
);
|
.short("s")
|
||||||
opts.optflag("", "help", "display this help and exit");
|
.help("do short format output, this is the default"),
|
||||||
opts.optflag("", "version", "output version information and exit");
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OMIT_HEADINGS)
|
||||||
|
.short("f")
|
||||||
|
.help("omit the line of column headings in short format"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OMIT_NAME)
|
||||||
|
.short("w")
|
||||||
|
.help("omit the user's full name in short format"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OMIT_NAME_HOST)
|
||||||
|
.short("i")
|
||||||
|
.help("omit the user's full name and remote host in short format"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::OMIT_NAME_HOST_TIME)
|
||||||
|
.short("q")
|
||||||
|
.help("omit the user's full name, remote host and idle time in short format"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(options::USER)
|
||||||
|
.takes_value(true)
|
||||||
|
.multiple(true),
|
||||||
|
)
|
||||||
|
.get_matches_from(args);
|
||||||
|
|
||||||
let matches = opts.parse(args);
|
let users: Vec<String> = matches
|
||||||
|
.values_of(options::USER)
|
||||||
|
.map(|v| v.map(ToString::to_string).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
// If true, display the hours:minutes since each user has touched
|
// If true, display the hours:minutes since each user has touched
|
||||||
// the keyboard, or blank if within the last minute, or days followed
|
// the keyboard, or blank if within the last minute, or days followed
|
||||||
|
@ -87,45 +130,40 @@ The utmp file will be {}",
|
||||||
let mut include_idle = true;
|
let mut include_idle = true;
|
||||||
|
|
||||||
// If true, display a line at the top describing each field.
|
// If true, display a line at the top describing each field.
|
||||||
let include_heading = !matches.opt_present("f");
|
let include_heading = !matches.is_present(options::OMIT_HEADINGS);
|
||||||
|
|
||||||
// if true, display the user's full name from pw_gecos.
|
// if true, display the user's full name from pw_gecos.
|
||||||
let mut include_fullname = true;
|
let mut include_fullname = true;
|
||||||
|
|
||||||
// if true, display the user's ~/.project file when doing long format.
|
// if true, display the user's ~/.project file when doing long format.
|
||||||
let include_project = !matches.opt_present("h");
|
let include_project = !matches.is_present(options::OMIT_PROJECT_FILE);
|
||||||
|
|
||||||
// if true, display the user's ~/.plan file when doing long format.
|
// if true, display the user's ~/.plan file when doing long format.
|
||||||
let include_plan = !matches.opt_present("p");
|
let include_plan = !matches.is_present(options::OMIT_PLAN_FILE);
|
||||||
|
|
||||||
// if true, display the user's home directory and shell
|
// if true, display the user's home directory and shell
|
||||||
// when doing long format.
|
// when doing long format.
|
||||||
let include_home_and_shell = !matches.opt_present("b");
|
let include_home_and_shell = !matches.is_present(options::OMIT_HOME_DIR);
|
||||||
|
|
||||||
// if true, use the "short" output format.
|
// if true, use the "short" output format.
|
||||||
let do_short_format = !matches.opt_present("l");
|
let do_short_format = !matches.is_present(options::LONG_FORMAT);
|
||||||
|
|
||||||
/* if true, display the ut_host field. */
|
/* if true, display the ut_host field. */
|
||||||
let mut include_where = true;
|
let mut include_where = true;
|
||||||
|
|
||||||
if matches.opt_present("w") {
|
if matches.is_present(options::OMIT_NAME) {
|
||||||
include_fullname = false;
|
include_fullname = false;
|
||||||
}
|
}
|
||||||
if matches.opt_present("i") {
|
if matches.is_present(options::OMIT_NAME_HOST) {
|
||||||
include_fullname = false;
|
include_fullname = false;
|
||||||
include_where = false;
|
include_where = false;
|
||||||
}
|
}
|
||||||
if matches.opt_present("q") {
|
if matches.is_present(options::OMIT_NAME_HOST_TIME) {
|
||||||
include_fullname = false;
|
include_fullname = false;
|
||||||
include_idle = false;
|
include_idle = false;
|
||||||
include_where = false;
|
include_where = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !do_short_format && matches.free.is_empty() {
|
|
||||||
show_usage_error!("no username specified; at least one must be specified when using -l");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pk = Pinky {
|
let pk = Pinky {
|
||||||
include_idle,
|
include_idle,
|
||||||
include_heading,
|
include_heading,
|
||||||
|
@ -134,7 +172,7 @@ The utmp file will be {}",
|
||||||
include_plan,
|
include_plan,
|
||||||
include_home_and_shell,
|
include_home_and_shell,
|
||||||
include_where,
|
include_where,
|
||||||
names: matches.free,
|
names: users,
|
||||||
};
|
};
|
||||||
|
|
||||||
if do_short_format {
|
if do_short_format {
|
||||||
|
|
|
@ -34,6 +34,36 @@ fn test_long_format() {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[test]
|
||||||
|
fn test_long_format_multiple_users() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
|
||||||
|
let expected = scene
|
||||||
|
.cmd_keepenv(util_name!())
|
||||||
|
.env("LANGUAGE", "C")
|
||||||
|
.arg("-l")
|
||||||
|
.arg("root")
|
||||||
|
.arg("root")
|
||||||
|
.arg("root")
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("-l")
|
||||||
|
.arg("root")
|
||||||
|
.arg("root")
|
||||||
|
.arg("root")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is(expected.stdout_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_long_format_wo_user() {
|
||||||
|
// "no username specified; at least one must be specified when using -l"
|
||||||
|
new_ucmd!().arg("-l").fails().code_is(1);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_short_format_i() {
|
fn test_short_format_i() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue