1#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct RlsContext {
32 pub operator_id: String,
35
36 pub agent_id: String,
39
40 pub is_super_admin: bool,
43}
44
45impl RlsContext {
46 pub fn operator(operator_id: &str) -> Self {
48 Self {
49 operator_id: operator_id.to_string(),
50 agent_id: String::new(),
51 is_super_admin: false,
52 }
53 }
54
55 pub fn agent(agent_id: &str) -> Self {
57 Self {
58 operator_id: String::new(),
59 agent_id: agent_id.to_string(),
60 is_super_admin: false,
61 }
62 }
63
64 pub fn operator_and_agent(operator_id: &str, agent_id: &str) -> Self {
67 Self {
68 operator_id: operator_id.to_string(),
69 agent_id: agent_id.to_string(),
70 is_super_admin: false,
71 }
72 }
73
74 pub fn super_admin() -> Self {
76 Self {
77 operator_id: String::new(),
78 agent_id: String::new(),
79 is_super_admin: true,
80 }
81 }
82
83 pub fn has_operator(&self) -> bool {
85 !self.operator_id.is_empty()
86 }
87
88 pub fn has_agent(&self) -> bool {
90 !self.agent_id.is_empty()
91 }
92
93 pub fn bypasses_rls(&self) -> bool {
95 self.is_super_admin
96 }
97}
98
99impl std::fmt::Display for RlsContext {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 if self.is_super_admin {
102 write!(f, "RlsContext(super_admin)")
103 } else if !self.operator_id.is_empty() && !self.agent_id.is_empty() {
104 write!(f, "RlsContext(op={}, ag={})", self.operator_id, self.agent_id)
105 } else if !self.operator_id.is_empty() {
106 write!(f, "RlsContext(op={})", self.operator_id)
107 } else if !self.agent_id.is_empty() {
108 write!(f, "RlsContext(ag={})", self.agent_id)
109 } else {
110 write!(f, "RlsContext(none)")
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_operator_context() {
121 let ctx = RlsContext::operator("op-123");
122 assert_eq!(ctx.operator_id, "op-123");
123 assert!(ctx.agent_id.is_empty());
124 assert!(!ctx.is_super_admin);
125 assert!(ctx.has_operator());
126 assert!(!ctx.has_agent());
127 assert!(!ctx.bypasses_rls());
128 }
129
130 #[test]
131 fn test_agent_context() {
132 let ctx = RlsContext::agent("ag-456");
133 assert!(ctx.operator_id.is_empty());
134 assert_eq!(ctx.agent_id, "ag-456");
135 assert!(ctx.has_agent());
136 assert!(!ctx.has_operator());
137 }
138
139 #[test]
140 fn test_super_admin() {
141 let ctx = RlsContext::super_admin();
142 assert!(ctx.is_super_admin);
143 assert!(ctx.bypasses_rls());
144 }
145
146 #[test]
147 fn test_operator_and_agent() {
148 let ctx = RlsContext::operator_and_agent("op-1", "ag-2");
149 assert!(ctx.has_operator());
150 assert!(ctx.has_agent());
151 assert!(!ctx.bypasses_rls());
152 }
153
154 #[test]
155 fn test_display() {
156 assert_eq!(RlsContext::super_admin().to_string(), "RlsContext(super_admin)");
157 assert_eq!(RlsContext::operator("x").to_string(), "RlsContext(op=x)");
158 assert_eq!(RlsContext::agent("y").to_string(), "RlsContext(ag=y)");
159 assert_eq!(
160 RlsContext::operator_and_agent("x", "y").to_string(),
161 "RlsContext(op=x, ag=y)"
162 );
163 }
164
165 #[test]
166 fn test_equality() {
167 let a = RlsContext::operator("op-1");
168 let b = RlsContext::operator("op-1");
169 let c = RlsContext::operator("op-2");
170 assert_eq!(a, b);
171 assert_ne!(a, c);
172 }
173}