1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
use std::collections::HashSet; use std::fs; use std::fs::File; use std::marker::PhantomData; use std::ops::Deref; use std::path::{Path, PathBuf}; use fs2::FileExt; use crate::prelude::*; const SOMA_DATA_DIR_ENV_NAME: &str = "SOMA_DATA_DIR"; const SOMA_DATA_DIR_NAME: &str = ".soma"; const LOCK_FILE_NAME: &str = "soma.lock"; pub struct DataDirectory { root_path: PathBuf, lock: File, manager_set: HashSet<&'static str>, } impl DataDirectory { pub fn new() -> SomaResult<Self> { let path = { if let Some(dir) = std::env::var_os(SOMA_DATA_DIR_ENV_NAME) { dir.into() } else { let mut home = dirs::home_dir().ok_or(SomaError::DataDirectoryAccessDenied)?; home.push(SOMA_DATA_DIR_NAME); home } }; if !path.exists() { fs::create_dir_all(&path)?; println!("Created Soma data directory at: {}", path.to_string_lossy()); } DataDirectory::initialize_and_lock(path) } pub fn at_path(path: impl AsRef<Path>) -> SomaResult<Self> { fs::create_dir_all(&path)?; DataDirectory::initialize_and_lock(path.as_ref().to_owned()) } fn initialize_and_lock(path: PathBuf) -> SomaResult<Self> { let lock = File::create(path.join(LOCK_FILE_NAME))?; lock.try_lock_exclusive() .or(Err(SomaError::DataDirectoryLockFailed))?; Ok(DataDirectory { root_path: path, lock, manager_set: HashSet::new(), }) } pub fn register<'a, T>(&'a mut self) -> SomaResult<T> where T: DirectoryManager<'a>, { if !self.manager_set.insert(T::DIR) { panic!("A manager should be registered only once"); } let manager_root = self.root_path.join(T::DIR); fs::create_dir_all(&manager_root)?; Ok(T::new(Registration::new(self, manager_root))?) } } impl Drop for DataDirectory { fn drop(&mut self) { if self.lock.unlock().is_err() { eprintln!("Failed to unlock the data directory"); } } } pub struct Registration<'a, T> where T: DirectoryManager<'a>, { data_dir: &'a DataDirectory, root_path: PathBuf, phantom: PhantomData<*const T>, } impl<'a, T> Registration<'a, T> where T: DirectoryManager<'a>, { fn new(data_dir: &'a DataDirectory, root_path: PathBuf) -> Self { Registration { data_dir, root_path, phantom: PhantomData, } } pub fn data_dir(&self) -> &'a DataDirectory { self.data_dir } pub fn root_path(&self) -> &PathBuf { &self.root_path } } pub trait DirectoryManager<'a>: Deref<Target = Registration<'a, Self>> where Self: Sized + 'a, { const DIR: &'static str; fn new(registration: Registration<'a, Self>) -> SomaResult<Self>; }