use anyhow::{bail, Result}; use std::{ fs::{create_dir_all, remove_file, rename, File}, path::{PathBuf, MAIN_SEPARATOR}, thread, time::{Duration, SystemTime, UNIX_EPOCH}, }; const TMP_SUFFIX: &str = ".tmp"; pub struct Item { pub file: File, pub path: PathBuf, // pub id: u64, } impl Item { // Constructors pub fn create(directory: &str) -> Result { loop { // generate file id from current unix time let id = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); // build optimized fs path: // add directory separator after every digit to keep up to 10 files per directory let path = PathBuf::from(format!( "{}{MAIN_SEPARATOR}{}{TMP_SUFFIX}", directory.trim_end_matches(MAIN_SEPARATOR), id.to_string().chars().fold(String::new(), |mut acc, c| { if !acc.is_empty() { acc.push(MAIN_SEPARATOR); } acc.push(c); acc }) )); // recursively create directories // * parent directory is expected create_dir_all(path.parent().unwrap())?; // build `Self` match File::create_new(&path) { // make sure slot is not taken (by another thread) Ok(file) => { return Ok(Self { file, path, // id }); } Err(_) => { println!("[warning] Could not init location: {:?}", path.to_str()); // find free slot after some delay.. thread::sleep(Duration::from_secs(1)); continue; } } } } // Actions /// Take object processed, commit its changes pub fn commit(self) -> Result { match self.path.to_str() { Some(old) => match old.strip_suffix(TMP_SUFFIX) { Some(new) => { rename(old, new)?; Ok(new.to_string()) } None => bail!("Invalid TMP suffix"), // | panic }, None => bail!("Invalid Item path"), // | panic } } /// Cleanup object relationships pub fn delete(self) -> Result<()> { Ok(remove_file(self.path)?) } // Getters /* @TODO implement short links handle without slash pub fn alias(&self, relative: &str) -> String {} */ }