use std::sync::Arc; use async_trait::async_trait; use crate::{TenantId, WorkspaceId}; #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct TenantResolutionContext { pub workspace_id: Option, pub workspace_slug: Option, pub host: Option, } #[derive(Debug, thiserror::Error, PartialEq, Eq)] pub enum TenancyError { #[error("tenant lookup is not supported in this edition")] UnsupportedLookup, #[error("internal tenancy failure: {0}")] Internal(String), } #[async_trait] pub trait TenantController: Send + Sync { fn resolve_tenant(&self, request: &TenantResolutionContext) -> Result; async fn list_workspaces_for_tenant( &self, tenant: &TenantId, ) -> Result, TenancyError>; } pub type SharedTenantController = Arc; #[derive(Clone, Debug, PartialEq, Eq)] pub struct SingleTenantController { tenant_id: TenantId, } impl Default for SingleTenantController { fn default() -> Self { Self { tenant_id: TenantId::new("default"), } } } #[async_trait] impl TenantController for SingleTenantController { fn resolve_tenant(&self, _request: &TenantResolutionContext) -> Result { Ok(self.tenant_id.clone()) } async fn list_workspaces_for_tenant( &self, _tenant: &TenantId, ) -> Result, TenancyError> { Ok(Vec::new()) } } #[cfg(test)] mod tests { use super::{SingleTenantController, TenantController, TenantResolutionContext}; use crate::TenantId; #[tokio::test] async fn single_tenant_controller_returns_default_tenant() { let controller = SingleTenantController::default(); let tenant = controller .resolve_tenant(&TenantResolutionContext::default()) .unwrap(); let workspaces = controller .list_workspaces_for_tenant(&tenant) .await .unwrap(); assert_eq!(tenant, TenantId::new("default")); assert!(workspaces.is_empty()); } }