@jpgilldev / steid

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