reinhardt_query/dcl/create_user.rs
1//! CREATE USER statement builder
2//!
3//! This module provides a fluent API for building CREATE USER statements for both
4//! PostgreSQL and MySQL databases.
5//!
6//! # PostgreSQL
7//!
8//! PostgreSQL doesn't have a separate CREATE USER command; it's an alias for CREATE ROLE WITH LOGIN.
9//! This builder wraps CreateRoleStatement with the LOGIN attribute.
10//!
11//! # MySQL
12//!
13//! MySQL has a native CREATE USER command that supports user@host specification,
14//! DEFAULT ROLE, and user options.
15//!
16//! # Examples
17//!
18//! PostgreSQL example:
19//!
20//! ```
21//! use reinhardt_query::dcl::{CreateUserStatement, RoleAttribute};
22//!
23//! let stmt = CreateUserStatement::new()
24//! .user("app_user")
25//! .attribute(RoleAttribute::Password("secret".to_string()));
26//! ```
27//!
28//! MySQL example:
29//!
30//! ```
31//! use reinhardt_query::dcl::{CreateUserStatement, UserOption};
32//!
33//! let stmt = CreateUserStatement::new()
34//! .user("app_user@localhost")
35//! .if_not_exists(true)
36//! .default_role(vec!["app_role".to_string()])
37//! .option(UserOption::Comment("Application user".to_string()));
38//! ```
39
40use super::{RoleAttribute, UserOption, validate_name};
41
42/// CREATE USER statement builder
43///
44/// This struct provides a fluent API for building CREATE USER statements.
45///
46/// # PostgreSQL
47///
48/// PostgreSQL CREATE USER is an alias for CREATE ROLE WITH LOGIN.
49/// Use the `` `attribute()` `` method to add role attributes.
50///
51/// # MySQL
52///
53/// MySQL CREATE USER supports:
54/// - IF NOT EXISTS clause
55/// - User@host specification
56/// - DEFAULT ROLE clause
57/// - User options
58///
59/// # Examples
60///
61/// Create a simple user:
62///
63/// ```
64/// use reinhardt_query::dcl::CreateUserStatement;
65///
66/// let stmt = CreateUserStatement::new()
67/// .user("app_user");
68/// ```
69///
70/// Create a user with password (PostgreSQL):
71///
72/// ```
73/// use reinhardt_query::dcl::{CreateUserStatement, RoleAttribute};
74///
75/// let stmt = CreateUserStatement::new()
76/// .user("app_user")
77/// .attribute(RoleAttribute::Password("password123".to_string()))
78/// .attribute(RoleAttribute::ConnectionLimit(5));
79/// ```
80///
81/// Create a user with default role (MySQL):
82///
83/// ```
84/// use reinhardt_query::dcl::{CreateUserStatement, UserOption};
85///
86/// let stmt = CreateUserStatement::new()
87/// .user("app_user@localhost")
88/// .if_not_exists(true)
89/// .default_role(vec!["app_role".to_string()])
90/// .option(UserOption::Comment("My application user".to_string()));
91/// ```
92#[derive(Debug, Clone, Default)]
93pub struct CreateUserStatement {
94 /// User name (with optional @host for MySQL)
95 pub user_name: String,
96 /// IF NOT EXISTS clause (MySQL only)
97 pub if_not_exists: bool,
98 /// PostgreSQL role attributes (PostgreSQL only)
99 pub attributes: Vec<RoleAttribute>,
100 /// MySQL DEFAULT ROLE clause (MySQL only)
101 pub default_roles: Vec<String>,
102 /// MySQL user options
103 pub options: Vec<UserOption>,
104}
105
106impl CreateUserStatement {
107 /// Create a new CREATE USER statement
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// use reinhardt_query::dcl::CreateUserStatement;
113 ///
114 /// let stmt = CreateUserStatement::new();
115 /// ```
116 pub fn new() -> Self {
117 Self::default()
118 }
119
120 /// Set the user name
121 ///
122 /// # Examples
123 ///
124 /// ```
125 /// use reinhardt_query::dcl::CreateUserStatement;
126 ///
127 /// let stmt = CreateUserStatement::new()
128 /// .user("app_user");
129 /// ```
130 ///
131 /// MySQL with host:
132 ///
133 /// ```
134 /// use reinhardt_query::dcl::CreateUserStatement;
135 ///
136 /// let stmt = CreateUserStatement::new()
137 /// .user("app_user@localhost");
138 /// ```
139 pub fn user(mut self, name: impl Into<String>) -> Self {
140 self.user_name = name.into();
141 self
142 }
143
144 /// Set IF NOT EXISTS flag (MySQL only)
145 ///
146 /// # Examples
147 ///
148 /// ```
149 /// use reinhardt_query::dcl::CreateUserStatement;
150 ///
151 /// let stmt = CreateUserStatement::new()
152 /// .user("app_user")
153 /// .if_not_exists(true);
154 /// ```
155 pub fn if_not_exists(mut self, flag: bool) -> Self {
156 self.if_not_exists = flag;
157 self
158 }
159
160 /// Add a single PostgreSQL role attribute
161 ///
162 /// # Examples
163 ///
164 /// ```
165 /// use reinhardt_query::dcl::{CreateUserStatement, RoleAttribute};
166 ///
167 /// let stmt = CreateUserStatement::new()
168 /// .user("app_user")
169 /// .attribute(RoleAttribute::Password("secret".to_string()))
170 /// .attribute(RoleAttribute::CreateDb);
171 /// ```
172 pub fn attribute(mut self, attr: RoleAttribute) -> Self {
173 self.attributes.push(attr);
174 self
175 }
176
177 /// Set all PostgreSQL role attributes at once
178 ///
179 /// # Examples
180 ///
181 /// ```
182 /// use reinhardt_query::dcl::{CreateUserStatement, RoleAttribute};
183 ///
184 /// let stmt = CreateUserStatement::new()
185 /// .user("app_user")
186 /// .attributes(vec![
187 /// RoleAttribute::Password("secret".to_string()),
188 /// RoleAttribute::CreateDb,
189 /// RoleAttribute::ConnectionLimit(10),
190 /// ]);
191 /// ```
192 pub fn attributes(mut self, attrs: Vec<RoleAttribute>) -> Self {
193 self.attributes = attrs;
194 self
195 }
196
197 /// Set DEFAULT ROLE clause (MySQL only)
198 ///
199 /// # Examples
200 ///
201 /// ```
202 /// use reinhardt_query::dcl::CreateUserStatement;
203 ///
204 /// let stmt = CreateUserStatement::new()
205 /// .user("app_user@localhost")
206 /// .default_role(vec!["app_role".to_string(), "admin_role".to_string()]);
207 /// ```
208 pub fn default_role(mut self, roles: Vec<String>) -> Self {
209 self.default_roles = roles;
210 self
211 }
212
213 /// Add a single MySQL user option
214 ///
215 /// # Examples
216 ///
217 /// ```
218 /// use reinhardt_query::dcl::{CreateUserStatement, UserOption};
219 ///
220 /// let stmt = CreateUserStatement::new()
221 /// .user("app_user")
222 /// .option(UserOption::Comment("Application user".to_string()));
223 /// ```
224 pub fn option(mut self, opt: UserOption) -> Self {
225 self.options.push(opt);
226 self
227 }
228
229 /// Set all MySQL user options at once
230 ///
231 /// # Examples
232 ///
233 /// ```
234 /// use reinhardt_query::dcl::{CreateUserStatement, UserOption};
235 ///
236 /// let stmt = CreateUserStatement::new()
237 /// .user("app_user")
238 /// .options(vec![
239 /// UserOption::Comment("Application user".to_string()),
240 /// UserOption::AccountLock,
241 /// ]);
242 /// ```
243 pub fn options(mut self, opts: Vec<UserOption>) -> Self {
244 self.options = opts;
245 self
246 }
247
248 /// Validate the CREATE USER statement
249 ///
250 /// # Validation Rules
251 ///
252 /// 1. User name cannot be empty
253 ///
254 /// # Examples
255 ///
256 /// ```
257 /// use reinhardt_query::dcl::CreateUserStatement;
258 ///
259 /// let stmt = CreateUserStatement::new()
260 /// .user("app_user");
261 ///
262 /// assert!(stmt.validate().is_ok());
263 /// ```
264 ///
265 /// ```
266 /// use reinhardt_query::dcl::CreateUserStatement;
267 ///
268 /// let stmt = CreateUserStatement::new();
269 /// assert!(stmt.validate().is_err());
270 /// ```
271 pub fn validate(&self) -> Result<(), String> {
272 validate_name(&self.user_name, "User name")?;
273 Ok(())
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn test_create_user_new() {
283 let stmt = CreateUserStatement::new();
284 assert!(stmt.user_name.is_empty());
285 assert!(!stmt.if_not_exists);
286 assert!(stmt.attributes.is_empty());
287 assert!(stmt.default_roles.is_empty());
288 assert!(stmt.options.is_empty());
289 }
290
291 #[test]
292 fn test_create_user_basic() {
293 let stmt = CreateUserStatement::new().user("app_user");
294 assert_eq!(stmt.user_name, "app_user");
295 assert!(stmt.validate().is_ok());
296 }
297
298 #[test]
299 fn test_create_user_if_not_exists() {
300 let stmt = CreateUserStatement::new()
301 .user("app_user")
302 .if_not_exists(true);
303 assert!(stmt.if_not_exists);
304 }
305
306 #[test]
307 fn test_create_user_with_attributes() {
308 let stmt = CreateUserStatement::new()
309 .user("app_user")
310 .attribute(RoleAttribute::Password("secret".to_string()))
311 .attribute(RoleAttribute::CreateDb);
312 assert_eq!(stmt.attributes.len(), 2);
313 }
314
315 #[test]
316 fn test_create_user_with_default_role() {
317 let stmt = CreateUserStatement::new()
318 .user("app_user@localhost")
319 .default_role(vec!["app_role".to_string()]);
320 assert_eq!(stmt.default_roles.len(), 1);
321 }
322
323 #[test]
324 fn test_create_user_with_options() {
325 let stmt = CreateUserStatement::new()
326 .user("app_user")
327 .option(UserOption::Comment("Test user".to_string()));
328 assert_eq!(stmt.options.len(), 1);
329 }
330
331 #[test]
332 fn test_create_user_validation_empty_name() {
333 let stmt = CreateUserStatement::new();
334 assert!(stmt.validate().is_err());
335 }
336}