stout 0.2.0

A fast, Rust-based Homebrew-compatible package manager
# Architecture

This document describes the internal architecture of stout.

## Overview

stout is structured as a Rust workspace with multiple crates, each handling a specific concern:

```
stout/
├── src/                    # Main CLI binary
├── crates/
│   ├── stout-index/       # SQLite index management
│   ├── stout-resolve/     # Dependency resolution
│   ├── stout-fetch/       # Download management
│   ├── stout-install/     # Package installation
│   └── stout-state/       # Local state management
└── scripts/
    └── sync.py            # Index sync script
```

## System Architecture

```
┌─────────────────────────────────────────────────────────────────────────┐
│                              User                                        │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                           stout CLI (src/)                               │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐           │
│  │ install │ │ search  │ │  info   │ │  list   │ │ doctor  │  ...      │
│  └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘           │
└───────┼──────────┼──────────┼──────────┼──────────┼─────────────────────┘
        │          │          │          │          │
        ▼          ▼          ▼          ▼          ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                         Crate Layer                                      │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                   │
│  │ stout-index  │  │stout-resolve │  │ stout-fetch  │                   │
│  │  (SQLite)    │  │  (Dep Graph) │  │ (Downloads)  │                   │
│  └──────────────┘  └──────────────┘  └──────────────┘                   │
│  ┌──────────────┐  ┌──────────────┐                                     │
│  │stout-install │  │ stout-state  │                                     │
│  │  (Extraction)│  │  (Config)    │                                     │
│  └──────────────┘  └──────────────┘                                     │
└─────────────────────────────────────────────────────────────────────────┘
        │                    │                    │
        ▼                    ▼                    ▼
┌───────────────┐  ┌───────────────┐  ┌───────────────────────────────────┐
│  ~/.stout/    │  │  Homebrew     │  │        Network                    │
│  index.db     │  │  Cellar       │  │  ┌─────────────────────────────┐  │
│  config.toml  │  │  /opt/homebrew│  │  │ stout-index (GitHub raw)    │  │
│  state/       │  │               │  │  │ Homebrew bottles (ghcr.io)  │  │
└───────────────┘  └───────────────┘  │  └─────────────────────────────┘  │
                                      └───────────────────────────────────┘
```

## Crate Details

### stout-index

**Purpose**: SQLite database management and formula queries.

**Key Components**:
- `Database`: SQLite connection wrapper with transaction support
- `Query`: High-level query interface
- `IndexSync`: Remote index synchronization
- `Formula`/`FormulaInfo`: Formula data structures

**Schema**:
```sql
-- Main formula table
CREATE TABLE formulas (
    name TEXT PRIMARY KEY,
    version TEXT NOT NULL,
    revision INTEGER DEFAULT 0,
    desc TEXT,
    homepage TEXT,
    license TEXT,
    tap TEXT DEFAULT 'homebrew/core',
    deprecated INTEGER DEFAULT 0,
    disabled INTEGER DEFAULT 0,
    has_bottle INTEGER DEFAULT 1,
    json_hash TEXT
);

-- Full-text search
CREATE VIRTUAL TABLE formulas_fts USING fts5(name, desc);

-- Dependencies
CREATE TABLE dependencies (
    formula TEXT NOT NULL,
    dep_name TEXT NOT NULL,
    dep_type TEXT NOT NULL  -- runtime, build, test, optional, recommended
);

-- Bottle availability
CREATE TABLE bottles (
    formula TEXT NOT NULL,
    platform TEXT NOT NULL  -- arm64_sonoma, x86_64_linux, etc.
);
```

### stout-resolve

**Purpose**: Dependency graph construction and resolution.

**Key Components**:
- `DependencyGraph`: DAG representation of package dependencies
- `InstallPlan`: Ordered list of packages to install
- Topological sort with cycle detection

**Algorithm**:
```
1. Start with requested packages
2. BFS to collect all dependencies
3. Build directed graph (package -> dependencies)
4. Topological sort (Kahn's algorithm)
5. Detect cycles (if sort incomplete)
6. Return install order (deps first)
```

### stout-fetch

**Purpose**: Download management with caching and verification.

**Key Components**:
- `DownloadClient`: HTTP client with connection pooling
- `DownloadCache`: Local cache for downloaded bottles
- `ProgressReporter`: Multi-progress bar display
- SHA256 verification

**Features**:
- Parallel downloads with semaphore-based concurrency control
- Automatic retry on failure
- Cache-first fetching
- Progress reporting for each download

### stout-install

**Purpose**: Package installation and symlink management.

