use std::sync::Arc;
use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use crate::ports::Database;
use crate::problem::Problem;
use crate::tenant::{Tenancy, Tenant};
#[derive(Clone)]
pub(crate) struct ResolvedTenant {
pub(crate) tenant: Tenant,
pub(crate) tenancy: Tenancy,
pub(crate) db: Arc<dyn Database>,
}
#[derive(Clone)]
pub struct TenantConn {
tenant: Tenant,
tenancy: Tenancy,
db: Arc<dyn Database>,
}
impl TenantConn {
#[must_use]
pub fn tenant(&self) -> &Tenant {
&self.tenant
}
#[must_use]
pub fn tenancy(&self) -> Tenancy {
self.tenancy
}
}
impl std::fmt::Debug for TenantConn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TenantConn")
.field("tenant", &self.tenant.id())
.field("tenancy", &self.tenancy)
.finish_non_exhaustive()
}
}
#[async_trait::async_trait]
impl Database for TenantConn {
async fn execute(&self, stmt: &crate::ports::Statement) -> Result<u64, crate::ports::DbError> {
self.db.execute(stmt).await
}
async fn query(
&self,
stmt: &crate::ports::Statement,
) -> Result<crate::ports::Rows, crate::ports::DbError> {
self.db.query(stmt).await
}
async fn batch_atomic(
&self,
stmts: &[crate::ports::Statement],
) -> Result<(), crate::ports::DbError> {
self.db.batch_atomic(stmts).await
}
}
impl<S> FromRequestParts<S> for TenantConn
where
S: Send + Sync,
{
type Rejection = Problem;
fn from_request_parts(
parts: &mut Parts,
_state: &S,
) -> impl std::future::Future<Output = Result<Self, Self::Rejection>> + Send {
std::future::ready(
parts
.extensions
.get::<ResolvedTenant>()
.map(|resolved| Self {
tenant: resolved.tenant.clone(),
tenancy: resolved.tenancy,
db: Arc::clone(&resolved.db),
})
.ok_or_else(Problem::internal),
)
}
}