diff options
Diffstat (limited to 'util')
-rw-r--r-- | util/src/fs.rs | 56 | ||||
-rw-r--r-- | util/src/lib.rs | 59 |
2 files changed, 57 insertions, 58 deletions
diff --git a/util/src/fs.rs b/util/src/fs.rs new file mode 100644 index 0000000..6a7a5b4 --- /dev/null +++ b/util/src/fs.rs @@ -0,0 +1,56 @@ +use std::io::{self, Result}; +use std::path::{Path, PathBuf}; +use rand::{Rng, distributions::Alphanumeric}; +use tokio::fs; + +/// Create a temporary file named `temp.[a-zA-Z0-9]{length}` in +/// the given location and immediately open it. Returns the +/// filename and the corresponding file handle. It is the caller's +/// responsibility to clean up the temporary file when it is no +/// longer needed. +/// +/// Uses [`OpenOptions::create_new`][fs::OpenOptions::create_new] +/// to detect filename collisions, in which case it will +/// automatically retry until the operation succeeds. +/// +/// # Errors +/// +/// Returns the underlying [`io::Error`] if the operation fails +/// due to reasons other than filename collision. +pub async fn mktemp<T, B>(dir: T, basename: B, length: usize) -> Result<(PathBuf, fs::File)> +where + T: AsRef<Path>, + B: Into<Option<&'static str>> +{ + let dir = dir.as_ref(); + let basename = basename.into().unwrap_or(""); + fs::create_dir_all(dir).await?; + + loop { + let filename = dir.join(format!( + "{}{}{}", + basename, + if basename.is_empty() { "" } else { "." }, + { + let string = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(length) + .collect::<Vec<u8>>(); + String::from_utf8(string).unwrap() + } + )); + + match fs::OpenOptions::new() + .create_new(true) + .write(true) + .open(&filename) + .await + { + Ok(file) => return Ok((filename, file)), + Err(err) => match err.kind() { + io::ErrorKind::AlreadyExists => continue, + _ => return Err(err) + } + } + } +} diff --git a/util/src/lib.rs b/util/src/lib.rs index c49bdf5..c840e59 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -63,61 +63,4 @@ pub mod queue; #[cfg(feature = "fs")] /// Commonly-used operations with the file system in Kittybox's /// underlying storage mechanisms. -pub mod fs { - use std::io::{self, Result}; - use std::path::{Path, PathBuf}; - use rand::{Rng, distributions::Alphanumeric}; - use tokio::fs; - - /// Create a temporary file named `temp.[a-zA-Z0-9]{length}` in - /// the given location and immediately open it. Returns the - /// filename and the corresponding file handle. It is the caller's - /// responsibility to clean up the temporary file when it is no - /// longer needed. - /// - /// Uses [`OpenOptions::create_new`][fs::OpenOptions::create_new] - /// to detect filename collisions, in which case it will - /// automatically retry until the operation succeeds. - /// - /// # Errors - /// - /// Returns the underlying [`io::Error`] if the operation fails - /// due to reasons other than filename collision. - pub async fn mktemp<T, B>(dir: T, basename: B, length: usize) -> Result<(PathBuf, fs::File)> - where - T: AsRef<Path>, - B: Into<Option<&'static str>> - { - let dir = dir.as_ref(); - let basename = basename.into().unwrap_or(""); - fs::create_dir_all(dir).await?; - - loop { - let filename = dir.join(format!( - "{}{}{}", - basename, - if basename.is_empty() { "" } else { "." }, - { - let string = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(length) - .collect::<Vec<u8>>(); - String::from_utf8(string).unwrap() - } - )); - - match fs::OpenOptions::new() - .create_new(true) - .write(true) - .open(&filename) - .await - { - Ok(file) => return Ok((filename, file)), - Err(err) => match err.kind() { - io::ErrorKind::AlreadyExists => continue, - _ => return Err(err) - } - } - } - } -} +pub mod fs; |