aboutsummaryrefslogtreecommitdiff
path: root/crates/secd/src/client/store/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--crates/secd/src/client/store/mod.rs190
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?,
+ })
+ }
+}