1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

cp: accept shortcuts for stringly-enum arguments

This commit is contained in:
Ben Wiederhake 2024-04-01 06:00:15 +02:00
parent bfc7411dec
commit d4546ced26
3 changed files with 88 additions and 63 deletions

View file

@ -37,8 +37,8 @@ use uucore::{backup_control, update_control};
// requires these enum. // requires these enum.
pub use uucore::{backup_control::BackupMode, update_control::UpdateMode}; pub use uucore::{backup_control::BackupMode, update_control::UpdateMode};
use uucore::{ use uucore::{
format_usage, help_about, help_section, help_usage, prompt_yes, show_error, show_warning, format_usage, help_about, help_section, help_usage, prompt_yes,
util_name, shortcut_value_parser::ShortcutValueParser, show_error, show_warning, util_name,
}; };
use crate::copydir::copy_directory; use crate::copydir::copy_directory;
@ -396,22 +396,14 @@ static PRESERVABLE_ATTRIBUTES: &[&str] = &[
"ownership", "ownership",
"timestamps", "timestamps",
"context", "context",
"link",
"links", "links",
"xattr", "xattr",
"all", "all",
]; ];
#[cfg(not(unix))] #[cfg(not(unix))]
static PRESERVABLE_ATTRIBUTES: &[&str] = &[ static PRESERVABLE_ATTRIBUTES: &[&str] =
"mode", &["mode", "timestamps", "context", "links", "xattr", "all"];
"timestamps",
"context",
"link",
"links",
"xattr",
"all",
];
pub fn uu_app() -> Command { pub fn uu_app() -> Command {
const MODE_ARGS: &[&str] = &[ const MODE_ARGS: &[&str] = &[
@ -543,7 +535,7 @@ pub fn uu_app() -> Command {
.overrides_with_all(MODE_ARGS) .overrides_with_all(MODE_ARGS)
.require_equals(true) .require_equals(true)
.default_missing_value("always") .default_missing_value("always")
.value_parser(["auto", "always", "never"]) .value_parser(ShortcutValueParser::new(["auto", "always", "never"]))
.num_args(0..=1) .num_args(0..=1)
.help("control clone/CoW copies. See below"), .help("control clone/CoW copies. See below"),
) )
@ -559,9 +551,7 @@ pub fn uu_app() -> Command {
.long(options::PRESERVE) .long(options::PRESERVE)
.action(ArgAction::Append) .action(ArgAction::Append)
.use_value_delimiter(true) .use_value_delimiter(true)
.value_parser(clap::builder::PossibleValuesParser::new( .value_parser(ShortcutValueParser::new(PRESERVABLE_ATTRIBUTES))
PRESERVABLE_ATTRIBUTES,
))
.num_args(0..) .num_args(0..)
.require_equals(true) .require_equals(true)
.value_name("ATTR_LIST") .value_name("ATTR_LIST")
@ -655,7 +645,7 @@ pub fn uu_app() -> Command {
Arg::new(options::SPARSE) Arg::new(options::SPARSE)
.long(options::SPARSE) .long(options::SPARSE)
.value_name("WHEN") .value_name("WHEN")
.value_parser(["never", "auto", "always"]) .value_parser(ShortcutValueParser::new(["never", "auto", "always"]))
.help("control creation of sparse files. See below"), .help("control creation of sparse files. See below"),
) )
// TODO: implement the following args // TODO: implement the following args

View file

@ -61,6 +61,7 @@ pub enum UpdateMode {
} }
pub mod arguments { pub mod arguments {
use crate::shortcut_value_parser::ShortcutValueParser;
use clap::ArgAction; use clap::ArgAction;
pub static OPT_UPDATE: &str = "update"; pub static OPT_UPDATE: &str = "update";
@ -71,7 +72,7 @@ pub mod arguments {
clap::Arg::new(OPT_UPDATE) clap::Arg::new(OPT_UPDATE)
.long("update") .long("update")
.help("move only when the SOURCE file is newer than the destination file or when the destination file is missing") .help("move only when the SOURCE file is newer than the destination file or when the destination file is missing")
.value_parser(["none", "all", "older"]) .value_parser(ShortcutValueParser::new(["none", "all", "older"]))
.num_args(0..=1) .num_args(0..=1)
.default_missing_value("older") .default_missing_value("older")
.require_equals(true) .require_equals(true)

View file

@ -2,7 +2,7 @@
// //
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile uufs xattrs // spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs neve ROOTDIR USERDIR procfs outfile uufs xattrs
use crate::common::util::TestScenario; use crate::common::util::TestScenario;
#[cfg(not(windows))] #[cfg(not(windows))]
@ -286,17 +286,19 @@ fn test_cp_arg_update_interactive_error() {
#[test] #[test]
fn test_cp_arg_update_none() { fn test_cp_arg_update_none() {
for argument in ["--update=none", "--update=non", "--update=n"] {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg(TEST_HELLO_WORLD_SOURCE) ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.arg("--update=none") .arg(argument)
.succeeds() .succeeds()
.no_stderr() .no_stderr()
.no_stdout(); .no_stdout();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
} }
}
#[test] #[test]
fn test_cp_arg_update_all() { fn test_cp_arg_update_all() {
@ -1402,6 +1404,7 @@ fn test_cp_preserve_no_args_before_opts() {
#[test] #[test]
fn test_cp_preserve_all() { fn test_cp_preserve_all() {
for argument in ["--preserve=all", "--preserve=al"] {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let src_file = "a"; let src_file = "a";
let dst_file = "b"; let dst_file = "b";
@ -1413,10 +1416,7 @@ fn test_cp_preserve_all() {
// TODO: create a destination that does not allow copying of xattr and context // TODO: create a destination that does not allow copying of xattr and context
// Copy // Copy
ucmd.arg(src_file) ucmd.arg(src_file).arg(dst_file).arg(argument).succeeds();
.arg(dst_file)
.arg("--preserve=all")
.succeeds();
#[cfg(all(unix, not(target_os = "freebsd")))] #[cfg(all(unix, not(target_os = "freebsd")))]
{ {
@ -1427,6 +1427,7 @@ fn test_cp_preserve_all() {
assert_metadata_eq!(metadata_src, metadata_dst); assert_metadata_eq!(metadata_src, metadata_dst);
} }
} }
}
#[test] #[test]
#[cfg(all(unix, not(target_os = "android")))] #[cfg(all(unix, not(target_os = "android")))]
@ -1472,6 +1473,35 @@ fn test_cp_preserve_all_context_fails_on_non_selinux() {
.fails(); .fails();
} }
#[test]
fn test_cp_preserve_link_parses() {
// TODO: Also check whether --preserve=link did the right thing!
for argument in [
"--preserve=links",
"--preserve=link",
"--preserve=li",
"--preserve=l",
] {
new_ucmd!()
.arg(argument)
.arg(TEST_COPY_FROM_FOLDER_FILE)
.arg(TEST_HELLO_WORLD_DEST)
.succeeds()
.no_output();
}
}
#[test]
fn test_cp_preserve_invalid_rejected() {
new_ucmd!()
.arg("--preserve=invalid-value")
.arg(TEST_COPY_FROM_FOLDER_FILE)
.arg(TEST_HELLO_WORLD_DEST)
.fails()
.code_is(1)
.no_stdout();
}
#[test] #[test]
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
#[cfg(disabled_until_fixed)] // FIXME: the test looks to .succeed on android #[cfg(disabled_until_fixed)] // FIXME: the test looks to .succeed on android
@ -2196,8 +2226,9 @@ fn test_cp_reflink_none() {
#[test] #[test]
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
fn test_cp_reflink_never() { fn test_cp_reflink_never() {
for argument in ["--reflink=never", "--reflink=neve", "--reflink=n"] {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--reflink=never") ucmd.arg(argument)
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_EXISTING_FILE) .arg(TEST_EXISTING_FILE)
.succeeds(); .succeeds();
@ -2205,6 +2236,7 @@ fn test_cp_reflink_never() {
// Check the content of the destination file // Check the content of the destination file
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n"); assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
} }
}
#[test] #[test]
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
@ -2286,6 +2318,7 @@ fn test_cp_sparse_never_empty() {
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
#[test] #[test]
fn test_cp_sparse_always_empty() { fn test_cp_sparse_always_empty() {
for argument in ["--sparse=always", "--sparse=alway", "--sparse=al"] {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
const BUFFER_SIZE: usize = 4096 * 4; const BUFFER_SIZE: usize = 4096 * 4;
@ -2294,12 +2327,13 @@ fn test_cp_sparse_always_empty() {
at.make_file("src_file1"); at.make_file("src_file1");
at.write_bytes("src_file1", &buf); at.write_bytes("src_file1", &buf);
ucmd.args(&["--sparse=always", "src_file1", "dst_file_sparse"]) ucmd.args(&[argument, "src_file1", "dst_file_sparse"])
.succeeds(); .succeeds();
assert_eq!(at.read_bytes("dst_file_sparse"), buf); assert_eq!(at.read_bytes("dst_file_sparse"), buf);
assert_eq!(at.metadata("dst_file_sparse").blocks(), 0); assert_eq!(at.metadata("dst_file_sparse").blocks(), 0);
} }
}
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
#[test] #[test]