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}