drizzle_types/postgres/ddl/
role.rs

1//! PostgreSQL Role DDL types
2//!
3//! See: <https://github.com/drizzle-team/drizzle-orm/blob/beta/drizzle-kit/src/dialects/postgres/ddl.ts>
4
5#[cfg(feature = "std")]
6use std::borrow::Cow;
7
8#[cfg(all(feature = "alloc", not(feature = "std")))]
9use alloc::borrow::Cow;
10
11#[cfg(feature = "serde")]
12use crate::serde_helpers::{cow_from_string, cow_option_from_string};
13
14// =============================================================================
15// Const-friendly Definition Type
16// =============================================================================
17
18/// Const-friendly role definition for compile-time schema definitions.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20pub struct RoleDef {
21    /// Role name
22    pub name: &'static str,
23    /// Superuser privilege
24    pub superuser: Option<bool>,
25    /// Can create databases?
26    pub create_db: Option<bool>,
27    /// Can create roles?
28    pub create_role: Option<bool>,
29    /// Inherit privileges?
30    pub inherit: Option<bool>,
31    /// Can login?
32    pub can_login: Option<bool>,
33    /// Replication privilege
34    pub replication: Option<bool>,
35    /// Bypass row-level security
36    pub bypass_rls: Option<bool>,
37    /// Connection limit (-1 for unlimited)
38    pub conn_limit: Option<i32>,
39    /// Password (encrypted)
40    pub password: Option<&'static str>,
41    /// Password expiration date
42    pub valid_until: Option<&'static str>,
43}
44
45impl RoleDef {
46    /// Create a new role definition
47    #[must_use]
48    pub const fn new(name: &'static str) -> Self {
49        Self {
50            name,
51            superuser: None,
52            create_db: None,
53            create_role: None,
54            inherit: None,
55            can_login: None,
56            replication: None,
57            bypass_rls: None,
58            conn_limit: None,
59            password: None,
60            valid_until: None,
61        }
62    }
63
64    /// Set superuser privilege
65    #[must_use]
66    pub const fn superuser(self, value: bool) -> Self {
67        Self {
68            superuser: Some(value),
69            ..self
70        }
71    }
72
73    /// Set create_db flag
74    #[must_use]
75    pub const fn create_db(self, value: bool) -> Self {
76        Self {
77            create_db: Some(value),
78            ..self
79        }
80    }
81
82    /// Set create_role flag
83    #[must_use]
84    pub const fn create_role(self, value: bool) -> Self {
85        Self {
86            create_role: Some(value),
87            ..self
88        }
89    }
90
91    /// Set inherit flag
92    #[must_use]
93    pub const fn inherit(self, value: bool) -> Self {
94        Self {
95            inherit: Some(value),
96            ..self
97        }
98    }
99
100    /// Set can_login flag
101    #[must_use]
102    pub const fn can_login(self, value: bool) -> Self {
103        Self {
104            can_login: Some(value),
105            ..self
106        }
107    }
108
109    /// Set replication privilege
110    #[must_use]
111    pub const fn replication(self, value: bool) -> Self {
112        Self {
113            replication: Some(value),
114            ..self
115        }
116    }
117
118    /// Set bypass_rls flag
119    #[must_use]
120    pub const fn bypass_rls(self, value: bool) -> Self {
121        Self {
122            bypass_rls: Some(value),
123            ..self
124        }
125    }
126
127    /// Set connection limit
128    #[must_use]
129    pub const fn conn_limit(self, limit: i32) -> Self {
130        Self {
131            conn_limit: Some(limit),
132            ..self
133        }
134    }
135
136    /// Set password
137    #[must_use]
138    pub const fn password(self, password: &'static str) -> Self {
139        Self {
140            password: Some(password),
141            ..self
142        }
143    }
144
145    /// Set valid_until date
146    #[must_use]
147    pub const fn valid_until(self, date: &'static str) -> Self {
148        Self {
149            valid_until: Some(date),
150            ..self
151        }
152    }
153
154    /// Convert to runtime [`Role`] type
155    #[must_use]
156    pub const fn into_role(self) -> Role {
157        Role {
158            name: Cow::Borrowed(self.name),
159            superuser: self.superuser,
160            create_db: self.create_db,
161            create_role: self.create_role,
162            inherit: self.inherit,
163            can_login: self.can_login,
164            replication: self.replication,
165            bypass_rls: self.bypass_rls,
166            conn_limit: self.conn_limit,
167            password: match self.password {
168                Some(p) => Some(Cow::Borrowed(p)),
169                None => None,
170            },
171            valid_until: match self.valid_until {
172                Some(d) => Some(Cow::Borrowed(d)),
173                None => None,
174            },
175        }
176    }
177}
178
179impl Default for RoleDef {
180    fn default() -> Self {
181        Self::new("")
182    }
183}
184
185// =============================================================================
186// Runtime Type for Serde
187// =============================================================================
188
189/// Runtime role entity for serde serialization.
190#[derive(Clone, Debug, PartialEq, Eq)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
192#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
193pub struct Role {
194    /// Role name
195    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
196    pub name: Cow<'static, str>,
197
198    /// Superuser privilege
199    #[cfg_attr(
200        feature = "serde",
201        serde(default, skip_serializing_if = "Option::is_none")
202    )]
203    pub superuser: Option<bool>,
204
205    /// Can create databases?
206    #[cfg_attr(
207        feature = "serde",
208        serde(default, skip_serializing_if = "Option::is_none")
209    )]
210    pub create_db: Option<bool>,
211
212    /// Can create roles?
213    #[cfg_attr(
214        feature = "serde",
215        serde(default, skip_serializing_if = "Option::is_none")
216    )]
217    pub create_role: Option<bool>,
218
219    /// Inherit privileges?
220    #[cfg_attr(
221        feature = "serde",
222        serde(default, skip_serializing_if = "Option::is_none")
223    )]
224    pub inherit: Option<bool>,
225
226    /// Can login?
227    #[cfg_attr(
228        feature = "serde",
229        serde(default, skip_serializing_if = "Option::is_none")
230    )]
231    pub can_login: Option<bool>,
232
233    /// Replication privilege
234    #[cfg_attr(
235        feature = "serde",
236        serde(default, skip_serializing_if = "Option::is_none")
237    )]
238    pub replication: Option<bool>,
239
240    /// Bypass row-level security
241    #[cfg_attr(
242        feature = "serde",
243        serde(default, skip_serializing_if = "Option::is_none")
244    )]
245    pub bypass_rls: Option<bool>,
246
247    /// Connection limit (-1 for unlimited)
248    #[cfg_attr(
249        feature = "serde",
250        serde(default, skip_serializing_if = "Option::is_none")
251    )]
252    pub conn_limit: Option<i32>,
253
254    /// Password (encrypted)
255    #[cfg_attr(
256        feature = "serde",
257        serde(
258            default,
259            skip_serializing_if = "Option::is_none",
260            deserialize_with = "cow_option_from_string"
261        )
262    )]
263    pub password: Option<Cow<'static, str>>,
264
265    /// Password expiration date
266    #[cfg_attr(
267        feature = "serde",
268        serde(
269            default,
270            skip_serializing_if = "Option::is_none",
271            deserialize_with = "cow_option_from_string"
272        )
273    )]
274    pub valid_until: Option<Cow<'static, str>>,
275}
276
277impl Role {
278    /// Create a new role (runtime)
279    #[must_use]
280    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
281        Self {
282            name: name.into(),
283            superuser: None,
284            create_db: None,
285            create_role: None,
286            inherit: None,
287            can_login: None,
288            replication: None,
289            bypass_rls: None,
290            conn_limit: None,
291            password: None,
292            valid_until: None,
293        }
294    }
295
296    /// Get the role name
297    #[inline]
298    #[must_use]
299    pub fn name(&self) -> &str {
300        &self.name
301    }
302}
303
304impl Default for Role {
305    fn default() -> Self {
306        Self::new("")
307    }
308}
309
310impl From<RoleDef> for Role {
311    fn from(def: RoleDef) -> Self {
312        def.into_role()
313    }
314}
315
316#[cfg(test)]
317mod tests {
318    use super::*;
319
320    #[test]
321    fn test_const_role_def() {
322        const ROLE: RoleDef = RoleDef::new("app_user")
323            .create_db(true)
324            .create_role(true)
325            .can_login(true);
326
327        assert_eq!(ROLE.name, "app_user");
328        assert_eq!(ROLE.create_db, Some(true));
329        assert_eq!(ROLE.create_role, Some(true));
330        assert_eq!(ROLE.can_login, Some(true));
331    }
332
333    #[test]
334    fn test_role_def_to_role() {
335        const DEF: RoleDef = RoleDef::new("admin").superuser(true).create_db(true);
336        let role = DEF.into_role();
337        assert_eq!(role.name(), "admin");
338        assert_eq!(role.superuser, Some(true));
339        assert_eq!(role.create_db, Some(true));
340    }
341
342    #[test]
343    fn test_role_with_conn_limit() {
344        const ROLE: RoleDef = RoleDef::new("limited_user").conn_limit(5).can_login(true);
345        let role = ROLE.into_role();
346        assert_eq!(role.conn_limit, Some(5));
347    }
348}