diff options
Diffstat (limited to 'crates/secd/src/client/store/mod.rs')
| -rw-r--r-- | crates/secd/src/client/store/mod.rs | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/crates/secd/src/client/store/mod.rs b/crates/secd/src/client/store/mod.rs new file mode 100644 index 0000000..b93fd84 --- /dev/null +++ b/crates/secd/src/client/store/mod.rs @@ -0,0 +1,190 @@ +pub(crate) mod sql_db; + +use email_address::EmailAddress; +use sqlx::{Postgres, Sqlite}; +use std::sync::Arc; +use time::OffsetDateTime; +use uuid::Uuid; + +use crate::{ + util, Address, AddressType, AddressValidation, Identity, IdentityId, PhoneNumber, Session, + SessionToken, +}; + +use self::sql_db::SqlClient; + +#[derive(Debug, thiserror::Error, derive_more::Display)] +pub enum StoreError { + SqlClientError(#[from] sqlx::Error), + StoreValueCannotBeParsedInvariant, + IdempotentCheckAlreadyExists, +} + +#[async_trait::async_trait(?Send)] +pub trait Store { + fn get_type(&self) -> StoreType; +} + +pub enum StoreType { + Postgres { c: Arc<SqlClient<Postgres>> }, + Sqlite { c: Arc<SqlClient<Sqlite>> }, +} + +#[async_trait::async_trait(?Send)] +pub(crate) trait Storable<'a> { + type Item; + type Lens; + + async fn write(&self, store: Arc<dyn Store>) -> Result<(), StoreError>; + async fn find( + store: Arc<dyn Store>, + lens: &'a Self::Lens, + ) -> Result<Vec<Self::Item>, 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<bool>, + pub session_token_hash: Option<Vec<u8>>, +} +impl<'a> Lens for IdentityLens<'a> {} + +pub(crate) struct SessionLens<'a> { + pub token_hash: Option<&'a Vec<u8>>, + pub identity_id: Option<&'a IdentityId>, +} +impl<'a> Lens for SessionLens<'a> {} + +#[async_trait::async_trait(?Send)] +impl<'a> Storable<'a> for Address { + type Item = Address; + type Lens = AddressLens<'a>; + + async fn write(&self, store: Arc<dyn Store>) -> 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<dyn Store>, + lens: &'a Self::Lens, + ) -> Result<Vec<Self::Item>, StoreError> { + let typ = lens.t.map(|at| at.to_string().clone()); + 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::async_trait(?Send)] +impl<'a> Storable<'a> for AddressValidation { + type Item = AddressValidation; + type Lens = AddressValidationLens<'a>; + + async fn write(&self, store: Arc<dyn Store>) -> 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<dyn Store>, + lens: &'a Self::Lens, + ) -> Result<Vec<Self::Item>, 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::async_trait(?Send)] +impl<'a> Storable<'a> for Identity { + type Item = Identity; + type Lens = IdentityLens<'a>; + + async fn write(&self, store: Arc<dyn Store>) -> 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<dyn Store>, + lens: &'a Self::Lens, + ) -> Result<Vec<Self::Item>, 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, + &lens.session_token_hash, + ) + .await? + } + StoreType::Sqlite { c } => { + c.find_identity( + lens.id, + val, + lens.validated_address, + &lens.session_token_hash, + ) + .await? + } + }) + } +} + +#[async_trait::async_trait(?Send)] +impl<'a> Storable<'a> for Session { + type Item = Session; + type Lens = SessionLens<'a>; + + async fn write(&self, store: Arc<dyn Store>) -> Result<(), StoreError> { + let token_hash = util::hash(&self.token); + match store.get_type() { + StoreType::Postgres { c } => c.write_session(self, &token_hash).await?, + StoreType::Sqlite { c } => c.write_session(self, &token_hash).await?, + } + Ok(()) + } + async fn find( + store: Arc<dyn Store>, + lens: &'a Self::Lens, + ) -> Result<Vec<Self::Item>, StoreError> { + let token = lens.token_hash.map(|t| t.clone()).unwrap_or(vec![]); + + Ok(match store.get_type() { + StoreType::Postgres { c } => c.find_session(token, lens.identity_id).await?, + StoreType::Sqlite { c } => c.find_session(token, lens.identity_id).await?, + }) + } +} |