**Key Components**:
- `extract_bottle()`: Extracts .tar.gz bottles to Cellar
- `link_package()`: Creates symlinks to prefix
- `write_receipt()`: Creates INSTALL_RECEIPT.json
- `remove_package()`: Cleanup on uninstall

**Directory Structure**:
```
/opt/homebrew/
├── Cellar/
│   └── jq/
│       └── 1.7.1/
│           ├── bin/jq
│           ├── lib/
│           ├── share/
│           └── INSTALL_RECEIPT.json
├── bin/
│   └── jq -> ../Cellar/jq/1.7.1/bin/jq
└── opt/
    └── jq -> ../Cellar/jq/1.7.1
```

### stout-state

**Purpose**: Configuration and local state management.

**Key Components**:
- `Config`: User configuration (config.toml)
- `InstalledPackages`: Tracks installed packages (installed.toml)
- `Paths`: Standard directory locations

**Files**:
```
~/.stout/
├── config.toml         # User configuration
├── index.db            # Formula index (SQLite)
├── manifest.json       # Index metadata
├── state/
│   └── installed.toml  # Installed packages tracking
└── cache/
    ├── formulas/       # Cached formula JSON
    └── downloads/      # Cached bottles
```

## Data Flow

### Search Operation

```
User: stout search json
         │
         ▼
    ┌─────────┐
    │ CLI     │ Parse arguments
    └────┬────┘
         │
         ▼
    ┌─────────┐
    │ Index   │ Open ~/.stout/index.db
    └────┬────┘
                      ┌─────────┐
    │ SQLite  │ FTS5 MATCH query
    └────┬────┘
                      ┌─────────┐
    │ Output  │ Format and display results
    └─────────┘
```

### Install Operation

```
User: stout install jq
                      ┌─────────────┐
    │ CLI         │ Parse arguments
    └──────┬──────┘
                          ┌─────────────┐
    │ Index       │ Look up formula, get deps
    └──────┬──────┘
                          ┌─────────────┐
    │ Resolve     │ Build dependency graph
    │             │ Topological sort
    │             │ Create install plan
    └──────┬──────┘
                          ┌─────────────┐
    │ Fetch       │ Download bottles (parallel)
    │             │ Verify checksums
    │             │ Cache locally
    └──────┬──────┘
                          ┌─────────────┐
    │ Install     │ Extract to Cellar
    │             │ Create symlinks
    │             │ Write receipt
    └──────┬──────┘
                          ┌─────────────┐
    │ State       │ Update installed.toml
    └─────────────┘
```

## Index Sync Architecture

The index is maintained separately and synced to clients:

```
┌───────────────────────────────────────────────────────────────┐
│                    Index Generation (sync.py)                  │
├───────────────────────────────────────────────────────────────┤
│                                                                │
│  Homebrew API ──▶ Transform ──▶ SQLite + JSON ──▶ GitHub      │
│  (formula.json)    (Python)      (compressed)     (raw files) │
│                                                                │
└───────────────────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────────────────┐
│                    Index Consumption (stout)                   │
├───────────────────────────────────────────────────────────────┤
│                                                                │
│  GitHub raw ──▶ Download ──▶ Decompress ──▶ Local SQLite      │
│  (index.db.zst)  (HTTP GET)   (zstd)        (~/.stout/)       │
│                                                                │
└───────────────────────────────────────────────────────────────┘
```

## Concurrency Model

stout uses Tokio for async operations:

```rust
// Parallel bottle downloads with semaphore
pub async fn download_bottles(
    &self,
    bottles: Vec<BottleSpec>,
    progress: Arc<ProgressReporter>,
) -> Result<Vec<PathBuf>> {
    let futures: Vec<_> = bottles
        .into_iter()
        .map(|spec| {
            let client = self.clone();
            let progress = Arc::clone(&progress);
            async move {
                // Acquire semaphore permit (limits concurrency)
                let _permit = client.semaphore.acquire().await?;
                client.download_bottle(spec, progress).await
            }
        })
        .collect();

    join_all(futures).await
}
```

## Error Handling

Each crate defines its own error type using `thiserror`:

```rust
#[derive(Error, Debug)]
pub enum Error {
    #[error("Database error: {0}")]
    Database(#[from] rusqlite::Error),

    #[error("Formula not found: {0}")]
    FormulaNotFound(String),

    #[error("Dependency cycle detected: {0}")]
    CycleDetected(String),
    // ...
}
```

Errors propagate up through the crate hierarchy and are handled at the CLI level with user-friendly messages.

## Testing Strategy

- **Unit tests**: Each crate has a `tests.rs` module
- **Integration tests**: Test full workflows with temp directories
- **122 total tests** covering all crates

```bash
cargo test --workspace
```