drizzle_types/postgres/ddl/
privilege.rs

1//! PostgreSQL Privilege 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;
13
14// =============================================================================
15// Privilege Type Enum
16// =============================================================================
17
18/// PostgreSQL privilege type
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
22pub enum PrivilegeType {
23    /// All privileges
24    #[default]
25    All,
26    /// SELECT privilege
27    Select,
28    /// INSERT privilege
29    Insert,
30    /// UPDATE privilege
31    Update,
32    /// DELETE privilege
33    Delete,
34    /// TRUNCATE privilege
35    Truncate,
36    /// REFERENCES privilege
37    References,
38    /// TRIGGER privilege
39    Trigger,
40}
41
42impl PrivilegeType {
43    /// Get the SQL representation
44    #[must_use]
45    pub const fn as_sql(&self) -> &'static str {
46        match self {
47            Self::All => "ALL",
48            Self::Select => "SELECT",
49            Self::Insert => "INSERT",
50            Self::Update => "UPDATE",
51            Self::Delete => "DELETE",
52            Self::Truncate => "TRUNCATE",
53            Self::References => "REFERENCES",
54            Self::Trigger => "TRIGGER",
55        }
56    }
57
58    /// Parse from SQL string
59    pub fn from_sql(s: &str) -> Option<Self> {
60        match s.to_uppercase().as_str() {
61            "ALL" => Some(Self::All),
62            "SELECT" => Some(Self::Select),
63            "INSERT" => Some(Self::Insert),
64            "UPDATE" => Some(Self::Update),
65            "DELETE" => Some(Self::Delete),
66            "TRUNCATE" => Some(Self::Truncate),
67            "REFERENCES" => Some(Self::References),
68            "TRIGGER" => Some(Self::Trigger),
69            _ => None,
70        }
71    }
72}
73
74// =============================================================================
75// Const-friendly Definition Type
76// =============================================================================
77
78/// Const-friendly privilege definition for compile-time schema definitions.
79#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
80pub struct PrivilegeDef {
81    /// Role granting the privilege
82    pub grantor: &'static str,
83    /// Role receiving the privilege
84    pub grantee: &'static str,
85    /// Schema name
86    pub schema: &'static str,
87    /// Table name
88    pub table: &'static str,
89    /// Privilege type
90    pub privilege_type: PrivilegeType,
91    /// Can the grantee grant this privilege to others?
92    pub is_grantable: bool,
93}
94
95impl PrivilegeDef {
96    /// Create a new privilege definition
97    #[must_use]
98    pub const fn new(
99        schema: &'static str,
100        table: &'static str,
101        grantee: &'static str,
102        privilege_type: PrivilegeType,
103    ) -> Self {
104        Self {
105            grantor: "",
106            grantee,
107            schema,
108            table,
109            privilege_type,
110            is_grantable: false,
111        }
112    }
113
114    /// Set the grantor
115    #[must_use]
116    pub const fn grantor(self, grantor: &'static str) -> Self {
117        Self { grantor, ..self }
118    }
119
120    /// Set is_grantable flag
121    #[must_use]
122    pub const fn grantable(self) -> Self {
123        Self {
124            is_grantable: true,
125            ..self
126        }
127    }
128
129    /// Convert to runtime [`Privilege`] type
130    #[must_use]
131    pub const fn into_privilege(self) -> Privilege {
132        Privilege {
133            grantor: Cow::Borrowed(self.grantor),
134            grantee: Cow::Borrowed(self.grantee),
135            schema: Cow::Borrowed(self.schema),
136            table: Cow::Borrowed(self.table),
137            privilege_type: self.privilege_type,
138            is_grantable: self.is_grantable,
139        }
140    }
141}
142
143impl Default for PrivilegeDef {
144    fn default() -> Self {
145        Self::new("public", "", "", PrivilegeType::All)
146    }
147}
148
149// =============================================================================
150// Runtime Type for Serde
151// =============================================================================
152
153/// Runtime privilege entity for serde serialization.
154#[derive(Clone, Debug, PartialEq, Eq)]
155#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
156#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
157pub struct Privilege {
158    /// Role granting the privilege
159    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
160    pub grantor: Cow<'static, str>,
161
162    /// Role receiving the privilege
163    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
164    pub grantee: Cow<'static, str>,
165
166    /// Schema name
167    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
168    pub schema: Cow<'static, str>,
169
170    /// Table name
171    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
172    pub table: Cow<'static, str>,
173
174    /// Privilege type
175    #[cfg_attr(feature = "serde", serde(rename = "type"))]
176    pub privilege_type: PrivilegeType,
177
178    /// Can the grantee grant this privilege to others?
179    #[cfg_attr(feature = "serde", serde(default))]
180    pub is_grantable: bool,
181}
182
183impl Privilege {
184    /// Create a new privilege (runtime)
185    #[must_use]
186    pub fn new(
187        schema: impl Into<Cow<'static, str>>,
188        table: impl Into<Cow<'static, str>>,
189        grantee: impl Into<Cow<'static, str>>,
190        privilege_type: PrivilegeType,
191    ) -> Self {
192        Self {
193            grantor: Cow::Borrowed(""),
194            grantee: grantee.into(),
195            schema: schema.into(),
196            table: table.into(),
197            privilege_type,
198            is_grantable: false,
199        }
200    }
201
202    /// Get the schema name
203    #[inline]
204    #[must_use]
205    pub fn schema(&self) -> &str {
206        &self.schema
207    }
208
209    /// Get the table name
210    #[inline]
211    #[must_use]
212    pub fn table(&self) -> &str {
213        &self.table
214    }
215
216    /// Get the grantee
217    #[inline]
218    #[must_use]
219    pub fn grantee(&self) -> &str {
220        &self.grantee
221    }
222
223    /// Get the grantor
224    #[inline]
225    #[must_use]
226    pub fn grantor(&self) -> &str {
227        &self.grantor
228    }
229}
230
231impl Default for Privilege {
232    fn default() -> Self {
233        Self::new("public", "", "", PrivilegeType::All)
234    }
235}
236
237impl From<PrivilegeDef> for Privilege {
238    fn from(def: PrivilegeDef) -> Self {
239        def.into_privilege()
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246
247    #[test]
248    fn test_const_privilege_def() {
249        const PRIV: PrivilegeDef =
250            PrivilegeDef::new("public", "users", "app_user", PrivilegeType::Select)
251                .grantor("admin")
252                .grantable();
253
254        assert_eq!(PRIV.schema, "public");
255        assert_eq!(PRIV.table, "users");
256        assert_eq!(PRIV.grantee, "app_user");
257        assert_eq!(PRIV.grantor, "admin");
258        assert_eq!(PRIV.privilege_type, PrivilegeType::Select);
259        assert!(PRIV.is_grantable);
260    }
261
262    #[test]
263    fn test_privilege_def_to_privilege() {
264        const DEF: PrivilegeDef =
265            PrivilegeDef::new("public", "users", "reader", PrivilegeType::Select);
266        let priv_ = DEF.into_privilege();
267        assert_eq!(priv_.schema(), "public");
268        assert_eq!(priv_.table(), "users");
269        assert_eq!(priv_.grantee(), "reader");
270    }
271
272    #[test]
273    fn test_privilege_type_sql() {
274        assert_eq!(PrivilegeType::Select.as_sql(), "SELECT");
275        assert_eq!(PrivilegeType::All.as_sql(), "ALL");
276        assert_eq!(PrivilegeType::Truncate.as_sql(), "TRUNCATE");
277    }
278}