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