pub(crate) mod sql_db; use async_trait::async_trait; use sqlx::{Postgres, Sqlite}; use std::sync::Arc; use uuid::Uuid; use crate::{ Address, AddressType, AddressValidation, Credential, CredentialId, CredentialType, Identity, IdentityId, }; use self::sql_db::SqlClient; #[derive(Debug, thiserror::Error, derive_more::Display)] pub enum StoreError { SqlClientError(#[from] sqlx::Error), SerdeError(#[from] serde_json::Error), ParseError(#[from] strum::ParseError), StoreValueCannotBeParsedInvariant, IdempotentCheckAlreadyExists, } #[async_trait] pub trait Store: Send + Sync { fn get_type(&self) -> StoreType; } pub enum StoreType { Postgres { c: Arc> }, Sqlite { c: Arc> }, } #[async_trait] pub(crate) trait Storable<'a> { type Item; type Lens; async fn write(&self, store: Arc) -> Result<(), StoreError>; async fn find( store: Arc, lens: &'a Self::Lens, ) -> Result, StoreError>; } pub(crate) trait Lens {} pub(crate) struct AddressLens<'a> { pub id: Option<&'a Uuid>, pub t: Option<&'a AddressType>, } impl<'a> Lens for AddressLens<'a> {} pub(crate) struct AddressValidationLens<'a> { pub id: Option<&'a Uuid>, } impl<'a> Lens for AddressValidationLens<'a> {} pub(crate) struct IdentityLens<'a> { pub id: Option<&'a Uuid>, pub address_type: Option<&'a AddressType>, pub validated_address: Option, } impl<'a> Lens for IdentityLens<'a> {} pub(crate) struct CredentialLens<'a> { pub id: Option, pub identity_id: Option, pub t: Option<&'a CredentialType>, } impl<'a> Lens for CredentialLens<'a> {} #[async_trait] impl<'a> Storable<'a> for Address { type Item = Address; type Lens = AddressLens<'a>; async fn write(&self, store: Arc) -> Result<(), StoreError> { match store.get_type() { StoreType::Postgres { c } => c.write_address(self).await?, StoreType::Sqlite { c } => c.write_address(self).await?, } Ok(()) } async fn find( store: Arc, lens: &'a Self::Lens, ) -> Result, StoreError> { let typ = lens.t.map(|at| at.to_string()); let typ = typ.as_deref(); let val = lens.t.and_then(|at| at.get_value()); let val = val.as_deref(); Ok(match store.get_type() { StoreType::Postgres { c } => c.find_address(lens.id, typ, val).await?, StoreType::Sqlite { c } => c.find_address(lens.id, typ, val).await?, }) } } #[async_trait] impl<'a> Storable<'a> for AddressValidation { type Item = AddressValidation; type Lens = AddressValidationLens<'a>; async fn write(&self, store: Arc) -> Result<(), StoreError> { match store.get_type() { StoreType::Sqlite { c } => c.write_address_validation(self).await?, StoreType::Postgres { c } => c.write_address_validation(self).await?, } Ok(()) } async fn find( store: Arc, lens: &'a Self::Lens, ) -> Result, StoreError> { Ok(match store.get_type() { StoreType::Postgres { c } => c.find_address_validation(lens.id).await?, StoreType::Sqlite { c } => c.find_address_validation(lens.id).await?, }) } } #[async_trait] impl<'a> Storable<'a> for Identity { type Item = Identity; type Lens = IdentityLens<'a>; async fn write(&self, store: Arc) -> Result<(), StoreError> { match store.get_type() { StoreType::Postgres { c } => c.write_identity(self).await?, StoreType::Sqlite { c } => c.write_identity(self).await?, } Ok(()) } async fn find( store: Arc, lens: &'a Self::Lens, ) -> Result, StoreError> { let val = lens.address_type.and_then(|at| at.get_value()); let val = val.as_deref(); Ok(match store.get_type() { StoreType::Postgres { c } => { c.find_identity(lens.id, val, lens.validated_address) .await? } StoreType::Sqlite { c } => { c.find_identity(lens.id, val, lens.validated_address) .await? } }) } } #[async_trait] impl<'a> Storable<'a> for Credential { type Item = Credential; type Lens = CredentialLens<'a>; async fn write(&self, store: Arc) -> Result<(), StoreError> { match store.get_type() { StoreType::Postgres { c } => c.write_credential(self).await?, StoreType::Sqlite { c } => c.write_credential(self).await?, } Ok(()) } async fn find( store: Arc, lens: &'a Self::Lens, ) -> Result, StoreError> { Ok(match store.get_type() { StoreType::Postgres { c } => { c.find_credential(lens.id, lens.identity_id, lens.t).await? } StoreType::Sqlite { c } => c.find_credential(lens.id, lens.identity_id, lens.t).await?, }) } }