use crate::kernel::{TenantId, UserId};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceLimits {
pub max_steps: u32,
pub max_tokens: u32,
pub max_wall_time_ms: u64,
pub max_memory_mb: Option<u32>,
pub max_concurrent_executions: Option<u32>,
}
impl Default for ResourceLimits {
fn default() -> Self {
Self {
max_steps: 100,
max_tokens: 100_000,
max_wall_time_ms: 300_000, max_memory_mb: None,
max_concurrent_executions: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TenantContext {
pub tenant_id: TenantId,
pub user_id: Option<UserId>,
pub limits: ResourceLimits,
pub features: HashMap<String, bool>,
pub metadata: HashMap<String, serde_json::Value>,
}
impl TenantContext {
pub fn new(tenant_id: TenantId) -> Self {
Self {
tenant_id,
user_id: None,
limits: ResourceLimits::default(),
features: HashMap::new(),
metadata: HashMap::new(),
}
}
pub fn with_user(mut self, user_id: UserId) -> Self {
self.user_id = Some(user_id);
self
}
pub fn with_limits(mut self, limits: ResourceLimits) -> Self {
self.limits = limits;
self
}
pub fn with_feature(mut self, feature: impl Into<String>, enabled: bool) -> Self {
self.features.insert(feature.into(), enabled);
self
}
pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
self.metadata.insert(key.into(), value);
self
}
pub fn is_feature_enabled(&self, feature: &str) -> bool {
self.features.get(feature).copied().unwrap_or(false)
}
pub fn tenant_id(&self) -> &TenantId {
&self.tenant_id
}
pub fn user_id(&self) -> Option<&UserId> {
self.user_id.as_ref()
}
pub fn child_context(&self, user_id: Option<UserId>) -> Self {
Self {
tenant_id: self.tenant_id.clone(),
user_id: user_id.or_else(|| self.user_id.clone()),
limits: self.limits.clone(),
features: self.features.clone(),
metadata: HashMap::new(), }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tenant_context_required_id() {
let tenant = TenantContext::new(TenantId::from("tenant_123"));
assert_eq!(tenant.tenant_id().as_str(), "tenant_123");
}
#[test]
fn test_tenant_context_with_user() {
let tenant =
TenantContext::new(TenantId::from("tenant_123")).with_user(UserId::from("usr_456"));
assert_eq!(tenant.user_id().unwrap().as_str(), "usr_456");
}
#[test]
fn test_feature_flags() {
let tenant = TenantContext::new(TenantId::from("tenant_123"))
.with_feature("beta_tools", true)
.with_feature("experimental", false);
assert!(tenant.is_feature_enabled("beta_tools"));
assert!(!tenant.is_feature_enabled("experimental"));
assert!(!tenant.is_feature_enabled("nonexistent"));
}
#[test]
fn test_child_context() {
let parent = TenantContext::new(TenantId::from("tenant_123"))
.with_user(UserId::from("usr_456"))
.with_feature("beta", true);
let child = parent.child_context(Some(UserId::from("usr_789")));
assert_eq!(child.tenant_id().as_str(), "tenant_123");
assert_eq!(child.user_id().unwrap().as_str(), "usr_789");
assert!(child.is_feature_enabled("beta"));
}
}