mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-19 05:59:16 -04:00
- Added a new `apps/cli` module to encapsulate the CLI application logic, improving organization and modularity. - Updated `Cargo.toml` to include dependencies for the CLI, such as `clap` for command-line argument parsing. - Implemented a `Context` struct to manage application state and core client interactions. - Created command handling for file operations and library management, enhancing user experience and functionality. - Introduced utility functions for output formatting, supporting both human-readable and JSON outputs. These changes significantly enhance the structure and maintainability of the CLI application while providing a more robust user interface.
4.4 KiB
4.4 KiB
Perfect Dual Action API: CoreAction vs LibraryAction
Date: 2025-01-27 Status: Perfect Architecture Achieved
🎯 The Perfect Solution
We've achieved the ideal action architecture that eliminates boilerplate while preserving extensibility and central infrastructure.
✅ Two Clear Action Types:
1. CoreAction - Global Operations
/// Core-level actions that operate without library context
pub trait CoreAction: Send + Sync + 'static {
type Output: Send + Sync + 'static;
async fn execute(self, context: Arc<CoreContext>) -> Result<Self::Output, ActionError>;
fn action_kind(&self) -> &'static str;
// No library validation boilerplate!
}
// Examples: Managing libraries themselves, volumes, devices
impl CoreAction for LibraryCreateAction {
type Output = LibraryCreateOutput; // Creates libraries
}
impl CoreAction for VolumeSpeedTestAction {
type Output = VolumeSpeedTestOutput; // Tests volumes globally
}
2. LibraryAction - Library-Scoped Operations
/// Library-scoped actions that operate within a specific library
pub trait LibraryAction: Send + Sync + 'static {
type Output: Send + Sync + 'static;
async fn execute(self, library: Arc<Library>, context: Arc<CoreContext>) -> Result<Self::Output, ActionError>;
fn action_kind(&self) -> &'static str;
fn library_id(&self) -> Uuid;
// Library pre-validated by ActionManager - zero boilerplate!
}
// Examples: Files, locations, indexing within libraries
impl LibraryAction for VolumeTrackAction {
type Output = VolumeTrackOutput; // Tracks volume in library
}
impl LibraryAction for FileCopyAction {
type Output = JobHandle; // Copies files within library
}
🚀 Perfect Usage Examples:
Core Actions:
// ✅ Global operations - no library context needed
let library: LibraryCreateOutput = core.execute_core_action(
LibraryCreateAction::new("Photos".to_string(), None)
).await?;
let speed_result: VolumeSpeedTestOutput = core.execute_core_action(
VolumeSpeedTestAction::new(fingerprint)
).await?;
Library Actions:
// ✅ Library operations - library pre-validated automatically
let volume: VolumeTrackOutput = core.execute_library_action(
VolumeTrackAction::with_name(fingerprint, library_id, "My Drive".to_string())
).await?;
let copy_job: JobHandle = core.execute_library_action(
FileCopyAction::builder()
.library_id(library_id)
.sources(sources)
.destination(dest)
.build()?
).await?;
🎉 Key Benefits Achieved:
1. Zero Boilerplate:
// ❌ OLD: Every action validates library existence
async fn validate(&self, context: Arc<CoreContext>) -> Result<(), ActionError> {
let _library = context.library_manager.get_library(self.library_id).await
.ok_or_else(|| ActionError::LibraryNotFound(self.library_id))?;
// Repeated in EVERY action!
}
// ✅ NEW: ActionManager validates once, provides Library
async fn execute(self, library: Arc<Library>, context: Arc<CoreContext>) -> Result<Self::Output, ActionError> {
// Library already validated - use it directly!
}
2. Clear Semantics:
CoreAction- "This operates at the global level"LibraryAction- "This operates within a library"- No confusion about when to use which pattern
3. Extension Support:
// ✅ Runtime registration for plugins
pub trait ActionRegistry {
fn register_core_action<A: CoreAction + 'static>(&mut self, name: &str);
fn register_library_action<A: LibraryAction + 'static>(&mut self, name: &str);
}
4. Enhanced ActionManager:
impl ActionManager {
// Handles global operations
pub async fn dispatch_core<A: CoreAction>(&self, action: A) -> Result<A::Output>
// Handles library operations with pre-validation
pub async fn dispatch_library<A: LibraryAction>(&self, action: A) -> Result<A::Output>
}
💡 Perfect Architecture:
- ✅ No centralized enums - True modularity achieved
- ✅ Central dispatch - Validation, audit logging, monitoring
- ✅ Zero boilerplate - Library validation done once at ActionManager level
- ✅ Type safety - Library vs Core distinction enforced by type system
- ✅ Extension support - Runtime registration for plugins
- ✅ Natural return types - Domain objects or job handles as appropriate
This is the perfect balance of modularity, infrastructure, and usability! 🎯✨