systemprompt_identifiers/
actor.rs1use std::fmt;
12
13use serde::{Deserialize, Serialize};
14
15use crate::UserId;
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub struct Actor {
19 pub user_id: UserId,
20 pub kind: ActorKind,
21}
22
23impl Actor {
24 #[must_use]
25 pub const fn user(user_id: UserId) -> Self {
26 Self {
27 user_id,
28 kind: ActorKind::User,
29 }
30 }
31
32 #[must_use]
36 pub const fn anonymous(user_id: UserId) -> Self {
37 Self {
38 user_id,
39 kind: ActorKind::Anonymous,
40 }
41 }
42
43 #[must_use]
47 pub const fn system(user_id: UserId) -> Self {
48 Self {
49 user_id,
50 kind: ActorKind::System,
51 }
52 }
53
54 #[must_use]
55 pub fn job(user_id: UserId, job_name: impl Into<String>) -> Self {
56 Self {
57 user_id,
58 kind: ActorKind::Job {
59 job_name: job_name.into(),
60 },
61 }
62 }
63
64 #[must_use]
65 pub fn mcp(user_id: UserId, server_name: impl Into<String>) -> Self {
66 Self {
67 user_id,
68 kind: ActorKind::Mcp {
69 server_name: server_name.into(),
70 },
71 }
72 }
73
74 #[must_use]
78 pub fn agent(user_id: UserId, agent_id: impl Into<String>) -> Self {
79 Self {
80 user_id,
81 kind: ActorKind::Agent {
82 agent_id: agent_id.into(),
83 },
84 }
85 }
86
87 #[must_use]
88 pub fn audit_columns(&self) -> (&str, &str) {
89 (self.kind.as_str(), self.kind.actor_id(&self.user_id))
90 }
91
92 #[must_use]
93 pub fn from_tool_name(user_id: UserId, agent_id: Option<&str>, tool_name: &str) -> Self {
94 if let Some(rest) = tool_name.strip_prefix("mcp__")
95 && let Some(server) = rest.split("__").next()
96 && !server.is_empty()
97 {
98 return Self::mcp(user_id, server);
99 }
100 match agent_id {
101 Some(id) if !id.is_empty() => Self::agent(user_id, id),
102 _ => Self::user(user_id),
103 }
104 }
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108#[serde(tag = "kind", rename_all = "snake_case")]
109pub enum ActorKind {
110 User,
111 Anonymous,
112 System,
113 Job { job_name: String },
114 Mcp { server_name: String },
115 Agent { agent_id: String },
116}
117
118impl ActorKind {
119 #[must_use]
120 pub const fn as_str(&self) -> &'static str {
121 match self {
122 Self::User => "user",
123 Self::Anonymous => "anonymous",
124 Self::System => "system",
125 Self::Job { .. } => "job",
126 Self::Mcp { .. } => "mcp",
127 Self::Agent { .. } => "agent",
128 }
129 }
130
131 #[must_use]
132 pub fn actor_id<'a>(&'a self, user_id: &'a UserId) -> &'a str {
133 match self {
134 Self::User | Self::Anonymous | Self::System => user_id.as_str(),
135 Self::Job { job_name } => job_name.as_str(),
136 Self::Mcp { server_name } => server_name.as_str(),
137 Self::Agent { agent_id } => agent_id.as_str(),
138 }
139 }
140}
141
142impl ActorKind {
143 #[must_use]
144 pub const fn tag(&self) -> ActorKindTag {
145 match self {
146 Self::User => ActorKindTag::User,
147 Self::Anonymous => ActorKindTag::Anonymous,
148 Self::System => ActorKindTag::System,
149 Self::Job { .. } => ActorKindTag::Job,
150 Self::Mcp { .. } => ActorKindTag::Mcp,
151 Self::Agent { .. } => ActorKindTag::Agent,
152 }
153 }
154}
155
156impl fmt::Display for ActorKind {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 f.write_str(self.as_str())
159 }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
169#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
170#[cfg_attr(feature = "sqlx", sqlx(type_name = "TEXT", rename_all = "snake_case"))]
171#[serde(rename_all = "snake_case")]
172pub enum ActorKindTag {
173 User,
174 Anonymous,
175 System,
176 Job,
177 Mcp,
178 Agent,
179}
180
181impl ActorKindTag {
182 #[must_use]
183 pub const fn as_str(self) -> &'static str {
184 match self {
185 Self::User => "user",
186 Self::Anonymous => "anonymous",
187 Self::System => "system",
188 Self::Job => "job",
189 Self::Mcp => "mcp",
190 Self::Agent => "agent",
191 }
192 }
193}
194
195impl fmt::Display for ActorKindTag {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 f.write_str(self.as_str())
198 }
199}