Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

9. Storage trait for dependency injection

Date: 2026-04-22

Status

Accepted

Context

Collectors need to read files and access git/OpenCode databases. We want to enable testing without real filesystems.

Decision

We define Storage trait with default implementations:

#![allow(unused)]
fn main() {
pub trait Storage: Send + Sync {
    async fn exists(&self, path: &Path) -> bool;
    async fn read_to_string(&self, path: &Path) -> Result<String>;
    // ...
}

pub trait Git: Send + Sync {
    async fn get_commits(&self, repo: &Path, n: usize) -> Result<Vec<GitCommit>>;
    async fn get_modified_files(&self, repo: &Path) -> Result<Vec<FileChange>>;
    // ...
}
}

Implementations:

  • TokioStorage — Uses tokio for async file I/O
  • Git2 — Uses git2 crate for git operations
  • SqliteDb — Uses rusqlite for OpenCode DB

Consequences

Pros

  • Testable without real filesystem
  • Swap implementations (e.g., mock storage for tests)
  • Clear abstraction boundaries

Cons

  • Trait indirection overhead
  • More complex DI setup

Notes

See cerebro-core/src/storage.rs.