drizzle_types/postgres/ddl/
policy.rs1use crate::alloc_prelude::*;
5
6#[cfg(feature = "serde")]
7use crate::serde_helpers::{cow_from_string, cow_option_from_string, cow_option_vec_from_strings};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
36pub struct PolicyDef {
37 pub schema: &'static str,
39 pub table: &'static str,
41 pub name: &'static str,
43 pub as_clause: Option<&'static str>,
45 pub for_clause: Option<&'static str>,
47 pub to: Option<&'static [&'static str]>,
49 pub using: Option<&'static str>,
51 pub with_check: Option<&'static str>,
53}
54
55impl PolicyDef {
56 #[must_use]
58 pub const fn new(schema: &'static str, table: &'static str, name: &'static str) -> Self {
59 Self {
60 schema,
61 table,
62 name,
63 as_clause: None,
64 for_clause: None,
65 to: None,
66 using: None,
67 with_check: None,
68 }
69 }
70
71 #[must_use]
73 pub const fn as_clause(self, clause: &'static str) -> Self {
74 Self {
75 as_clause: Some(clause),
76 ..self
77 }
78 }
79
80 #[must_use]
82 pub const fn for_clause(self, clause: &'static str) -> Self {
83 Self {
84 for_clause: Some(clause),
85 ..self
86 }
87 }
88
89 #[must_use]
91 pub const fn to(self, roles: &'static [&'static str]) -> Self {
92 Self {
93 to: Some(roles),
94 ..self
95 }
96 }
97
98 #[must_use]
100 pub const fn using(self, expr: &'static str) -> Self {
101 Self {
102 using: Some(expr),
103 ..self
104 }
105 }
106
107 #[must_use]
109 pub const fn with_check(self, expr: &'static str) -> Self {
110 Self {
111 with_check: Some(expr),
112 ..self
113 }
114 }
115
116 #[must_use]
118 pub fn into_policy(self) -> Policy {
119 Policy {
120 schema: Cow::Borrowed(self.schema),
121 table: Cow::Borrowed(self.table),
122 name: Cow::Borrowed(self.name),
123 as_clause: self.as_clause.map(Cow::Borrowed),
124 for_clause: self.for_clause.map(Cow::Borrowed),
125 to: self
126 .to
127 .map(|roles| roles.iter().copied().map(Cow::Borrowed).collect()),
128 using: self.using.map(Cow::Borrowed),
129 with_check: self.with_check.map(Cow::Borrowed),
130 }
131 }
132}
133
134#[derive(Clone, Debug, PartialEq, Eq)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
141#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
142pub struct Policy {
143 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
145 pub schema: Cow<'static, str>,
146
147 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
149 pub table: Cow<'static, str>,
150
151 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
153 pub name: Cow<'static, str>,
154
155 #[cfg_attr(
157 feature = "serde",
158 serde(
159 rename = "as",
160 default,
161 skip_serializing_if = "Option::is_none",
162 deserialize_with = "cow_option_from_string"
163 )
164 )]
165 pub as_clause: Option<Cow<'static, str>>,
166
167 #[cfg_attr(
169 feature = "serde",
170 serde(
171 rename = "for",
172 default,
173 skip_serializing_if = "Option::is_none",
174 deserialize_with = "cow_option_from_string"
175 )
176 )]
177 pub for_clause: Option<Cow<'static, str>>,
178
179 #[cfg_attr(
181 feature = "serde",
182 serde(
183 default,
184 skip_serializing_if = "Option::is_none",
185 deserialize_with = "cow_option_vec_from_strings"
186 )
187 )]
188 pub to: Option<Vec<Cow<'static, str>>>,
189
190 #[cfg_attr(
192 feature = "serde",
193 serde(
194 default,
195 skip_serializing_if = "Option::is_none",
196 deserialize_with = "cow_option_from_string"
197 )
198 )]
199 pub using: Option<Cow<'static, str>>,
200
201 #[cfg_attr(
203 feature = "serde",
204 serde(
205 default,
206 skip_serializing_if = "Option::is_none",
207 deserialize_with = "cow_option_from_string"
208 )
209 )]
210 pub with_check: Option<Cow<'static, str>>,
211}
212
213impl Policy {
214 #[must_use]
216 pub fn new(
217 schema: impl Into<Cow<'static, str>>,
218 table: impl Into<Cow<'static, str>>,
219 name: impl Into<Cow<'static, str>>,
220 ) -> Self {
221 Self {
222 schema: schema.into(),
223 table: table.into(),
224 name: name.into(),
225 as_clause: None,
226 for_clause: None,
227 to: None,
228 using: None,
229 with_check: None,
230 }
231 }
232
233 #[inline]
235 #[must_use]
236 pub fn schema(&self) -> &str {
237 &self.schema
238 }
239
240 #[inline]
242 #[must_use]
243 pub fn table(&self) -> &str {
244 &self.table
245 }
246
247 #[inline]
249 #[must_use]
250 pub fn name(&self) -> &str {
251 &self.name
252 }
253}
254
255impl From<PolicyDef> for Policy {
256 fn from(def: PolicyDef) -> Self {
257 def.into_policy()
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_const_policy_def() {
267 const POLICY: PolicyDef = PolicyDef::new("public", "users", "users_policy")
268 .for_clause("SELECT")
269 .using("user_id = current_user_id()");
270
271 assert_eq!(POLICY.schema, "public");
272 assert_eq!(POLICY.table, "users");
273 assert_eq!(POLICY.name, "users_policy");
274 }
275
276 #[cfg(feature = "serde")]
279 #[test]
280 fn test_serde_roundtrip_with_all_none_optionals() {
281 let policy = Policy::new("public", "users", "users_policy");
282 assert!(policy.as_clause.is_none());
283 assert!(policy.using.is_none());
284
285 let json = serde_json::to_string(&policy).expect("serialize");
286 let parsed: Policy = serde_json::from_str(&json).expect("deserialize");
287 assert_eq!(parsed, policy);
288 }
289
290 #[test]
291 fn test_policy_def_to_policy() {
292 const DEF: PolicyDef = PolicyDef::new("public", "users", "policy");
293 let policy = DEF.into_policy();
294 assert_eq!(policy.schema(), "public");
295 assert_eq!(policy.table(), "users");
296 assert_eq!(policy.name(), "policy");
297 }
298}