---
title: Operations
sidebarTitle: Operations
---

The operations system automatically generates type-safe Swift and TypeScript clients from Rust API definitions. Define your API once in Rust and get native clients for iOS, web, and desktop without manual synchronization.

## How It Works

The system uses compile-time type extraction to discover all operations and generate client code during the build process. This eliminates the traditional API boundary.

### Define Operations

Operations are either Actions (write) or Queries (read):

```rust
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
pub struct CreateLibraryInput {
    pub name: String,
    pub description: Option<String>,
}

pub struct CreateLibraryAction {
	input: CreateLibraryInput
	// action state can be held here
}

impl CoreAction for CreateLibraryAction {
    type Input = CreateLibraryInput;
    type Output = Library;

    async fn validate(&self, context: Arc<CoreContext>)
        -> Result<ValidationResult, ActionError> {
    	// Check if the library already exists and return validation result
     	Ok(ValidationResult::Success)
    }

    async fn execute(self, context: Arc<CoreContext>) -> Result<Self::Output, ActionError> {
      	// Create library and return it
    }

    fn action_kind(&self) -> &'static str {
        "libraries.create"
    }
}
```

### Register and Generate

Register the operation with a single macro:

```rust
register_core_action!(CreateLibraryAction, "libraries.create");
```

The build process automatically:

1. Extracts type information using Specta
2. Generates Swift and TypeScript type definitions
3. Creates native API methods for each client

### Use Generated Clients

Swift:

```swift
let library = try await spacedrive.libraries.create(
    CreateLibraryInput(name: "My Library", description: nil)
)
```

TypeScript:

```typescript
const library = await spacedrive.libraries.create({
	name: "My Library",
});
```

## Operation Types

### Actions

Actions modify state and typically return job receipts or updated entities:

```rust
pub trait LibraryAction {
    type Input: Send + Sync + 'static;
    type Output: Send + Sync + 'static;

    fn from_input(input: Self::Input) -> Result<Self, String>;

    async fn validate(&self, library: &Arc<Library>, context: Arc<CoreContext>)
        -> Result<ValidationResult, ActionError>;

    fn resolve_confirmation(&mut self, choice_index: usize)
        -> Result<(), ActionError>;

    async fn execute(self, library: Arc<Library>, context: Arc<CoreContext>)
        -> impl Future<Output = Result<Self::Output, ActionError>>;

    fn action_kind(&self) -> &'static str;
}
```

### Validation and Confirmation

Actions support a validation phase that can request user confirmation before execution. This enables safe, interactive operations with clear user feedback.

#### ValidationResult

The `validate()` method returns one of two results:

```rust
pub enum ValidationResult {
    /// Action is valid and can proceed
    Success,
    /// Action requires user confirmation
    RequiresConfirmation(ConfirmationRequest),
}

pub struct ConfirmationRequest {
    /// Message to display to the user
    pub message: String,
    /// List of choices for the user
    pub choices: Vec<String>,
}
```

#### Example: File Copy with Conflict Resolution

```rust
impl LibraryAction for FileCopyAction {
    type Input = FileCopyInput;
    type Output = JobReceipt;

    async fn validate(&self, library: &Arc<Library>, context: Arc<CoreContext>)
        -> Result<ValidationResult, ActionError> {
        // Check if destination file exists
        if !self.options.overwrite && self.destination_exists().await? {
            return Ok(ValidationResult::RequiresConfirmation(ConfirmationRequest {
                message: format!(
                    "Destination file already exists: {}",
                    self.destination.display()
                ),
                choices: vec![
                    "Overwrite the existing file".to_string(),
                    "Rename the new file (e.g., file.txt -> file (1).txt)".to_string(),
                    "Abort this copy operation".to_string(),
                ],
            }));
        }

        Ok(ValidationResult::Success)
    }

    fn resolve_confirmation(&mut self, choice_index: usize)
        -> Result<(), ActionError> {
        match choice_index {
            0 => {
                self.on_conflict = Some(FileConflictResolution::Overwrite);
                Ok(())
            }
            1 => {
                self.on_conflict = Some(FileConflictResolution::AutoModifyName);
                Ok(())
            }
            2 => Err(ActionError::Cancelled),
            _ => Err(ActionError::Validation {
                field: "choice".to_string(),
                message: "Invalid choice selected".to_string(),
            })
        }
    }

    async fn execute(mut self, library: Arc<Library>, context: Arc<CoreContext>)
        -> Result<Self::Output, ActionError> {
        // Apply the conflict resolution strategy if set
        if let Some(resolution) = self.on_conflict {
            match resolution {
                FileConflictResolution::Overwrite => {
                    self.options.overwrite = true;
                }
                FileConflictResolution::AutoModifyName => {
                    self.destination = self.generate_unique_name().await?;
                }
                _ => {}
            }
        }

        // Execute the copy operation
        let job = FileCopyJob::new(self.sources, self.destination)
            .with_options(self.options);
        let receipt = library.jobs().dispatch(job).await?;
        Ok(receipt)
    }

    fn action_kind(&self) -> &'static str {
        "files.copy"
    }
}
```

#### CLI Integration

The CLI handles confirmations interactively:

```rust
// In CLI handler
let mut action = FileCopyAction::from_input(input)?;

// Validate the action
let validation_result = action.validate(&library, context).await?;

match validation_result {
    ValidationResult::Success => {
        // Proceed with execution
        let result = action.execute(library, context).await?;
    }
    ValidationResult::RequiresConfirmation(request) => {
        // Prompt user for choice
        let choice_index = prompt_for_choice(request)?;

        // Resolve the confirmation
        action.resolve_confirmation(choice_index)?;

        // Now execute with resolved choice
        let result = action.execute(library, context).await?;
    }
}
```

<Note>
The `validate()` method takes `&self` (a reference), while `execute()` takes `self` (consumes the action). This ensures validation doesn't modify state, while execution can take ownership to transform the action into its result.
</Note>

### Queries

Queries retrieve data without side effects:

```rust
pub trait LibraryQuery {
    type Input: Send + Sync + 'static;
    type Output: Send + Sync + 'static;

    fn from_input(input: Self::Input) -> QueryResult<Self>;
    fn execute(self, context: Arc<CoreContext>, session: SessionContext)
        -> impl Future<Output = QueryResult<Self::Output>>;
}
```

## Type System

All standard Rust types are supported through Specta:

```rust
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
pub struct FileOperationResult {
    pub succeeded: Vec<PathBuf>,
    pub failed: HashMap<PathBuf, String>,
    pub stats: OperationStats,
}

#[derive(Debug, Clone, Serialize, Deserialize, Type)]
pub enum OperationError {
    NotFound(String),
    PermissionDenied,
    DiskFull { required: u64, available: u64 },
}
```

<Note>
	The `Type` derive is required for all types used in operations. This enables
	Specta to extract type information for client generation.
</Note>

## Wire Protocol

Operations use a consistent wire protocol:

- Actions: `action:{category}.{operation}.input.v{version}`
- Queries: `query:{scope}.{operation}.v{version}`

Examples:

- `action:files.copy.input`
- `query:library.stats`

## Adding Operations

### 1. Create Input/Output Types

```rust
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
pub struct SearchInput {
    pub query: String,
    pub filters: SearchFilters,
    pub limit: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize, Type)]
pub struct SearchResult {
    pub items: Vec<SearchItem>,
    pub total_count: u64,
}
```

### 2. Implement the Operation

For a query:

```rust
pub struct SearchQuery {
    query: String,
    filters: SearchFilters,
    limit: u32,
}

impl LibraryQuery for SearchQuery {
    type Input = SearchInput;
    type Output = SearchResult;

    fn from_input(input: Self::Input) -> QueryResult<Self> {
        Ok(Self {
            query: input.query,
            filters: input.filters,
            limit: input.limit,
        })
    }

    async fn execute(self, context: Arc<CoreContext>, session: SessionContext)
        -> QueryResult<Self::Output> {
        // Perform search and return results
    }
}
```

### 3. Register It

```rust
register_library_query!(SearchQuery, "search");
```

### 4. Build and Use

After building, the operation is available in all clients automatically.

## iOS Integration

The iOS app embeds the Rust core and communicates through FFI:

```rust
#[no_mangle]
pub extern "C" fn handle_core_msg(
    query: *const c_char,
    callback: extern "C" fn(*mut c_void, *const c_char),
    callback_data: *mut c_void,
) {
    // Parse JSON-RPC request
    // Execute operation using same registry
    // Return JSON response
}
```

Swift calls through the FFI boundary using the generated types.

## Code Generation Details

### Build Process

The build script runs during `cargo build`:

```rust
// build.rs
fn main() {
    generate_swift_api_code().expect("Failed to generate Swift code");
}
```

### Type Extraction

A binary extracts all registered operations:

```rust
// generate_swift_types binary
fn main() {
    let (operations, queries, types) = generate_spacedrive_api();

    // Generate Swift code
    let swift_types = specta_swift::Swift::new().export(&types)?;
    let api_methods = generate_api_methods(&operations, &queries);

    // Write to Swift package
    fs::write("SpacedriveTypes.swift", swift_types)?;
    fs::write("SpacedriveAPI.swift", api_methods)?;
}
```

### Registration Internals

The registration macros use inventory for compile-time collection:

```rust
inventory::submit! {
    TypeExtractorEntry {
        extractor: SearchQuery::extract_types,
        identifier: "search",
    }
}
```

## Best Practices

### Operation Design

Keep operations focused with clear inputs and outputs. Use appropriate scopes (Library vs Core) based on whether the operation needs library context.

### When to Use Validation Confirmations

Use the confirmation pattern for operations that:

1. **Have Destructive Side Effects**: Deleting files, overwriting data, or making irreversible changes
2. **Encounter Conflicts**: File name collisions, duplicate entries, or conflicting states
3. **Need User Decisions**: Multiple valid approaches where user preference matters
4. **Risk Data Loss**: Operations that could result in unexpected data loss

Examples of good confirmation use cases:
- File copy/move when destination exists
- Deleting non-empty directories
- Overwriting modified files
- Removing locations with indexed content
- Irreversible format conversions

Keep confirmations minimal - only ask when truly necessary. Don't confirm routine operations or when the intent is already clear from the input.

### Type Design

Flatten structures when possible and use Rust enums for variants. Document fields as comments flow through to generated code.

### Error Handling

Define specific error types for each operation:

```rust
#[derive(Debug, Serialize, Deserialize, Type)]
pub enum SearchError {
    InvalidQuery(String),
    IndexNotReady,
    TooManyResults { max: u32, requested: u32 },
}
```

### Performance

For large result sets, consider pagination or streaming:

```rust
#[derive(Type)]
pub struct PaginatedSearch {
    pub query: String,
    pub cursor: Option<String>,
    pub limit: u32,
}
```

## Advanced Features

### Batch Operations

```rust
#[derive(Type)]
pub struct BatchDeleteInput {
    pub items: Vec<ItemIdentifier>,
    pub skip_trash: bool,
}
```

### Operation Metadata

Actions can define metadata for UI presentation:

```rust
impl ActionMetadata for DeleteAction {
    fn display_name() -> &'static str {
        "Delete Items"
    }

    fn description() -> &'static str {
        "Permanently delete selected items"
    }

    fn is_dangerous() -> bool {
        true
    }
}
```

<Note>
Confirmation is handled dynamically through the `validate()` method, not as static metadata. This allows context-aware confirmations based on the actual operation state.
</Note>

<Tip>
	Run `cargo run --bin generate_swift_types` to debug type extraction issues.
	Check the generated files in
	`packages/swift/Sources/SpacedriveClient/Generated/`.
</Tip>

The operations system eliminates manual API maintenance while providing type-safe, performant clients across all platforms.
