Skip to main content

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