pgevolve_core/ir/
policy.rs1use serde::{Deserialize, Serialize};
8
9use crate::identifier::Identifier;
10use crate::ir::default_expr::NormalizedExpr;
11use crate::ir::grant::GrantTarget;
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
15pub struct Policy {
16 pub name: Identifier,
18 pub permissive: bool,
20 pub command: PolicyCommand,
22 pub roles: Vec<GrantTarget>,
26 pub using: Option<NormalizedExpr>,
28 pub with_check: Option<NormalizedExpr>,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
35#[serde(rename_all = "snake_case")]
36pub enum PolicyCommand {
37 All,
39 Select,
41 Insert,
43 Update,
45 Delete,
47}
48
49impl PolicyCommand {
50 #[must_use]
52 pub const fn sql_keyword(self) -> &'static str {
53 match self {
54 Self::All => "ALL",
55 Self::Select => "SELECT",
56 Self::Insert => "INSERT",
57 Self::Update => "UPDATE",
58 Self::Delete => "DELETE",
59 }
60 }
61
62 #[must_use]
64 pub fn from_pg_text(s: &str) -> Option<Self> {
65 match s {
66 "ALL" => Some(Self::All),
67 "SELECT" => Some(Self::Select),
68 "INSERT" => Some(Self::Insert),
69 "UPDATE" => Some(Self::Update),
70 "DELETE" => Some(Self::Delete),
71 _ => None,
72 }
73 }
74
75 #[must_use]
78 pub const fn allows_with_check(self) -> bool {
79 matches!(self, Self::All | Self::Insert | Self::Update)
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 fn id(s: &str) -> Identifier {
88 Identifier::from_unquoted(s).unwrap()
89 }
90
91 #[test]
92 fn pg_text_roundtrips() {
93 for cmd in [
94 PolicyCommand::All,
95 PolicyCommand::Select,
96 PolicyCommand::Insert,
97 PolicyCommand::Update,
98 PolicyCommand::Delete,
99 ] {
100 assert_eq!(PolicyCommand::from_pg_text(cmd.sql_keyword()), Some(cmd));
101 }
102 }
103
104 #[test]
105 fn from_pg_text_rejects_unknown() {
106 assert_eq!(PolicyCommand::from_pg_text("BOGUS"), None);
107 }
108
109 #[test]
110 fn select_and_delete_reject_with_check() {
111 assert!(!PolicyCommand::Select.allows_with_check());
112 assert!(!PolicyCommand::Delete.allows_with_check());
113 assert!(PolicyCommand::All.allows_with_check());
114 assert!(PolicyCommand::Insert.allows_with_check());
115 assert!(PolicyCommand::Update.allows_with_check());
116 }
117
118 #[test]
119 fn policy_sort_by_name() {
120 let a = Policy {
121 name: id("alpha"),
122 permissive: true,
123 command: PolicyCommand::All,
124 roles: vec![GrantTarget::Public],
125 using: None,
126 with_check: None,
127 };
128 let b = Policy {
129 name: id("beta"),
130 permissive: true,
131 command: PolicyCommand::All,
132 roles: vec![GrantTarget::Public],
133 using: None,
134 with_check: None,
135 };
136 let mut policies = vec![b.clone(), a.clone()];
137 policies.sort();
138 assert_eq!(policies, vec![a, b]);
139 }
140}