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