1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-31 21:17:46 +00:00

feat: expose mv externals + document for nushell

This commit is contained in:
PThorpe92 2023-09-28 15:11:48 -04:00
parent 6091bafe08
commit 4a44a106a0
No known key found for this signature in database
GPG key ID: 66DB3FBACBDD05CC

View file

@ -19,11 +19,11 @@ use std::os::unix;
#[cfg(windows)] #[cfg(windows)]
use std::os::windows; use std::os::windows;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use uucore::backup_control::{self, source_is_target_backup, BackupMode}; pub use uucore::backup_control::BackupMode;
use uucore::backup_control::{self, source_is_target_backup};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError}; use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError, UUsageError};
use uucore::fs::{are_hardlinks_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file}; use uucore::fs::{are_hardlinks_or_one_way_symlink_to_same_file, are_hardlinks_to_same_file};
use uucore::libc::ENOTEMPTY;
use uucore::update_control::{self, UpdateMode}; use uucore::update_control::{self, UpdateMode};
use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show}; use uucore::{format_usage, help_about, help_section, help_usage, prompt_yes, show};
@ -33,12 +33,13 @@ use fs_extra::dir::{
}; };
use crate::error::MvError; use crate::error::MvError;
/// Options contains all the possible behaviors and flags for mv. /// Options contains all the possible behaviors and flags for mv.
/// ///
/// All options are public so that the options can be programmatically /// All options are public so that the options can be programmatically
/// constructed by other crates, such as nushell. That means that this struct /// constructed by other crates, such as nushell. That means that this struct is
/// is part of our public API. It should therefore not be changed without good /// part of our public API. It should therefore not be changed without good reason.
/// reason. ///
/// The fields are documented with the arguments that determine their value. /// The fields are documented with the arguments that determine their value.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct Options { pub struct Options {
@ -162,7 +163,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
progress_bar: matches.get_flag(OPT_PROGRESS), progress_bar: matches.get_flag(OPT_PROGRESS),
}; };
exec_mv(&files[..], &opts) mv(&files[..], &opts)
} }
pub fn uu_app() -> Command { pub fn uu_app() -> Command {
@ -254,7 +255,7 @@ pub fn uu_app() -> Command {
) )
} }
pub fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode { fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode {
// This does not exactly match the GNU implementation: // This does not exactly match the GNU implementation:
// The GNU mv defaults to Force, but if more than one of the // The GNU mv defaults to Force, but if more than one of the
// overwrite options are supplied, only the last takes effect. // overwrite options are supplied, only the last takes effect.
@ -358,12 +359,10 @@ fn handle_multiple_paths(paths: &[PathBuf], opts: &Options) -> UResult<()> {
move_files_into_dir(sources, target_dir, opts) move_files_into_dir(sources, target_dir, opts)
} }
/// Execute mv command, moving 'source' to 'target', where /// Execute the mv command. This moves 'source' to 'target', where
/// 'target' is a directory. If 'target' does not exist, and source is a single /// 'target' is a directory. If 'target' does not exist, and source is a single
/// file or directory, then 'source' will be renamed to 'target'. /// file or directory, then 'source' will be renamed to 'target'.
/// pub fn mv(files: &[OsString], opts: &Options) -> UResult<()> {
/// returns MvError | UError
pub fn exec_mv(files: &[OsString], opts: &Options) -> UResult<()> {
let paths = parse_paths(files, opts); let paths = parse_paths(files, opts);
if let Some(ref name) = opts.target_dir { if let Some(ref name) = opts.target_dir {
@ -440,31 +439,17 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, opts: &Options) ->
match rename(sourcepath, &targetpath, opts, multi_progress.as_ref()) { match rename(sourcepath, &targetpath, opts, multi_progress.as_ref()) {
Err(e) if e.to_string().is_empty() => set_exit_code(1), Err(e) if e.to_string().is_empty() => set_exit_code(1),
Err(e) => { Err(e) => {
match e.raw_os_error() { let e = e.map_err_context(|| {
Some(ENOTEMPTY) => { format!(
// The error message was changed to match GNU's decision "cannot move {} to {}",
// when an issue was filed. These will match when merged upstream. sourcepath.quote(),
let e = e targetpath.quote()
.map_err_context(|| format!("cannot overwrite {}", targetpath.quote())); )
match multi_progress { });
Some(ref pb) => pb.suspend(|| show!(e)), match multi_progress {
None => show!(e), Some(ref pb) => pb.suspend(|| show!(e)),
}; None => show!(e),
} };
_ => {
let e = e.map_err_context(|| {
format!(
"cannot move {} to {}",
sourcepath.quote(),
targetpath.quote()
)
});
match multi_progress {
Some(ref pb) => pb.suspend(|| show!(e)),
None => show!(e),
};
}
}
} }
Ok(()) => (), Ok(()) => (),
} }
@ -527,7 +512,7 @@ fn rename(
if is_empty_dir(to) { if is_empty_dir(to) {
fs::remove_dir(to)?; fs::remove_dir(to)?;
} else { } else {
return Err(io::Error::from_raw_os_error(ENOTEMPTY)); return Err(io::Error::new(io::ErrorKind::Other, "Directory not empty"));
} }
} }
} }