diff --git a/Cargo.lock b/Cargo.lock index 9c7e3d3..83ce25c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,6 +232,17 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.22.0" @@ -453,6 +464,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "ppv-lite86" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" + [[package]] name = "pretty_env_logger" version = "0.4.0" @@ -499,6 +516,47 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + [[package]] name = "redox_syscall" version = "0.1.57" @@ -560,10 +618,12 @@ dependencies = [ "cfg-if", "color-eyre", "futures", + "lazy_static", "log", "pretty_env_logger", "rustc_version", "tokio", + "uuid", ] [[package]] @@ -664,6 +724,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "winapi" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index 12c178f..9b5cecc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,8 @@ log = "0.4.11" pretty_env_logger = "0.4.0" color-eyre = {version = "0.5.6", default-features=false} futures = {version = "0.3.5", optional = true} +lazy_static = "1.4.0" +uuid = {version = "0.8.1", features = ["v4"]} [build-dependencies] rustc_version = "0.2" diff --git a/src/error.rs b/src/error.rs index d5d1045..4db3dd4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,6 +12,8 @@ pub enum ErrorKind Stat(io::Error), Open(io::Error), Copy(io::Error), + Unlink(io::Error), + Move(io::Error), } impl ErrorKind @@ -75,6 +77,8 @@ impl std::error::Error for Error ErrorKind::Stat(io) => io, ErrorKind::Open(io) => io, ErrorKind::Copy(io) => io, + ErrorKind::Unlink(io) => io, + ErrorKind::Move(io) => io, // _ => return None, }) } @@ -86,7 +90,9 @@ impl std::fmt::Display for Error match &self.kind { ErrorKind::Stat(_) => write!(f, "Failed to stat file {:?}", self.path), ErrorKind::Open(_) => write!(f, "Failed to open file {:?}", self.path), - ErrorKind::Copy(_) => write!(f, "Failed to create copy of file {:?}", self.path), + ErrorKind::Copy(_) => write!(f, "Failed to create copy of file to temp location {:?}", self.path), + ErrorKind::Unlink(_) => write!(f, "Failed to unlink file {:?}", self.path), + ErrorKind::Move(_) => write!(f, "Failed to move new file into place {:?}", self.path), } } } diff --git a/src/main.rs b/src/main.rs index 23856f1..ccfc34a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use color_eyre::{ eyre::{self, eyre, WrapErr}, Help, SectionExt, }; +use lazy_static::lazy_static; fn init() -> eyre::Result<()> { @@ -16,6 +17,7 @@ fn init() -> eyre::Result<()> Ok(()) } +mod temp; mod error; #[cfg(feature="parallel")] diff --git a/src/parallel.rs b/src/parallel.rs index f4a50e3..b0317e9 100644 --- a/src/parallel.rs +++ b/src/parallel.rs @@ -36,17 +36,13 @@ fn gensem() -> Option> } } -fn tpath() -> String -{ - todo!(); - ".test.tmp".to_owned() -} - async fn unlink(path: &Path) -> Result<(), Error> { - let tmp = path.parent().unwrap().join(tpath()); - fs::copy(path, tmp).await.map_err(|e| Error::new(ErrorKind::Copy(e), path.to_owned()))?; - todo!(); + let tmp = temp::TempFile::new_in(path.parent().unwrap()); + fs::copy(path, &tmp).await.map_err(|e| Error::new(ErrorKind::Copy(e), path.to_owned()))?; + fs::remove_file(path).await.map_err(|e| Error::new(ErrorKind::Unlink(e), path.to_owned()))?; + fs::rename(&tmp, path).await.map_err(|e| Error::new(ErrorKind::Move(e), path.to_owned()))?; + tmp.release(); // file no longer exists, so no need to drop; Ok(()) } diff --git a/src/temp.rs b/src/temp.rs new file mode 100644 index 0000000..40ebe07 --- /dev/null +++ b/src/temp.rs @@ -0,0 +1,82 @@ +//! Temp file +use super::*; +use std::{ + ops::Drop, + path::{ + Path, + PathBuf, + }, +}; + +lazy_static! { + static ref DEFAULT_LOCATION: PathBuf = std::env::temp_dir(); +} + +fn genname() -> String +{ + uuid::Uuid::new_v4().to_string() +} + +#[derive(Debug)] +pub struct TempFile(Option); + +impl TempFile +{ + /// Create a new instance in default temp location + pub fn new() -> Self + { + Self::new_in(&*DEFAULT_LOCATION) + } + /// Create a new instance with random name in this location + pub fn new_in>(dir: P) -> Self + { + Self::new_path(dir.as_ref().join(genname())) + } + /// Create a new instance from a specific path + pub fn new_path>(path: P) -> Self + { + let path = path.into(); + trace!("Creating temp owned path {:?}", path); + Self(Some(path)) + } + + /// The internal path + pub fn path(&self) -> &Path + { + (&self.0).as_ref().unwrap() + } + + /// Release ownership of the path, not deleting the file if it exists + pub fn release(mut self) -> PathBuf + { + let d = self.0.take().unwrap(); + std::mem::forget(self); + d + } + + /// Attempt to remove this temp file async + pub async fn drop_async(mut self) -> tokio::io::Result<()> + { + let res = tokio::fs::remove_file(self.0.take().unwrap()).await; + std::mem::forget(self); + res + } +} + +impl AsRef for TempFile +{ + #[inline] fn as_ref(&self) -> &Path + { + self.path() + } +} + +impl Drop for TempFile +{ + fn drop(&mut self) + { + if let Err(e) = std::fs::remove_file(self.0.take().unwrap()) { + debug!("Failed to remove owned temp file (sync, drop): {}", e); + } + } +}