use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct TenantId(String);
impl TenantId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for TenantId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TenantContext {
tenant_id: TenantId,
name: String,
tier: TenantTier,
active: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TenantTier {
Free,
Pro,
Enterprise,
}
impl TenantContext {
pub fn new(tenant_id: TenantId, name: impl Into<String>, tier: TenantTier) -> Self {
Self {
tenant_id,
name: name.into(),
tier,
active: true,
}
}
pub fn tenant_id(&self) -> &TenantId {
&self.tenant_id
}
pub fn name(&self) -> &str {
&self.name
}
pub fn tier(&self) -> TenantTier {
self.tier
}
pub fn is_active(&self) -> bool {
self.active
}
pub fn deactivate(mut self) -> Self {
self.active = false;
self
}
}
pub struct TenantIsolation {
tenants: BTreeSet<TenantId>,
}
impl TenantIsolation {
pub fn new() -> Self {
Self {
tenants: BTreeSet::new(),
}
}
pub fn register(&mut self, tenant_id: TenantId) -> crate::error::DoDResult<()> {
self.tenants.insert(tenant_id);
Ok(())
}
pub fn exists(&self, tenant_id: &TenantId) -> bool {
self.tenants.contains(tenant_id)
}
pub fn verify_isolation(
&self, tenant_id: &TenantId, _items: &[&str],
) -> crate::error::DoDResult<()> {
if !self.exists(tenant_id) {
return Err(crate::error::DoDError::TenantIsolation(
"tenant not registered".to_string(),
));
}
Ok(())
}
pub fn all_tenants(&self) -> Vec<&TenantId> {
self.tenants.iter().collect()
}
pub fn tenant_count(&self) -> usize {
self.tenants.len()
}
}
impl Default for TenantIsolation {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tenant_context() {
let tenant = TenantContext::new(
TenantId::new("tenant-1"),
"Acme Corp",
TenantTier::Enterprise,
);
assert_eq!(tenant.name(), "Acme Corp");
assert_eq!(tenant.tier(), TenantTier::Enterprise);
assert!(tenant.is_active());
}
#[test]
fn test_tenant_isolation() -> crate::error::DoDResult<()> {
let mut isolation = TenantIsolation::new();
let tenant_id = TenantId::new("tenant-1");
isolation.register(tenant_id.clone())?;
assert!(isolation.exists(&tenant_id));
Ok(())
}
}