use tonic::{metadata::MetadataMap, Status}; #[derive(Debug, Clone)] pub struct TenantContext { pub org_id: String, pub project_id: String, } fn metadata_value(metadata: &MetadataMap, key: &str) -> Option { metadata .get(key) .and_then(|value| value.to_str().ok()) .map(|value| value.trim().to_string()) .filter(|value| !value.is_empty()) } fn org_from_metadata(metadata: &MetadataMap) -> Option { metadata_value(metadata, "org-id") .or_else(|| metadata_value(metadata, "x-org-id")) .or_else(|| metadata_value(metadata, "org_id")) } fn project_from_metadata(metadata: &MetadataMap) -> Option { metadata_value(metadata, "project-id") .or_else(|| metadata_value(metadata, "x-project-id")) .or_else(|| metadata_value(metadata, "project_id")) } pub fn resolve_org( metadata: &MetadataMap, org_id: Option, ) -> Result { org_id .filter(|value| !value.is_empty()) .or_else(|| org_from_metadata(metadata)) .ok_or_else(|| Status::invalid_argument("org_id is required")) } pub fn resolve_org_project_optional( metadata: &MetadataMap, org_id: Option, project_id: Option, ) -> Result<(String, Option), Status> { let org_id = resolve_org(metadata, org_id)?; let project_id = project_id .filter(|value| !value.is_empty()) .or_else(|| project_from_metadata(metadata)); Ok((org_id, project_id)) } pub fn resolve_tenant( metadata: &MetadataMap, org_id: Option, project_id: Option, ) -> Result { let (org_id, project_id) = resolve_org_project_optional(metadata, org_id, project_id)?; let project_id = project_id.ok_or_else(|| Status::invalid_argument("project_id is required"))?; Ok(TenantContext { org_id, project_id }) }