drizzle_types/postgres/ddl/
policy.rs1use crate::alloc_prelude::*;
8
9#[cfg(feature = "serde")]
10use crate::serde_helpers::{cow_from_string, cow_option_from_string, cow_option_vec_from_strings};
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
18pub struct PolicyDef {
19 pub schema: &'static str,
21 pub table: &'static str,
23 pub name: &'static str,
25 pub as_clause: Option<&'static str>,
27 pub for_clause: Option<&'static str>,
29 pub to: Option<&'static [&'static str]>,
31 pub using: Option<&'static str>,
33 pub with_check: Option<&'static str>,
35}
36
37impl PolicyDef {
38 #[must_use]
40 pub const fn new(schema: &'static str, table: &'static str, name: &'static str) -> Self {
41 Self {
42 schema,
43 table,
44 name,
45 as_clause: None,
46 for_clause: None,
47 to: None,
48 using: None,
49 with_check: None,
50 }
51 }
52
53 #[must_use]
55 pub const fn as_clause(self, clause: &'static str) -> Self {
56 Self {
57 as_clause: Some(clause),
58 ..self
59 }
60 }
61
62 #[must_use]
64 pub const fn for_clause(self, clause: &'static str) -> Self {
65 Self {
66 for_clause: Some(clause),
67 ..self
68 }
69 }
70
71 #[must_use]
73 pub const fn to(self, roles: &'static [&'static str]) -> Self {
74 Self {
75 to: Some(roles),
76 ..self
77 }
78 }
79
80 #[must_use]
82 pub const fn using(self, expr: &'static str) -> Self {
83 Self {
84 using: Some(expr),
85 ..self
86 }
87 }
88
89 #[must_use]
91 pub const fn with_check(self, expr: &'static str) -> Self {
92 Self {
93 with_check: Some(expr),
94 ..self
95 }
96 }
97
98 #[must_use]
100 pub fn into_policy(self) -> Policy {
101 Policy {
102 schema: Cow::Borrowed(self.schema),
103 table: Cow::Borrowed(self.table),
104 name: Cow::Borrowed(self.name),
105 as_clause: self.as_clause.map(Cow::Borrowed),
106 for_clause: self.for_clause.map(Cow::Borrowed),
107 to: self
108 .to
109 .map(|roles| roles.iter().copied().map(Cow::Borrowed).collect()),
110 using: self.using.map(Cow::Borrowed),
111 with_check: self.with_check.map(Cow::Borrowed),
112 }
113 }
114}
115
116#[derive(Clone, Debug, PartialEq, Eq)]
122#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
123#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
124pub struct Policy {
125 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
127 pub schema: Cow<'static, str>,
128
129 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
131 pub table: Cow<'static, str>,
132
133 #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
135 pub name: Cow<'static, str>,
136
137 #[cfg_attr(
139 feature = "serde",
140 serde(
141 rename = "as",
142 default,
143 skip_serializing_if = "Option::is_none",
144 deserialize_with = "cow_option_from_string"
145 )
146 )]
147 pub as_clause: Option<Cow<'static, str>>,
148
149 #[cfg_attr(
151 feature = "serde",
152 serde(
153 rename = "for",
154 default,
155 skip_serializing_if = "Option::is_none",
156 deserialize_with = "cow_option_from_string"
157 )
158 )]
159 pub for_clause: Option<Cow<'static, str>>,
160
161 #[cfg_attr(
163 feature = "serde",
164 serde(
165 default,
166 skip_serializing_if = "Option::is_none",
167 deserialize_with = "cow_option_vec_from_strings"
168 )
169 )]
170 pub to: Option<Vec<Cow<'static, str>>>,
171
172 #[cfg_attr(
174 feature = "serde",
175 serde(
176 default,
177 skip_serializing_if = "Option::is_none",
178 deserialize_with = "cow_option_from_string"
179 )
180 )]
181 pub using: Option<Cow<'static, str>>,
182
183 #[cfg_attr(
185 feature = "serde",
186 serde(
187 default,
188 skip_serializing_if = "Option::is_none",
189 deserialize_with = "cow_option_from_string"
190 )
191 )]
192 pub with_check: Option<Cow<'static, str>>,
193}
194
195impl Policy {
196 #[must_use]
198 pub fn new(
199 schema: impl Into<Cow<'static, str>>,
200 table: impl Into<Cow<'static, str>>,
201 name: impl Into<Cow<'static, str>>,
202 ) -> Self {
203 Self {
204 schema: schema.into(),
205 table: table.into(),
206 name: name.into(),
207 as_clause: None,
208 for_clause: None,
209 to: None,
210 using: None,
211 with_check: None,
212 }
213 }
214
215 #[inline]
217 #[must_use]
218 pub fn schema(&self) -> &str {
219 &self.schema
220 }
221
222 #[inline]
224 #[must_use]
225 pub fn table(&self) -> &str {
226 &self.table
227 }
228
229 #[inline]
231 #[must_use]
232 pub fn name(&self) -> &str {
233 &self.name
234 }
235}
236
237impl From<PolicyDef> for Policy {
238 fn from(def: PolicyDef) -> Self {
239 def.into_policy()
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_const_policy_def() {
249 const POLICY: PolicyDef = PolicyDef::new("public", "users", "users_policy")
250 .for_clause("SELECT")
251 .using("user_id = current_user_id()");
252
253 assert_eq!(POLICY.schema, "public");
254 assert_eq!(POLICY.table, "users");
255 assert_eq!(POLICY.name, "users_policy");
256 }
257
258 #[cfg(feature = "serde")]
261 #[test]
262 fn test_serde_roundtrip_with_all_none_optionals() {
263 let policy = Policy::new("public", "users", "users_policy");
264 assert!(policy.as_clause.is_none());
265 assert!(policy.using.is_none());
266
267 let json = serde_json::to_string(&policy).expect("serialize");
268 let parsed: Policy = serde_json::from_str(&json).expect("deserialize");
269 assert_eq!(parsed, policy);
270 }
271
272 #[test]
273 fn test_policy_def_to_policy() {
274 const DEF: PolicyDef = PolicyDef::new("public", "users", "policy");
275 let policy = DEF.into_policy();
276 assert_eq!(policy.schema(), "public");
277 assert_eq!(policy.table(), "users");
278 assert_eq!(policy.name(), "policy");
279 }
280}