Skip to content

Instantly share code, notes, and snippets.

@ExpHP
Last active November 14, 2017 21:12
Show Gist options
  • Select an option

  • Save ExpHP/cae455e9f7292d7a53f03dec0c7a7d09 to your computer and use it in GitHub Desktop.

Select an option

Save ExpHP/cae455e9f7292d7a53f03dec0c7a7d09 to your computer and use it in GitHub Desktop.
/// Move a file or directory, possibly across filesystems.
///
/// Properties:
/// * Moves files or folders alike.
/// * The destination must not exist.
/// * The destination can be on a different filesystem.
/// * The operation is O(1) and preserves hard-links if
/// the destination is on the same filesystem.
/// * Symlinks retain their textual path targets.
/// * Permissions and attributes are carried over on a best
/// effort basis.
/// * aaaah why am I even documenting this
///
/// Alright, look. It calls `mv -nT`, okay?
/// *It literally calls `mv -nT`.*
///
/// (who ever knew so much complexity was secretly hiding in
/// /usr/bin/mv?)
pub fn mv<P, Q>(src: P, dest: Q) -> Result<()>
where
P: AsRef<OsStr>,
Q: AsRef<OsStr>,
{ Move::new().one(src, dest) }
/// Builder for configuring a `mv` command.
#[derive(Debug, Clone)]
pub struct Move {
clobber: bool,
magic_directory: bool,
}
impl Move {
/// The default settings are those that most closely match
/// the semantics of ::std::fs::rename.
pub fn new() -> Self
{ Move {
clobber: true,
magic_directory: false,
}}
/// Allow existing files to be replaced.
///
/// Default: true.
pub fn clobber(&mut self, value: bool) -> &mut Self
{ self.clobber = value; self }
/// When the destination exists and is a directory,
/// `one` will move *into* the directory instead.
///
/// The default setting is false, which corresponds to the
/// `-T`/`--no-target-directory` option.
pub fn magic_directory(&mut self, value: bool) -> &mut Self
{ self.magic_directory = value; self }
/// `mv src dest`
pub fn one<P, Q>(&self, src: P, dest: Q) -> Result<()>
where
P: AsRef<OsStr>,
Q: AsRef<OsStr>,
{Ok({
let mut cmd = Command::new("/usr/bin/mv");
if !self.magic_directory { cmd.arg("--no-target-directory"); }
if !self.clobber { cmd.arg("--no-clobber"); }
cmd.arg("--").arg(src).arg(dest);
Self::call_and_check(cmd)?;
})}
/// `mv -t dest src1 [src2...]`
pub fn many<P, Q, Qs>(&self, target: P, sources: Qs) -> Result<()>
where
P: AsRef<OsStr>,
Q: AsRef<OsStr>,
Qs: IntoIterator<Item=Q>,
{Ok({
let mut cmd = Command::new("/usr/bin/mv");
if !self.clobber { cmd.arg("--no-clobber"); }
cmd.arg("-t").arg(target).arg("--").args(sources);
Self::call_and_check(cmd)?;
})}
fn call_and_check(mut cmd: Command) -> Result<()>
{Ok({
let mut child = cmd
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()?;
let stderr = {
let mut stderr = child.stderr.take().unwrap();
let mut s = String::new();
stderr.read_to_string(&mut s)?;
s
};
let code = child.wait()?;
if !code.success() {
bail!("{:?}' failed with code {} and message: {}", cmd, code, stderr);
}
})}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment