Skip to main content

drizzle_types/postgres/ddl/
privilege.rs

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