//! Work Draft //! //! Work Draft is the local workspace of `Rorolala`, used to store files being modified use std::path::PathBuf; use crate::{ DirPtrData, DirSearchPattern, RolaError, RolaModule, dir_search_prev, workdraft::constants::ROLA_DRAFT_DIR, }; #[rorolala_internal_macros::constants] pub mod constants { /// The name of the workdraft directory pub const ROLA_DRAFT_DIR: &str = ".rola"; /// The name of the directory containing bucket bindings pub const ROLA_BINDED_BUCKETS_DIR: &str = ".rola/BIND/"; /// The name of the bind file pub const ROLA_BINDED_BUCKET_FILE: &str = ".rola/BIND/{bucket}"; } /// Work Draft Pointer /// /// This struct is used to point to an operable local work draft directory on disk #[derive(Debug, Default, Clone)] pub struct WorkDraft; impl DirPtrData for WorkDraft { fn fix(raw_path: PathBuf) -> Option { let draft_dir = ROLA_DRAFT_DIR(); dir_search_prev(raw_path, DirSearchPattern::Dir(&draft_dir)) } } impl WorkDraft { /// Creates a new work draft directory at the given path pub fn create(path: PathBuf) -> Result<(), RolaError> { let dir = path.join(ROLA_DRAFT_DIR()); std::fs::create_dir_all(dir).map_err(|e| RolaError::from((RolaModule::Workdraft, e)))?; Ok(()) } } /// Module for managing workdraft bucket bindings pub mod bucket_bind_mgr { use std::path::PathBuf; use crate::{ DirPtr, RolaError, RolaModule, WorkDraft, constants::ROLA_BINDED_BUCKETS_DIR, workdraft::constants::ROLA_BINDED_BUCKET_FILE, }; impl DirPtr { /// Returns the path to the bind file for this work draft pub fn bind_bucket_path(&self, bucket: impl AsRef) -> PathBuf { self.path().join(ROLA_BINDED_BUCKET_FILE(bucket)) } /// Returns the path to the bind file for this work draft pub fn bind_bucket(&self, bucket: impl AsRef) -> Result { let bucket_path = self.bind_bucket_path(bucket); let parent = bucket_path.parent().ok_or_else(|| -> RolaError { RolaError::from(( RolaModule::Workdraft, std::io::Error::new( std::io::ErrorKind::InvalidInput, format!("Invalid bucket path: {}", bucket_path.to_string_lossy()), ), )) })?; if !parent.exists() { std::fs::create_dir_all(parent) .map_err(|e| RolaError::from((RolaModule::Workdraft, e)))?; } match std::fs::read_to_string(bucket_path) { Ok(str) => Ok(str), Err(err) => Err(RolaError::from((RolaModule::Workdraft, err))), } } /// Binds the work draft to the specified bucket pub fn bind_bucket_to(&self, bucket: &str) -> Result<(), RolaError> { let bucket_path = self.bind_bucket_path(bucket); let parent = bucket_path.parent().ok_or_else(|| -> RolaError { RolaError::from(( RolaModule::Workdraft, std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid bucket path"), )) })?; if !parent.exists() { std::fs::create_dir_all(parent) .map_err(|e| RolaError::from((RolaModule::Workdraft, e)))?; } std::fs::write(&bucket_path, bucket) .map_err(|e| RolaError::from((RolaModule::Workdraft, e)))?; Ok(()) } /// Returns all bound bucket names for this work draft pub fn binded_buckets(&self) -> Result, RolaError> { let bind_dir = self.path().join(ROLA_BINDED_BUCKETS_DIR()); if !bind_dir.exists() { return Ok(Vec::new()); } let mut buckets = Vec::new(); let entries = std::fs::read_dir(&bind_dir) .map_err(|e| RolaError::from((RolaModule::Workdraft, e)))?; for entry in entries { let entry = entry.map_err(|e| RolaError::from((RolaModule::Workdraft, e)))?; let path = entry.path(); if path.is_file() { // Read the file content which contains the bucket name if let Ok(content) = std::fs::read_to_string(&path) { let bucket = content.trim().to_string(); if !bucket.is_empty() { buckets.push(bucket); } } } } Ok(buckets) } /// Removes the binding for the specified bucket pub fn unbind_bucket(&self, bucket: impl AsRef) -> Result<(), RolaError> { let bucket_path = self.bind_bucket_path(bucket); if bucket_path.exists() { std::fs::remove_file(&bucket_path) .map_err(|e| RolaError::from((RolaModule::Workdraft, e)))?; } Ok(()) } /// Removes bindings for all specified buckets pub fn unbind_buckets(&self, buckets: &[impl AsRef]) -> Result<(), RolaError> { for bucket in buckets { self.unbind_bucket(bucket)?; } Ok(()) } } }