reinhardt_query/dcl/set_role.rs
1//! SET ROLE statement builder
2//!
3//! This module provides a fluent API for building SET ROLE statements for both
4//! PostgreSQL and MySQL databases.
5//!
6//! # PostgreSQL
7//!
8//! PostgreSQL supports:
9//! - `SET ROLE role_name` - Set current role
10//! - `SET ROLE NONE` - Reset to session user
11//!
12//! # MySQL
13//!
14//! MySQL supports:
15//! - `SET ROLE role_name` - Activate specific role
16//! - `SET ROLE NONE` - Deactivate all roles
17//! - `SET ROLE ALL` - Activate all granted roles
18//! - `SET ROLE ALL EXCEPT role1, role2` - Activate all except specified roles
19//! - `SET ROLE DEFAULT` - Activate default roles
20//!
21//! # Examples
22//!
23//! Set specific role:
24//!
25//! ```
26//! use reinhardt_query::dcl::{SetRoleStatement, RoleTarget};
27//!
28//! let stmt = SetRoleStatement::new()
29//! .role(RoleTarget::Named("admin".to_string()));
30//! ```
31//!
32//! Reset role (PostgreSQL):
33//!
34//! ```
35//! use reinhardt_query::dcl::{SetRoleStatement, RoleTarget};
36//!
37//! let stmt = SetRoleStatement::new()
38//! .role(RoleTarget::None);
39//! ```
40//!
41//! Activate all roles except specific ones (MySQL):
42//!
43//! ```
44//! use reinhardt_query::dcl::{SetRoleStatement, RoleTarget};
45//!
46//! let stmt = SetRoleStatement::new()
47//! .role(RoleTarget::AllExcept(vec!["restricted_role".to_string()]));
48//! ```
49
50/// Role target for SET ROLE statement
51///
52/// This enum specifies what role(s) to activate or deactivate.
53///
54/// # Variants
55///
56/// - `` `Named` `` - Set to a specific role (PostgreSQL & MySQL)
57/// - `` `None` `` - Deactivate all roles (PostgreSQL & MySQL)
58/// - `` `All` `` - Activate all granted roles (MySQL only)
59/// - `` `AllExcept` `` - Activate all except specified roles (MySQL only)
60/// - `` `Default` `` - Activate default roles (MySQL only)
61#[derive(Debug, Clone, PartialEq)]
62pub enum RoleTarget {
63 /// SET ROLE role_name
64 Named(String),
65 /// SET ROLE NONE
66 None,
67 /// SET ROLE ALL (MySQL only)
68 All,
69 /// SET ROLE ALL EXCEPT role_list (MySQL only)
70 AllExcept(Vec<String>),
71 /// SET ROLE DEFAULT (MySQL only)
72 Default,
73}
74
75/// SET ROLE statement builder
76///
77/// This struct provides a fluent API for building SET ROLE statements.
78///
79/// # PostgreSQL
80///
81/// PostgreSQL SET ROLE changes the current role for the session.
82/// Supports:
83/// - Named role: `SET ROLE role_name`
84/// - Reset: `SET ROLE NONE`
85///
86/// # MySQL
87///
88/// MySQL SET ROLE activates roles for the session.
89/// Supports:
90/// - Named role: `SET ROLE role_name`
91/// - None: `SET ROLE NONE`
92/// - All: `SET ROLE ALL`
93/// - All except: `SET ROLE ALL EXCEPT role_list`
94/// - Default: `SET ROLE DEFAULT`
95///
96/// # Examples
97///
98/// Set to a specific role:
99///
100/// ```
101/// use reinhardt_query::dcl::{SetRoleStatement, RoleTarget};
102///
103/// let stmt = SetRoleStatement::new()
104/// .role(RoleTarget::Named("admin".to_string()));
105/// ```
106///
107/// Deactivate all roles:
108///
109/// ```
110/// use reinhardt_query::dcl::{SetRoleStatement, RoleTarget};
111///
112/// let stmt = SetRoleStatement::new()
113/// .role(RoleTarget::None);
114/// ```
115///
116/// Activate all granted roles (MySQL):
117///
118/// ```
119/// use reinhardt_query::dcl::{SetRoleStatement, RoleTarget};
120///
121/// let stmt = SetRoleStatement::new()
122/// .role(RoleTarget::All);
123/// ```
124///
125/// Activate all except specific roles (MySQL):
126///
127/// ```
128/// use reinhardt_query::dcl::{SetRoleStatement, RoleTarget};
129///
130/// let stmt = SetRoleStatement::new()
131/// .role(RoleTarget::AllExcept(vec!["restricted".to_string()]));
132/// ```
133#[derive(Debug, Clone, Default)]
134pub struct SetRoleStatement {
135 /// Role target
136 pub target: Option<RoleTarget>,
137}
138
139impl SetRoleStatement {
140 /// Create a new SET ROLE statement
141 ///
142 /// # Examples
143 ///
144 /// ```
145 /// use reinhardt_query::dcl::SetRoleStatement;
146 ///
147 /// let stmt = SetRoleStatement::new();
148 /// ```
149 pub fn new() -> Self {
150 Self::default()
151 }
152
153 /// Set the role target
154 ///
155 /// # Examples
156 ///
157 /// ```
158 /// use reinhardt_query::dcl::{SetRoleStatement, RoleTarget};
159 ///
160 /// let stmt = SetRoleStatement::new()
161 /// .role(RoleTarget::Named("admin".to_string()));
162 /// ```
163 pub fn role(mut self, target: RoleTarget) -> Self {
164 self.target = Some(target);
165 self
166 }
167
168 /// Validate the SET ROLE statement
169 ///
170 /// # Validation Rules
171 ///
172 /// 1. Role target must be specified
173 /// 2. For Named variant, role name cannot be empty
174 /// 3. For AllExcept variant, exception list cannot be empty
175 ///
176 /// # Examples
177 ///
178 /// ```
179 /// use reinhardt_query::dcl::{SetRoleStatement, RoleTarget};
180 ///
181 /// let stmt = SetRoleStatement::new()
182 /// .role(RoleTarget::Named("admin".to_string()));
183 ///
184 /// assert!(stmt.validate().is_ok());
185 /// ```
186 ///
187 /// ```
188 /// use reinhardt_query::dcl::SetRoleStatement;
189 ///
190 /// let stmt = SetRoleStatement::new();
191 /// assert!(stmt.validate().is_err());
192 /// ```
193 pub fn validate(&self) -> Result<(), String> {
194 match &self.target {
195 None => Err("Role target must be specified".to_string()),
196 Some(RoleTarget::Named(name)) if name.trim().is_empty() => {
197 Err("Role name cannot be empty or whitespace only".to_string())
198 }
199 Some(RoleTarget::AllExcept(list)) if list.is_empty() => {
200 Err("AllExcept role list cannot be empty".to_string())
201 }
202 Some(RoleTarget::AllExcept(list)) => {
203 for role in list {
204 if role.trim().is_empty() {
205 return Err(
206 "Role name in AllExcept list cannot be empty or whitespace only"
207 .to_string(),
208 );
209 }
210 }
211 Ok(())
212 }
213 _ => Ok(()),
214 }
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_set_role_new() {
224 let stmt = SetRoleStatement::new();
225 assert!(stmt.target.is_none());
226 }
227
228 #[test]
229 fn test_set_role_named() {
230 let stmt = SetRoleStatement::new().role(RoleTarget::Named("admin".to_string()));
231 assert!(matches!(stmt.target, Some(RoleTarget::Named(_))));
232 assert!(stmt.validate().is_ok());
233 }
234
235 #[test]
236 fn test_set_role_none() {
237 let stmt = SetRoleStatement::new().role(RoleTarget::None);
238 assert!(matches!(stmt.target, Some(RoleTarget::None)));
239 assert!(stmt.validate().is_ok());
240 }
241
242 #[test]
243 fn test_set_role_all() {
244 let stmt = SetRoleStatement::new().role(RoleTarget::All);
245 assert!(matches!(stmt.target, Some(RoleTarget::All)));
246 assert!(stmt.validate().is_ok());
247 }
248
249 #[test]
250 fn test_set_role_all_except() {
251 let stmt =
252 SetRoleStatement::new().role(RoleTarget::AllExcept(vec!["restricted".to_string()]));
253 assert!(matches!(stmt.target, Some(RoleTarget::AllExcept(_))));
254 assert!(stmt.validate().is_ok());
255 }
256
257 #[test]
258 fn test_set_role_default() {
259 let stmt = SetRoleStatement::new().role(RoleTarget::Default);
260 assert!(matches!(stmt.target, Some(RoleTarget::Default)));
261 assert!(stmt.validate().is_ok());
262 }
263
264 #[test]
265 fn test_set_role_validation_no_target() {
266 let stmt = SetRoleStatement::new();
267 assert!(stmt.validate().is_err());
268 }
269
270 #[test]
271 fn test_set_role_validation_empty_name() {
272 let stmt = SetRoleStatement::new().role(RoleTarget::Named("".to_string()));
273 assert!(stmt.validate().is_err());
274 }
275
276 #[test]
277 fn test_set_role_validation_empty_except_list() {
278 let stmt = SetRoleStatement::new().role(RoleTarget::AllExcept(vec![]));
279 assert!(stmt.validate().is_err());
280 }
281}