steid

@jamesgill /

1//! Repository ports.
2//!
3//! Traits only — the domain declares what persistence it needs, and
4//! `infrastructure` supplies it. Every port has two implementations: an in-memory one
5//! that makes use cases testable without a database, and a SQLite one for real use.
6
7pub mod membership_repo;
8pub mod org_repo;
9pub mod repo_repo;
10pub mod session_repo;
11pub mod user_repo;
12
13pub use membership_repo::MembershipRepository;
14pub use org_repo::OrgRepository;
15pub use repo_repo::RepoRepository;
16pub use session_repo::SessionRepository;
17pub use user_repo::UserRepository;
18
19/// What a repository can fail with.
20///
21/// Storage failures are infrastructural and carry no domain meaning, so they collapse
22/// into one opaque variant rather than leaking driver types upward.
23#[derive(Debug)]
24pub enum RepositoryError {
25 Backend(Box<dyn std::error::Error + Send + Sync>),
26}
27
28impl RepositoryError {
29 pub fn backend(error: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> Self {
30 Self::Backend(error.into())
31 }
32}
33
34impl std::fmt::Display for RepositoryError {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 match self {
37 Self::Backend(error) => write!(f, "storage failure: {error}"),
38 }
39 }
40}
41
42impl std::error::Error for RepositoryError {
43 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
44 match self {
45 Self::Backend(error) => Some(&**error),
46 }
47 }
48}
49
50pub type RepositoryResult<T> = Result<T, RepositoryError>;