mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
cp: accept shortcuts for stringly-enum arguments
This commit is contained in:
parent
bfc7411dec
commit
d4546ced26
3 changed files with 88 additions and 63 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,16 +286,18 @@ fn test_cp_arg_update_interactive_error() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cp_arg_update_none() {
|
fn test_cp_arg_update_none() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
for argument in ["--update=none", "--update=non", "--update=n"] {
|
||||||
|
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]
|
||||||
|
@ -1402,29 +1404,28 @@ fn test_cp_preserve_no_args_before_opts() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cp_preserve_all() {
|
fn test_cp_preserve_all() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
for argument in ["--preserve=all", "--preserve=al"] {
|
||||||
let src_file = "a";
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
let dst_file = "b";
|
let src_file = "a";
|
||||||
|
let dst_file = "b";
|
||||||
|
|
||||||
// Prepare the source file
|
// Prepare the source file
|
||||||
at.touch(src_file);
|
at.touch(src_file);
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
at.set_mode(src_file, 0o0500);
|
at.set_mode(src_file, 0o0500);
|
||||||
|
|
||||||
// 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")))]
|
||||||
{
|
{
|
||||||
// Assert that the mode, ownership, and timestamps are preserved
|
// Assert that the mode, ownership, and timestamps are preserved
|
||||||
// NOTICE: the ownership is not modified on the src file, because that requires root permissions
|
// NOTICE: the ownership is not modified on the src file, because that requires root permissions
|
||||||
let metadata_src = at.metadata(src_file);
|
let metadata_src = at.metadata(src_file);
|
||||||
let metadata_dst = at.metadata(dst_file);
|
let metadata_dst = at.metadata(dst_file);
|
||||||
assert_metadata_eq!(metadata_src, metadata_dst);
|
assert_metadata_eq!(metadata_src, metadata_dst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,14 +2226,16 @@ 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() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
for argument in ["--reflink=never", "--reflink=neve", "--reflink=n"] {
|
||||||
ucmd.arg("--reflink=never")
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
.arg(TEST_HELLO_WORLD_SOURCE)
|
ucmd.arg(argument)
|
||||||
.arg(TEST_EXISTING_FILE)
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
||||||
.succeeds();
|
.arg(TEST_EXISTING_FILE)
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
// 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]
|
||||||
|
@ -2286,19 +2318,21 @@ 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() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
for argument in ["--sparse=always", "--sparse=alway", "--sparse=al"] {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = 4096 * 4;
|
const BUFFER_SIZE: usize = 4096 * 4;
|
||||||
let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||||
|
|
||||||
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"))]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue