cascade-cli 0.1.152

Stacked diffs CLI for Bitbucket Server
Documentation
---
description: 
globs: 
alwaysApply: true
---
# Architecture Patterns Guide

## Core Design Principles

### Command Pattern
All CLI commands implement a consistent pattern in [src/cli/commands/](mdc:src/cli/commands):

```rust
// Standard command module structure
pub async fn run(args: CommandArgs) -> Result<()> {
    // 1. Validate input
    // 2. Load repository/configuration
    // 3. Execute business logic
    // 4. Handle output/errors
}
```

### Repository Pattern
Git operations are abstracted through [src/git/repository.rs](mdc:src/git/repository.rs):

```rust
pub struct Repository {
    git_repo: git2::Repository,
    repo_path: PathBuf,
}

impl Repository {
    // High-level operations that combine multiple git2 calls
    pub fn create_branch(&self, name: &str) -> Result<()>
    pub fn force_push_branch(&self, branch: &str) -> Result<()>
}
```

## Modular Architecture

### Layer Separation

#### 1. CLI Layer ([src/cli/](mdc:src/cli))
- **Responsibility**: User interface, argument parsing, command routing
- **Key Files**: [mod.rs](mdc:src/cli/mod.rs), [commands/](mdc:src/cli/commands)
- **Pattern**: Each command is a separate module with standardized interface

#### 2. Business Logic Layer ([src/stack/](mdc:src/stack), [src/config/](mdc:src/config))
- **Responsibility**: Core domain logic, state management
- **Key Files**: [stack/manager.rs](mdc:src/stack/manager.rs), [config/settings.rs](mdc:src/config/settings.rs)
- **Pattern**: Manager classes coordinate between repositories and business rules

#### 3. Infrastructure Layer ([src/git/](mdc:src/git), [src/bitbucket/](mdc:src/bitbucket))
- **Responsibility**: External system integration
- **Key Files**: [git/repository.rs](mdc:src/git/repository.rs), [bitbucket/client.rs](mdc:src/bitbucket/client.rs)
- **Pattern**: Adapter pattern for external APIs and tools

## Error Handling Strategy

### Centralized Error Types
All errors flow through [src/errors.rs](mdc:src/errors.rs):

```rust
#[derive(Debug)]
pub enum CascadeError {
    Git(String),
    Config(String),
    Validation(String),
    // ... other variants
}
```

### Error Propagation Pattern
```rust
// Functions return Result<T, CascadeError>
pub fn operation() -> Result<String> {
    let git_result = git_operation()
        .map_err(|e| CascadeError::Git(e.to_string()))?;
    
    Ok(git_result)
}
```

## State Management

### Stack Metadata
Stack state is persisted through [src/stack/metadata.rs](mdc:src/stack/metadata.rs):

```rust
pub struct StackMetadata {
    pub name: String,
    pub entries: Vec<StackEntry>,
    pub base_branch: String,
}

impl StackMetadata {
    pub fn save(&self, repo_path: &Path) -> Result<()>
    pub fn load(repo_path: &Path, name: &str) -> Result<Self>
}
```

### Configuration Management
Application settings use [src/config/settings.rs](mdc:src/config/settings.rs):

```rust
pub struct Settings {
    pub bitbucket_url: Option<String>,
    pub auth: AuthConfig,
}

impl Settings {
    pub fn load_from_file(repo_path: &Path) -> Result<Self>
    pub fn save_to_file(&self, repo_path: &Path) -> Result<()>
}
```

## Async Patterns

### Command Execution
CLI commands are async to support:
- Network operations (Bitbucket API)
- Long-running Git operations
- User interaction (prompts, confirmations)

```rust
#[tokio::main]
async fn main() -> Result<()> {
    match cli.command {
        Commands::Stack(action) => stack::run(action).await,
        Commands::Init(args) => init::run(args.bitbucket_url, args.force).await,
        // ... other commands
    }
}
```

## External Integration Patterns

### Bitbucket API Client
[src/bitbucket/client.rs](mdc:src/bitbucket/client.rs) implements:

```rust
pub struct BitbucketClient {
    base_url: String,
    client: reqwest::Client,
}

impl BitbucketClient {
    // Factory methods for different auth types
    pub fn with_token(base_url: String, token: String) -> Self
    pub fn with_credentials(base_url: String, username: String, password: String) -> Self
    
    // High-level operations
    pub async fn create_pull_request(&self, pr: &PullRequest) -> Result<PullRequest>
}
```

### Git Abstraction
[src/git/repository.rs](mdc:src/git/repository.rs) wraps git2 with domain-specific operations:

```rust
impl Repository {
    // High-level workflows
    pub fn create_feature_branch(&self, base: &str, name: &str) -> Result<String>
    pub fn squash_and_merge(&self, commits: &[String]) -> Result<()>
    
    // Lifecycle management
    pub fn get_commits_between(&self, from: &str, to: &str) -> Result<Vec<git2::Commit<'_>>>
}
```

