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