Files
spacedrive/docs/worktable/PERFECT_DUAL_ACTION_API.md
Jamie Pine 1f2c38b440 refactor: introduce CLI application structure and enhance command handling
- 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.
2025-09-09 20:04:20 -04:00

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! 🎯