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 user_repo;
10
11pub use membership_repo::MembershipRepository;
12pub use org_repo::OrgRepository;
13pub use user_repo::UserRepository;
14
15/// What a repository can fail with.
16///
17/// Storage failures are infrastructural and carry no domain meaning, so they collapse
18/// into one opaque variant rather than leaking driver types upward.
19#[derive(Debug)]
20pub enum RepositoryError {
21 Backend(Box<dyn std::error::Error + Send + Sync>),
22}
23
24impl RepositoryError {
25 pub fn backend(error: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> Self {
26 Self::Backend(error.into())
27 }
28}
29
30impl std::fmt::Display for RepositoryError {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 Self::Backend(error) => write!(f, "storage failure: {error}"),
34 }
35 }
36}
37
38impl std::error::Error for RepositoryError {
39 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
40 match self {
41 Self::Backend(error) => Some(&**error),
42 }
43 }
44}
45
46pub type RepositoryResult<T> = Result<T, RepositoryError>;