Skip to main content

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}