## Testing Patterns

### Test Organization
- **Unit tests**: Co-located with source files using `#[cfg(test)]`
- **Integration tests**: Separate [tests/](mdc:tests) directory
- **Test helpers**: Shared utilities for repository creation and setup

### Mock and Test Double Strategy
```rust
#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;
    
    // Standard test setup pattern
    fn create_test_repo() -> (TempDir, PathBuf) {
        // Creates isolated test environment
    }
    
    async fn create_initialized_repo() -> (TempDir, PathBuf) {
        // Creates repo with Cascade initialized
    }
}
```

## Configuration Architecture

### Hierarchical Configuration
Settings are loaded with precedence:
1. **Command line arguments** (highest priority)
2. **Environment variables**
3. **Repository configuration** (`.cascade/config.toml`)
4. **Global configuration** (`~/.cascade/config.toml`)
5. **Default values** (lowest priority)

### Configuration Schema
```rust
// Repository-specific configuration
pub struct RepositoryConfig {
    pub bitbucket_url: String,
    pub default_reviewers: Vec<String>,
}

// User-specific configuration
pub struct UserConfig {
    pub auth: AuthConfig,
    pub preferences: UserPreferences,
}
```

## Hook System Architecture

### Hook Management
[src/cli/commands/hooks.rs](mdc:src/cli/commands/hooks.rs) implements:

```rust
pub enum HookType {
    PostCommit,    // Validates commits are added to stacks
    PrePush,       // Prevents force pushes, validates stack state
    CommitMsg,     // Validates commit message format
    PrepareCommitMsg, // Prepares commit message with stack context
}

pub struct HooksManager {
    repo_path: PathBuf,
    hooks_dir: PathBuf,
}
```

### Hook Content Generation
Each hook type has a dedicated content generator:
- **Template-based**: Hooks use shell script templates
- **Binary path resolution**: Hooks find the cascade binary dynamically
- **Error handling**: Hooks provide clear error messages
- **Chaining support**: Hooks can call original hooks from `.git/hooks` to preserve existing functionality

### Hook Installation Types
[src/cli/commands/hooks.rs](mdc:src/cli/commands/hooks.rs) provides two installation modes:

```rust
// Essential hooks (default) - 4 hooks
pub fn install_essential(&self) -> Result<()> {
    // Installs: pre-push, commit-msg, prepare-commit-msg, pre-commit
}

// All hooks (--all flag) - 5 hooks  
pub fn install_with_options(&self, options: &InstallOptions) -> Result<()> {
    // Installs: post-commit, pre-push, commit-msg, prepare-commit-msg, pre-commit
}
```

## Performance Considerations

### Lazy Loading
- Repository objects are created on-demand
- Stack metadata is cached during operations
- Git operations batch when possible

### Resource Management
- Temporary files use RAII with `tempfile::TempDir`
- Git repository objects have explicit lifetime management
- Network connections are pooled via `reqwest::Client`
- Temporary branches use RAII cleanup guards to ensure deletion even on error/panic

### RAII Cleanup Patterns
For resources that must be cleaned up even on error:

```rust
/// RAII guard to ensure temporary branches are cleaned up
struct TempBranchCleanupGuard {
    branches: Vec<String>,
    cleaned: bool,
}

impl Drop for TempBranchCleanupGuard {
    fn drop(&mut self) {
        if !self.cleaned {
            // Log warning - can't access git_repo from Drop
            eprintln!("Warning: Cleanup guard dropped without explicit cleanup");
        }
    }
}

// Usage:
let mut cleanup_guard = TempBranchCleanupGuard::new();
cleanup_guard.add_branch(temp_branch.clone());
// ... operations that might fail or panic ...
cleanup_guard.cleanup(&git_repo); // Explicit cleanup
```

### Atomic File Operations
Use atomic file writes to prevent data corruption:

```rust
// ✅ CORRECT - Atomic write (write to temp file, then rename)
crate::utils::atomic_file::write_string(&full_path, &content)?;

// ❌ WRONG - Non-atomic write (can corrupt file on crash)
std::fs::write(&full_path, &content)?;
```

## Security Patterns

### Authentication
- **Token-based**: Bitbucket API tokens (preferred)
- **Credential storage**: Platform-specific secure storage
- **No plaintext secrets**: All auth data is encrypted at rest

### Input Validation
- All user input is validated before Git operations
- Branch names are sanitized
- URLs are validated before network requests

## Future Extensibility

### Plugin Architecture Preparation
- Commands are modular and self-contained
- External integrations use adapter pattern
- Configuration system supports plugin-specific settings

### API Abstraction
- Git operations are abstracted for potential alternative backends
- Bitbucket client can be extended to other platforms
- Hook system supports custom hook types