summaryrefslogtreecommitdiff
path: root/systems/_framework
diff options
context:
space:
mode:
Diffstat (limited to 'systems/_framework')
-rw-r--r--systems/_framework/Cargo.toml2
-rw-r--r--systems/_framework/space_macro/Cargo.toml13
-rw-r--r--systems/_framework/space_macro/src/lib.rs60
-rw-r--r--systems/_framework/src/lib.rs2
-rw-r--r--systems/_framework/src/space.rs61
5 files changed, 131 insertions, 7 deletions
diff --git a/systems/_framework/Cargo.toml b/systems/_framework/Cargo.toml
index 663b7be..25d5fee 100644
--- a/systems/_framework/Cargo.toml
+++ b/systems/_framework/Cargo.toml
@@ -4,6 +4,8 @@ edition = "2024"
version.workspace = true
[dependencies]
+space_macro = { path = "space_macro" }
+
just_fmt.workspace = true
thiserror.workspace = true
tokio.workspace = true
diff --git a/systems/_framework/space_macro/Cargo.toml b/systems/_framework/space_macro/Cargo.toml
new file mode 100644
index 0000000..dea3f21
--- /dev/null
+++ b/systems/_framework/space_macro/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "space_macro"
+version.workspace = true
+edition = "2024"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn.workspace = true
+quote.workspace = true
+proc-macro2.workspace = true
+just_fmt.workspace = true
diff --git a/systems/_framework/space_macro/src/lib.rs b/systems/_framework/space_macro/src/lib.rs
new file mode 100644
index 0000000..7877181
--- /dev/null
+++ b/systems/_framework/space_macro/src/lib.rs
@@ -0,0 +1,60 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{DeriveInput, parse_macro_input};
+
+#[proc_macro_derive(SpaceRootTest)]
+pub fn space_root_test_derive(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ let name = &input.ident;
+
+ let test_mod_name = syn::Ident::new(
+ &format!(
+ "test_{}_space_root",
+ just_fmt::snake_case!(name.to_string())
+ ),
+ name.span(),
+ );
+
+ let expanded = quote! {
+ #[cfg(test)]
+ mod #test_mod_name {
+ use super::*;
+ use framework::space::Space;
+ use std::env::{current_dir, set_current_dir};
+ use tokio::fs::{create_dir_all, remove_dir_all};
+
+ #[tokio::test]
+ async fn test_create_space() {
+ let temp_dir = current_dir().unwrap().join(".temp").join(stringify!(#name));
+ remove_dir_all(&temp_dir).await.ok();
+ create_dir_all(&temp_dir).await.unwrap();
+ set_current_dir(&temp_dir).unwrap();
+
+ let space = Space::new(#name::default());
+
+ assert!(space.space_dir_current().is_err());
+
+ space.init_here().await.unwrap();
+
+ assert!(space.space_dir_current().is_ok());
+
+ let space_dir = space.space_dir_current().unwrap();
+ let pattern = <#name as framework::space::SpaceRoot>::get_pattern();
+
+ match pattern {
+ framework::space::SpaceRootFindPattern::IncludeDotDir(dir_name) => {
+ let expected_dir = space_dir.join(dir_name);
+ assert!(expected_dir.exists(), "Space directory {:?} should exist", expected_dir);
+ }
+ framework::space::SpaceRootFindPattern::IncludeFile(file_name) => {
+ let expected_file = space_dir.join(file_name);
+ assert!(expected_file.exists(), "Space file {:?} should exist", expected_file);
+ }
+ }
+ }
+ }
+ };
+
+ TokenStream::from(expanded)
+}
diff --git a/systems/_framework/src/lib.rs b/systems/_framework/src/lib.rs
index 9ffc73e..b0a3f9b 100644
--- a/systems/_framework/src/lib.rs
+++ b/systems/_framework/src/lib.rs
@@ -1 +1,3 @@
pub mod space;
+#[allow(unused_imports)]
+pub use space_macro::*;
diff --git a/systems/_framework/src/space.rs b/systems/_framework/src/space.rs
index 47d3c0d..c57fa26 100644
--- a/systems/_framework/src/space.rs
+++ b/systems/_framework/src/space.rs
@@ -10,7 +10,7 @@ use std::{
pub mod error;
-pub struct Space<T: SpaceContent> {
+pub struct Space<T: SpaceRoot> {
path_format_cfg: PathFormatConfig,
content: T,
@@ -18,7 +18,7 @@ pub struct Space<T: SpaceContent> {
current_dir: Option<PathBuf>,
}
-impl<T: SpaceContent> Space<T> {
+impl<T: SpaceRoot> Space<T> {
/// Create a new `Space` instance with the given content.
pub fn new(content: T) -> Self {
Space {
@@ -32,6 +32,49 @@ impl<T: SpaceContent> Space<T> {
}
}
+ /// Initialize a space at the given path.
+ ///
+ /// 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> {
+ let path = path.as_ref();
+ let pattern = T::get_pattern();
+
+ if !find_space_root_with(path.to_path_buf(), pattern).is_ok() {
+ T::create_space(path).await?;
+ }
+ Ok(())
+ }
+
+ /// Create a new space at the given path with the specified name.
+ ///
+ /// 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> {
+ let full_path = path.as_ref().join(name);
+ self.init(full_path).await
+ }
+
+ /// Initialize a space in the current directory.
+ ///
+ /// 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> {
+ let current_dir = self.current_dir()?;
+ self.init(current_dir).await
+ }
+
+ /// Create a new space in the current directory with the specified name.
+ ///
+ /// 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> {
+ let current_dir = self.current_dir()?;
+ self.create(current_dir, name).await
+ }
+
/// Consume the `Space`, returning the inner content.
pub fn into_inner(self) -> T {
self.content
@@ -101,7 +144,7 @@ impl<T: SpaceContent> Space<T> {
}
}
-impl<T: SpaceContent> Space<T> {
+impl<T: SpaceRoot> 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.
@@ -309,27 +352,31 @@ impl<T: SpaceContent> Space<T> {
}
}
-impl<T: SpaceContent> From<T> for Space<T> {
+impl<T: SpaceRoot> From<T> for Space<T> {
fn from(content: T) -> Self {
Space::<T>::new(content)
}
}
-impl<T: SpaceContent> AsRef<T> for Space<T> {
+impl<T: SpaceRoot> AsRef<T> for Space<T> {
fn as_ref(&self) -> &T {
&self.content
}
}
-impl<T: SpaceContent> Deref for Space<T> {
+impl<T: SpaceRoot> Deref for Space<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.as_ref()
}
}
-pub trait SpaceContent {
+pub trait SpaceRoot: Sized {
+ /// Get the pattern used to identify the space root
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;
}
pub enum SpaceRootFindPattern {