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/OGit2— Usesgit2crate for git operationsSqliteDb— Usesrusqlitefor 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.