use serde::{Deserialize, Serialize};
use std::fmt;
macro_rules! identity_id {
($name:ident, $documentation:literal) => {
#[doc = $documentation]
#[derive(
Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize,
)]
#[serde(transparent)]
pub struct $name(String);
impl $name {
#[must_use]
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl From<&str> for $name {
fn from(value: &str) -> Self {
Self::new(value)
}
}
impl From<String> for $name {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl AsRef<str> for $name {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl fmt::Display for $name {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
};
}
identity_id!(
TenantId,
"Opaque identifier for the tenant that owns an execution or data object."
);
identity_id!(
ProjectId,
"Opaque identifier for a project within a tenant."
);
identity_id!(
AgentId,
"Opaque identifier for an agent execution identity."
);
identity_id!(
HumanActorId,
"Opaque identifier for the human actor responsible for an action."
);
identity_id!(
ServiceId,
"Opaque identifier for a service acting on behalf of a tenant."
);
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum IdentityScope {
#[default]
Local,
Tenant,
CrossTenant,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct IdentityContext {
pub tenant: TenantId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub project: Option<ProjectId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent: Option<AgentId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub human_actor: Option<HumanActorId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub service: Option<ServiceId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session: Option<String>,
}
impl IdentityContext {
#[must_use]
pub fn new(tenant: impl Into<TenantId>) -> Self {
Self {
tenant: tenant.into(),
project: None,
agent: None,
human_actor: None,
service: None,
session: None,
}
}
#[must_use]
pub fn with_project(mut self, project: impl Into<ProjectId>) -> Self {
self.project = Some(project.into());
self
}
#[must_use]
pub fn with_agent(mut self, agent: impl Into<AgentId>) -> Self {
self.agent = Some(agent.into());
self
}
#[must_use]
pub fn with_human_actor(mut self, human_actor: impl Into<HumanActorId>) -> Self {
self.human_actor = Some(human_actor.into());
self
}
#[must_use]
pub fn with_service(mut self, service: impl Into<ServiceId>) -> Self {
self.service = Some(service.into());
self
}
#[must_use]
pub fn with_session(mut self, session: impl Into<String>) -> Self {
self.session = Some(session.into());
self
}
#[must_use]
pub fn tenant_id(&self) -> &TenantId {
&self.tenant
}
#[must_use]
pub fn scope(&self) -> IdentityScope {
if self.tenant.is_empty() {
IdentityScope::Local
} else {
IdentityScope::Tenant
}
}
}