Skip to main content

reinhardt_query/dcl/
drop_user.rs

1//! DROP USER statement builder
2//!
3//! This module provides a fluent API for building DROP USER statements for both
4//! PostgreSQL and MySQL databases.
5//!
6//! # PostgreSQL
7//!
8//! PostgreSQL doesn't have a separate DROP USER command; it's an alias for DROP ROLE.
9//! This builder wraps DropRoleStatement.
10//!
11//! # MySQL
12//!
13//! MySQL has a native DROP USER command that supports user@host specification.
14//!
15//! # Examples
16//!
17//! PostgreSQL example:
18//!
19//! ```
20//! use reinhardt_query::dcl::DropUserStatement;
21//!
22//! let stmt = DropUserStatement::new()
23//!     .user("app_user");
24//! ```
25//!
26//! MySQL example:
27//!
28//! ```
29//! use reinhardt_query::dcl::DropUserStatement;
30//!
31//! let stmt = DropUserStatement::new()
32//!     .user("app_user@localhost")
33//!     .if_exists(true);
34//! ```
35
36/// DROP USER statement builder
37///
38/// This struct provides a fluent API for building DROP USER statements.
39///
40/// # PostgreSQL
41///
42/// PostgreSQL DROP USER is an alias for DROP ROLE.
43///
44/// # MySQL
45///
46/// MySQL DROP USER supports:
47/// - IF EXISTS clause
48/// - User@host specification
49/// - Multiple users
50///
51/// # Examples
52///
53/// Drop a single user:
54///
55/// ```
56/// use reinhardt_query::dcl::DropUserStatement;
57///
58/// let stmt = DropUserStatement::new()
59///     .user("app_user");
60/// ```
61///
62/// Drop multiple users (MySQL):
63///
64/// ```
65/// use reinhardt_query::dcl::DropUserStatement;
66///
67/// let stmt = DropUserStatement::new()
68///     .users(vec!["user1@localhost".to_string(), "user2@localhost".to_string()])
69///     .if_exists(true);
70/// ```
71#[derive(Debug, Clone, Default)]
72pub struct DropUserStatement {
73	/// User names (with optional @host for MySQL)
74	pub user_names: Vec<String>,
75	/// IF EXISTS clause
76	pub if_exists: bool,
77}
78
79impl DropUserStatement {
80	/// Create a new DROP USER statement
81	///
82	/// # Examples
83	///
84	/// ```
85	/// use reinhardt_query::dcl::DropUserStatement;
86	///
87	/// let stmt = DropUserStatement::new();
88	/// ```
89	pub fn new() -> Self {
90		Self::default()
91	}
92
93	/// Add a single user to drop
94	///
95	/// # Examples
96	///
97	/// ```
98	/// use reinhardt_query::dcl::DropUserStatement;
99	///
100	/// let stmt = DropUserStatement::new()
101	///     .user("app_user");
102	/// ```
103	///
104	/// MySQL with host:
105	///
106	/// ```
107	/// use reinhardt_query::dcl::DropUserStatement;
108	///
109	/// let stmt = DropUserStatement::new()
110	///     .user("app_user@localhost");
111	/// ```
112	pub fn user(mut self, name: impl Into<String>) -> Self {
113		self.user_names.push(name.into());
114		self
115	}
116
117	/// Set all users to drop at once
118	///
119	/// # Examples
120	///
121	/// ```
122	/// use reinhardt_query::dcl::DropUserStatement;
123	///
124	/// let stmt = DropUserStatement::new()
125	///     .users(vec!["user1".to_string(), "user2".to_string()]);
126	/// ```
127	pub fn users(mut self, names: Vec<String>) -> Self {
128		self.user_names = names;
129		self
130	}
131
132	/// Set IF EXISTS flag
133	///
134	/// # Examples
135	///
136	/// ```
137	/// use reinhardt_query::dcl::DropUserStatement;
138	///
139	/// let stmt = DropUserStatement::new()
140	///     .user("app_user")
141	///     .if_exists(true);
142	/// ```
143	pub fn if_exists(mut self, flag: bool) -> Self {
144		self.if_exists = flag;
145		self
146	}
147
148	/// Validate the DROP USER statement
149	///
150	/// # Validation Rules
151	///
152	/// 1. At least one user must be specified
153	///
154	/// # Examples
155	///
156	/// ```
157	/// use reinhardt_query::dcl::DropUserStatement;
158	///
159	/// let stmt = DropUserStatement::new()
160	///     .user("app_user");
161	///
162	/// assert!(stmt.validate().is_ok());
163	/// ```
164	///
165	/// ```
166	/// use reinhardt_query::dcl::DropUserStatement;
167	///
168	/// let stmt = DropUserStatement::new();
169	/// assert!(stmt.validate().is_err());
170	/// ```
171	pub fn validate(&self) -> Result<(), String> {
172		if self.user_names.is_empty() {
173			return Err("At least one user must be specified".to_string());
174		}
175		// Validate each user name is non-empty after trimming whitespace
176		for (idx, user_name) in self.user_names.iter().enumerate() {
177			let trimmed = user_name.trim();
178			if trimmed.is_empty() {
179				return Err(format!(
180					"User name at index {} cannot be empty or whitespace only",
181					idx
182				));
183			}
184		}
185		Ok(())
186	}
187}
188
189#[cfg(test)]
190mod tests {
191	use super::*;
192
193	#[test]
194	fn test_drop_user_new() {
195		let stmt = DropUserStatement::new();
196		assert!(stmt.user_names.is_empty());
197		assert!(!stmt.if_exists);
198	}
199
200	#[test]
201	fn test_drop_user_basic() {
202		let stmt = DropUserStatement::new().user("app_user");
203		assert_eq!(stmt.user_names.len(), 1);
204		assert_eq!(stmt.user_names[0], "app_user");
205		assert!(stmt.validate().is_ok());
206	}
207
208	#[test]
209	fn test_drop_user_multiple() {
210		let stmt = DropUserStatement::new().user("user1").user("user2");
211		assert_eq!(stmt.user_names.len(), 2);
212	}
213
214	#[test]
215	fn test_drop_user_if_exists() {
216		let stmt = DropUserStatement::new().user("app_user").if_exists(true);
217		assert!(stmt.if_exists);
218	}
219
220	#[test]
221	fn test_drop_user_validation_empty() {
222		let stmt = DropUserStatement::new();
223		assert!(stmt.validate().is_err());
224	}
225}