-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(repos): built basic repos crate
- Loading branch information
1 parent
a3624c9
commit 2928edb
Showing
5 changed files
with
208 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "repos" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
models = { path = "../models" } | ||
|
||
db.workspace = true | ||
hex.workspace = true | ||
|
||
tracing.workspace = true | ||
|
||
miette.workspace = true | ||
|
||
async-trait.workspace = true | ||
|
||
[lints] | ||
workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
use std::marker::PhantomData; | ||
|
||
pub use db::CreateModelError; | ||
pub(crate) use db::{DatabaseAdapter, FetchModelByIndexError, FetchModelError}; | ||
use hex::health; | ||
use miette::Result; | ||
use tracing::instrument; | ||
|
||
use crate::ModelRepository; | ||
|
||
/// Provides a base repository implementation for any model. | ||
pub struct BaseRepository<M: models::Model, DB: DatabaseAdapter> { | ||
db_adapter: DB, | ||
_phantom: PhantomData<M>, | ||
} | ||
|
||
impl<M: models::Model, DB: DatabaseAdapter + Clone> Clone | ||
for BaseRepository<M, DB> | ||
{ | ||
fn clone(&self) -> Self { | ||
Self { | ||
db_adapter: self.db_adapter.clone(), | ||
_phantom: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
impl<M: models::Model, DB: DatabaseAdapter> BaseRepository<M, DB> { | ||
/// Creates a new `BaseRepository` instance. | ||
pub fn new(db_adapter: DB) -> Self { | ||
tracing::info!( | ||
"creating new `BaseRepository<{:?}>` instance", | ||
M::TABLE_NAME | ||
); | ||
|
||
Self { | ||
db_adapter, | ||
_phantom: PhantomData, | ||
} | ||
} | ||
} | ||
|
||
#[async_trait::async_trait] | ||
impl<M: models::Model, DB: DatabaseAdapter> health::HealthReporter | ||
for BaseRepository<M, DB> | ||
{ | ||
fn name(&self) -> &'static str { stringify!(BaseRepository<M, DB>) } | ||
async fn health_check(&self) -> health::ComponentHealth { | ||
health::AdditiveComponentHealth::from_futures(Some( | ||
self.db_adapter.health_report(), | ||
)) | ||
.await | ||
.into() | ||
} | ||
} | ||
|
||
#[async_trait::async_trait] | ||
impl<M: models::Model, DB: DatabaseAdapter> ModelRepository | ||
for BaseRepository<M, DB> | ||
{ | ||
type Model = M; | ||
type ModelCreateRequest = M; | ||
type CreateError = CreateModelError; | ||
|
||
#[instrument(skip(self))] | ||
async fn create_model( | ||
&self, | ||
input: Self::ModelCreateRequest, | ||
) -> Result<Self::Model, CreateModelError> { | ||
self.db_adapter.create_model::<Self::Model>(input).await | ||
} | ||
|
||
#[instrument(skip(self))] | ||
async fn fetch_model_by_id( | ||
&self, | ||
id: models::RecordId<Self::Model>, | ||
) -> Result<Option<Self::Model>, FetchModelError> { | ||
self.db_adapter.fetch_model_by_id(id).await | ||
} | ||
|
||
#[instrument(skip(self))] | ||
async fn fetch_model_by_index( | ||
&self, | ||
index_name: String, | ||
index_value: models::EitherSlug, | ||
) -> Result<Option<Self::Model>, FetchModelByIndexError> { | ||
self | ||
.db_adapter | ||
.fetch_model_by_index(index_name, index_value) | ||
.await | ||
} | ||
|
||
#[instrument(skip(self))] | ||
async fn enumerate_models(&self) -> Result<Vec<Self::Model>> { | ||
self.db_adapter.enumerate_models::<Self::Model>().await | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
//! Repositories for use in services. | ||
use db::{FetchModelByIndexError, FetchModelError}; | ||
use hex::Hexagonal; | ||
use miette::Result; | ||
use models::EitherSlug; | ||
|
||
mod base; | ||
pub use self::base::BaseRepository; | ||
|
||
/// Defines a repository interface for models. | ||
#[async_trait::async_trait] | ||
pub trait ModelRepository: Hexagonal { | ||
/// The model type. | ||
type Model: models::Model; | ||
/// The request type for creating a model. | ||
type ModelCreateRequest: std::fmt::Debug + Send + Sync + 'static; | ||
/// The error type for creating a model. | ||
type CreateError: std::error::Error + Send + Sync + 'static; | ||
|
||
/// Creates a new model. | ||
async fn create_model( | ||
&self, | ||
input: Self::ModelCreateRequest, | ||
) -> Result<Self::Model, Self::CreateError>; | ||
|
||
/// Fetches a model by its ID. | ||
async fn fetch_model_by_id( | ||
&self, | ||
id: models::RecordId<Self::Model>, | ||
) -> Result<Option<Self::Model>, FetchModelError>; | ||
|
||
/// Fetches a model by an index. | ||
/// | ||
/// Must be a valid index, defined in the model's `INDICES` constant. | ||
async fn fetch_model_by_index( | ||
&self, | ||
index_name: String, | ||
index_value: EitherSlug, | ||
) -> Result<Option<Self::Model>, FetchModelByIndexError>; | ||
|
||
/// Produces a list of all model IDs. | ||
async fn enumerate_models(&self) -> Result<Vec<Self::Model>>; | ||
} | ||
|
||
#[async_trait::async_trait] | ||
impl<T, I> ModelRepository for T | ||
where | ||
T: std::ops::Deref<Target = I> + Hexagonal + Sized, | ||
I: ModelRepository + ?Sized, | ||
{ | ||
type Model = I::Model; | ||
type ModelCreateRequest = I::ModelCreateRequest; | ||
type CreateError = I::CreateError; | ||
|
||
async fn create_model( | ||
&self, | ||
input: Self::ModelCreateRequest, | ||
) -> Result<Self::Model, Self::CreateError> { | ||
I::create_model(self, input).await | ||
} | ||
async fn fetch_model_by_id( | ||
&self, | ||
id: models::RecordId<Self::Model>, | ||
) -> Result<Option<Self::Model>, FetchModelError> { | ||
I::fetch_model_by_id(self, id).await | ||
} | ||
async fn fetch_model_by_index( | ||
&self, | ||
index_name: String, | ||
index_value: EitherSlug, | ||
) -> Result<Option<Self::Model>, FetchModelByIndexError> { | ||
I::fetch_model_by_index(self, index_name, index_value).await | ||
} | ||
async fn enumerate_models(&self) -> Result<Vec<Self::Model>> { | ||
I::enumerate_models(self).await | ||
} | ||
} |