diff options
Diffstat (limited to 'rola-utils/space-system/src/space.rs')
| -rw-r--r-- | rola-utils/space-system/src/space.rs | 369 |
1 files changed, 300 insertions, 69 deletions
diff --git a/rola-utils/space-system/src/space.rs b/rola-utils/space-system/src/space.rs index 3fe3507..55c3add 100644 --- a/rola-utils/space-system/src/space.rs +++ b/rola-utils/space-system/src/space.rs @@ -10,7 +10,7 @@ use std::{ mod error; pub use error::*; -pub struct Space<T: SpaceRoot> { +pub struct Space<T: SpaceRoot + Default> { path_format_cfg: PathFormatConfig, content: T, @@ -20,7 +20,19 @@ pub struct Space<T: SpaceRoot> { pub(crate) override_pattern: Option<SpaceRootFindPattern>, } -impl<T: SpaceRoot> Space<T> { +impl<T: SpaceRoot + Default> Clone for Space<T> { + fn clone(&self) -> Self { + Self { + path_format_cfg: self.path_format_cfg, + content: T::default(), + space_dir: RwLock::new(None), + current_dir: self.current_dir.clone(), + override_pattern: None, + } + } +} + +impl<T: SpaceRoot + Default> Space<T> { /// Create a new `Space` instance with the given content. pub fn new(content: T) -> Self { Space { @@ -35,11 +47,11 @@ impl<T: SpaceRoot> Space<T> { } } - /// Initialize a space at the given path. + /// Initialize a space at the given path (sync version). /// /// Checks if a space exists at the given path. If not, creates a new space /// by calling `T::create_space()` at that path. - pub async fn init(&self, path: impl AsRef<Path>) -> Result<(), SpaceError> { + pub fn init(&self, path: impl AsRef<Path>) -> Result<(), SpaceError> { let path = path.as_ref(); let pattern = match &self.override_pattern { Some(pattern) => pattern, @@ -53,38 +65,90 @@ impl<T: SpaceRoot> Space<T> { }; if find_space_root_with(&path, pattern).is_err() { - T::create_space(&path).await?; + T::create_space(&path)? } Ok(()) } - /// Create a new space at the given path with the specified name. + /// Initialize a space at the given path (async version). + /// + /// Checks if a space exists at the given path. If not, creates a new space + /// by calling `T::create_space()` at that path. + pub async fn init_async(&self, path: impl AsRef<Path>) -> Result<(), SpaceError> { + let path = path.as_ref(); + let pattern = match &self.override_pattern { + Some(pattern) => pattern, + None => &T::get_pattern(), + }; + + // If using Absolute, directly read the internal path + let path = match &pattern { + SpaceRootFindPattern::AbsolutePath(path_buf) => path_buf.clone(), + _ => path.to_path_buf(), + }; + + if find_space_root_with(&path, pattern).is_err() { + T::create_space(&path)?; + } + Ok(()) + } + + /// Create a new space at the given path with the specified name (sync version). + /// + /// The full path is constructed as `path/name`. Checks if a space already + /// exists at that location. If not, creates a new space by calling + /// `T::create_space()` at that path. + pub fn create(&self, path: impl AsRef<Path>, name: &str) -> Result<(), SpaceError> { + let full_path = path.as_ref().join(name); + self.init(full_path) + } + + /// Create a new space at the given path with the specified name (async version). /// /// The full path is constructed as `path/name`. Checks if a space already /// exists at that location. If not, creates a new space by calling /// `T::create_space()` at that path. - pub async fn create(&self, path: impl AsRef<Path>, name: &str) -> Result<(), SpaceError> { + pub async fn create_async(&self, path: impl AsRef<Path>, name: &str) -> Result<(), SpaceError> { let full_path = path.as_ref().join(name); - self.init(full_path).await + self.init_async(full_path).await + } + + /// Initialize a space in the current directory (sync version). + /// + /// Checks if a space exists in the current directory. If not, creates a new space + /// by calling `T::create_space()` at the current directory. + pub fn init_here(&self) -> Result<(), SpaceError> { + let current_dir = self.current_dir()?; + self.init(current_dir) } - /// Initialize a space in the current directory. + /// Initialize a space in the current directory (async version). /// /// Checks if a space exists in the current directory. If not, creates a new space /// by calling `T::create_space()` at the current directory. - pub async fn init_here(&self) -> Result<(), SpaceError> { + pub async fn init_here_async(&self) -> Result<(), SpaceError> { let current_dir = self.current_dir()?; - self.init(current_dir).await + self.init_async(current_dir).await } - /// Create a new space in the current directory with the specified name. + /// Create a new space in the current directory with the specified name (sync version). /// /// The full path is constructed as `current_dir/name`. Checks if a space already /// exists at that location. If not, creates a new space by calling /// `T::create_space()` at that path. - pub async fn create_here(&self, name: &str) -> Result<(), SpaceError> { + pub fn create_here(&self, name: &str) -> Result<(), SpaceError> { let current_dir = self.current_dir()?; - self.create(current_dir, name).await + self.create(current_dir, name) + } + + /// Create a new space in the current directory with the specified name (async version). + /// + /// The full path is constructed as `current_dir/name`. Checks if a space already + /// exists at that location. If not, creates a new space by calling + /// `T::create_space()` at that path. + pub async fn create_here_async(&self, name: &str) -> Result<(), SpaceError> { + let current_dir = self.current_dir()?; + self.create_async(current_dir, name).await } /// Consume the `Space`, returning the inner content. @@ -131,9 +195,9 @@ impl<T: SpaceRoot> Space<T> { /// Set the current directory explicitly. /// /// This clears any cached space directory. - pub fn set_current_dir(&mut self, path: PathBuf) -> Result<(), SpaceError> { + pub fn set_current_dir(&mut self, path: impl AsRef<Path>) -> Result<(), SpaceError> { self.update_space_dir(None); - self.current_dir = Some(fmt_path(path)?); + self.current_dir = Some(fmt_path(path.as_ref())?); Ok(()) } @@ -177,7 +241,7 @@ impl<T: SpaceRoot> Space<T> { } } -impl<T: SpaceRoot> Space<T> { +impl<T: SpaceRoot + Default> Space<T> { /// Convert a relative path to an absolute path within the space. /// /// The path is formatted according to the space's path format configuration. @@ -203,65 +267,122 @@ impl<T: SpaceRoot> Space<T> { } /// Canonicalize a relative path within the space. - pub async fn canonicalize( + pub fn canonicalize(&self, relative_path: impl AsRef<Path>) -> Result<PathBuf, SpaceError> { + let path = self.local_path(relative_path)?; + Ok(std::fs::canonicalize(path)?) + } + + /// Canonicalize a relative path within the space (async version). + pub async fn canonicalize_async( &self, relative_path: impl AsRef<Path>, ) -> Result<PathBuf, SpaceError> { - let path = self.local_path(relative_path)?; - Ok(tokio::fs::canonicalize(path).await?) + self.canonicalize(relative_path) } /// Copy a file from one relative path to another within the space. - pub async fn copy( + pub fn copy(&self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<u64, SpaceError> { + let from_path = self.local_path(from)?; + let to_path = self.local_path(to)?; + Ok(std::fs::copy(from_path, to_path)?) + } + + /// Copy a file from one relative path to another within the space (async version). + pub async fn copy_async( &self, from: impl AsRef<Path>, to: impl AsRef<Path>, ) -> Result<u64, SpaceError> { - let from_path = self.local_path(from)?; - let to_path = self.local_path(to)?; - Ok(tokio::fs::copy(from_path, to_path).await?) + self.copy(from, to) } /// Create a directory at the given relative path within the space. - pub async fn create_dir(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { + pub fn create_dir(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::create_dir(path).await?) + Ok(std::fs::create_dir(path)?) + } + + /// Create a directory at the given relative path within the space (async version). + pub async fn create_dir_async( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<(), SpaceError> { + self.create_dir(relative_path) } /// Recursively create a directory and all its parents at the given relative path within the space. - pub async fn create_dir_all(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { + pub fn create_dir_all(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::create_dir_all(path).await?) + Ok(std::fs::create_dir_all(path)?) + } + + /// Recursively create a directory and all its parents at the given relative path within the space (async version). + pub async fn create_dir_all_async( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<(), SpaceError> { + self.create_dir_all(relative_path) } /// Create a hard link from `src` to `dst` within the space. - pub async fn hard_link( + pub fn hard_link( &self, src: impl AsRef<Path>, dst: impl AsRef<Path>, ) -> Result<(), SpaceError> { let src_path = self.local_path(src)?; let dst_path = self.local_path(dst)?; - Ok(tokio::fs::hard_link(src_path, dst_path).await?) + Ok(std::fs::hard_link(src_path, dst_path)?) + } + + /// Create a hard link from `src` to `dst` within the space (async version). + pub async fn hard_link_async( + &self, + src: impl AsRef<Path>, + dst: impl AsRef<Path>, + ) -> Result<(), SpaceError> { + self.hard_link(src, dst) } /// Get metadata for a file or directory at the given relative path within the space. - pub async fn metadata( + pub fn metadata( &self, relative_path: impl AsRef<Path>, ) -> Result<std::fs::Metadata, SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::metadata(path).await?) + Ok(std::fs::metadata(path)?) + } + + /// Get metadata for a file or directory at the given relative path within the space (async version). + pub async fn metadata_async( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<std::fs::Metadata, SpaceError> { + self.metadata(relative_path) } /// Read the entire contents of a file at the given relative path within the space. - pub async fn read(&self, relative_path: impl AsRef<Path>) -> Result<Vec<u8>, SpaceError> { + pub fn read(&self, relative_path: impl AsRef<Path>) -> Result<Vec<u8>, SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::read(path).await?) + Ok(std::fs::read(path)?) + } + + /// Read the entire contents of a file at the given relative path within the space (async version). + pub async fn read_async(&self, relative_path: impl AsRef<Path>) -> Result<Vec<u8>, SpaceError> { + self.read(relative_path) } /// Read the directory entries at the given relative path within the space. - pub async fn read_dir( + pub fn read_dir( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<std::fs::ReadDir, SpaceError> { + let path = self.local_path(relative_path)?; + Ok(std::fs::read_dir(path)?) + } + + /// Read the directory entries at the given relative path within the space (async version). + pub async fn read_dir_async( &self, relative_path: impl AsRef<Path>, ) -> Result<tokio::fs::ReadDir, SpaceError> { @@ -270,112 +391,216 @@ impl<T: SpaceRoot> Space<T> { } /// Read the target of a symbolic link at the given relative path within the space. - pub async fn read_link(&self, relative_path: impl AsRef<Path>) -> Result<PathBuf, SpaceError> { + pub fn read_link(&self, relative_path: impl AsRef<Path>) -> Result<PathBuf, SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::read_link(path).await?) + Ok(std::fs::read_link(path)?) + } + + /// Read the target of a symbolic link at the given relative path within the space (async version). + pub async fn read_link_async( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<PathBuf, SpaceError> { + self.read_link(relative_path) } /// Read the entire contents of a file as a string at the given relative path within the space. - pub async fn read_to_string( + pub fn read_to_string(&self, relative_path: impl AsRef<Path>) -> Result<String, SpaceError> { + let path = self.local_path(relative_path)?; + Ok(std::fs::read_to_string(path)?) + } + + /// Read the entire contents of a file as a string at the given relative path within the space (async version). + pub async fn read_to_string_async( &self, relative_path: impl AsRef<Path>, ) -> Result<String, SpaceError> { - let path = self.local_path(relative_path)?; - Ok(tokio::fs::read_to_string(path).await?) + self.read_to_string(relative_path) } /// Remove an empty directory at the given relative path within the space. - pub async fn remove_dir(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { + pub fn remove_dir(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::remove_dir(path).await?) + Ok(std::fs::remove_dir(path)?) + } + + /// Remove an empty directory at the given relative path within the space (async version). + pub async fn remove_dir_async( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<(), SpaceError> { + self.remove_dir(relative_path) } /// Remove a directory and all its contents at the given relative path within the space. - pub async fn remove_dir_all(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { + pub fn remove_dir_all(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::remove_dir_all(path).await?) + Ok(std::fs::remove_dir_all(path)?) + } + + /// Remove a directory and all its contents at the given relative path within the space (async version). + pub async fn remove_dir_all_async( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<(), SpaceError> { + self.remove_dir_all(relative_path) } /// Remove a file at the given relative path within the space. - pub async fn remove_file(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { + pub fn remove_file(&self, relative_path: impl AsRef<Path>) -> Result<(), SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::remove_file(path).await?) + Ok(std::fs::remove_file(path)?) + } + + /// Remove a file at the given relative path within the space (async version). + pub async fn remove_file_async( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<(), SpaceError> { + self.remove_file(relative_path) } /// Rename a file or directory from one relative path to another within the space. - pub async fn rename( + pub fn rename(&self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<(), SpaceError> { + let from_path = self.local_path(from)?; + let to_path = self.local_path(to)?; + Ok(std::fs::rename(from_path, to_path)?) + } + + /// Rename a file or directory from one relative path to another within the space (async version). + pub async fn rename_async( &self, from: impl AsRef<Path>, to: impl AsRef<Path>, ) -> Result<(), SpaceError> { - let from_path = self.local_path(from)?; - let to_path = self.local_path(to)?; - Ok(tokio::fs::rename(from_path, to_path).await?) + self.rename(from, to) } /// Set permissions for a file or directory at the given relative path within the space. - pub async fn set_permissions( + pub fn set_permissions( &self, relative_path: impl AsRef<Path>, perm: std::fs::Permissions, ) -> Result<(), SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::set_permissions(path, perm).await?) + Ok(std::fs::set_permissions(path, perm)?) + } + + /// Set permissions for a file or directory at the given relative path within the space (async version). + pub async fn set_permissions_async( + &self, + relative_path: impl AsRef<Path>, + perm: std::fs::Permissions, + ) -> Result<(), SpaceError> { + self.set_permissions(relative_path, perm) } /// Create a symbolic link from `src` to `dst` within the space (Unix only). #[cfg(unix)] - pub async fn symlink( + pub fn symlink(&self, src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<(), SpaceError> { + let src_path = self.local_path(src)?; + let dst_path = self.local_path(dst)?; + Ok(std::os::unix::fs::symlink(src_path, dst_path)?) + } + + /// Create a symbolic link from `src` to `dst` within the space (Unix only, async version). + #[cfg(unix)] + pub async fn symlink_async( &self, src: impl AsRef<Path>, dst: impl AsRef<Path>, ) -> Result<(), SpaceError> { - let src_path = self.local_path(src)?; - let dst_path = self.local_path(dst)?; - Ok(tokio::fs::symlink(src_path, dst_path).await?) + self.symlink(src, dst) } /// Create a directory symbolic link from `src` to `dst` within the space (Windows only). #[cfg(windows)] - pub async fn symlink_dir( + pub fn symlink_dir( &self, src: impl AsRef<Path>, dst: impl AsRef<Path>, ) -> Result<(), SpaceError> { let src_path = self.local_path(src)?; let dst_path = self.local_path(dst)?; - Ok(tokio::fs::symlink_dir(src_path, dst_path).await?) + Ok(std::os::windows::fs::symlink_dir(src_path, dst_path)?) + } + + /// Create a directory symbolic link from `src` to `dst` within the space (Windows only, async version). + #[cfg(windows)] + pub async fn symlink_dir_async( + &self, + src: impl AsRef<Path>, + dst: impl AsRef<Path>, + ) -> Result<(), SpaceError> { + self.symlink_dir(src, dst) } /// Create a file symbolic link from `src` to `dst` within the space (Windows only). #[cfg(windows)] - pub async fn symlink_file( + pub fn symlink_file( &self, src: impl AsRef<Path>, dst: impl AsRef<Path>, ) -> Result<(), SpaceError> { let src_path = self.local_path(src)?; let dst_path = self.local_path(dst)?; - Ok(tokio::fs::symlink_file(src_path, dst_path).await?) + Ok(std::os::windows::fs::symlink_file(src_path, dst_path)?) + } + + /// Create a file symbolic link from `src` to `dst` within the space (Windows only, async version). + #[cfg(windows)] + pub async fn symlink_file_async( + &self, + src: impl AsRef<Path>, + dst: impl AsRef<Path>, + ) -> Result<(), SpaceError> { + self.symlink_file(src, dst) } /// Get metadata for a file or directory without following symbolic links. - pub async fn symlink_metadata( + pub fn symlink_metadata( &self, relative_path: impl AsRef<Path>, ) -> Result<std::fs::Metadata, SpaceError> { let path = self.local_path(relative_path)?; - Ok(tokio::fs::symlink_metadata(path).await?) + Ok(std::fs::symlink_metadata(path)?) + } + + /// Get metadata for a file or directory without following symbolic links (async version). + pub async fn symlink_metadata_async( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<std::fs::Metadata, SpaceError> { + self.symlink_metadata(relative_path) } /// Check if a file or directory exists at the given relative path within the space. - pub async fn try_exists(&self, relative_path: impl AsRef<Path>) -> Result<bool, SpaceError> { + pub fn try_exists(&self, relative_path: impl AsRef<Path>) -> Result<bool, SpaceError> { + let path = self.local_path(relative_path)?; + Ok(path.try_exists()?) + } + + /// Check if a file or directory exists at the given relative path within the space (async version). + pub async fn try_exists_async( + &self, + relative_path: impl AsRef<Path>, + ) -> Result<bool, SpaceError> { let path = self.local_path(relative_path)?; Ok(tokio::fs::try_exists(path).await?) } /// Write data to a file at the given relative path within the space. - pub async fn write( + pub fn write( + &self, + relative_path: impl AsRef<Path>, + contents: impl AsRef<[u8]>, + ) -> Result<(), SpaceError> { + let path = self.local_path(relative_path)?; + Ok(std::fs::write(path, contents)?) + } + + /// Write data to a file at the given relative path within the space (async version). + pub async fn write_async( &self, relative_path: impl AsRef<Path>, contents: impl AsRef<[u8]>, @@ -385,25 +610,31 @@ impl<T: SpaceRoot> Space<T> { } /// Check if a file or directory exists at the given relative path within the space. - pub async fn exists(&self, relative_path: impl AsRef<Path>) -> Result<bool, SpaceError> { + pub fn exists(&self, relative_path: impl AsRef<Path>) -> Result<bool, SpaceError> { + let path = self.local_path(relative_path)?; + Ok(path.try_exists()?) + } + + /// Check if a file or directory exists at the given relative path within the space (async version). + pub async fn exists_async(&self, relative_path: impl AsRef<Path>) -> Result<bool, SpaceError> { let path = self.local_path(relative_path)?; Ok(tokio::fs::try_exists(path).await?) } } -impl<T: SpaceRoot> From<T> for Space<T> { +impl<T: SpaceRoot + Default> From<T> for Space<T> { fn from(content: T) -> Self { Space::<T>::new(content) } } -impl<T: SpaceRoot> AsRef<T> for Space<T> { +impl<T: SpaceRoot + Default> AsRef<T> for Space<T> { fn as_ref(&self) -> &T { &self.content } } -impl<T: SpaceRoot> Deref for Space<T> { +impl<T: SpaceRoot + Default> Deref for Space<T> { type Target = T; fn deref(&self) -> &Self::Target { self.as_ref() @@ -415,7 +646,7 @@ pub trait SpaceRoot: Sized { fn get_pattern() -> SpaceRootFindPattern; /// Given a non-space directory, implement logic to make it a space-recognizable directory - fn create_space(path: &Path) -> impl Future<Output = Result<(), SpaceError>> + Send; + fn create_space(path: &Path) -> Result<(), SpaceError>; } pub enum SpaceRootFindPattern { |
