pgevolve_core/diff/
changeset.rs1use serde::{Deserialize, Serialize};
8
9use crate::identifier::Identifier;
10use crate::ir::grant::GrantTarget;
11
12use super::change::{Change, ChangeEntry};
13use super::destructiveness::Destructiveness;
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub struct UnmanagedGrantObservation {
22 pub object_label: String,
24 pub privilege_label: String,
26 pub role_name: Identifier,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct RevokeWithOwnerObservation {
37 pub object_label: String,
39 pub privilege_label: String,
41 pub grantee: GrantTarget,
43 pub owner: Identifier,
45}
46
47#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
49pub struct ChangeSet {
50 pub entries: Vec<ChangeEntry>,
52 #[serde(default)]
58 pub unmanaged_grants: Vec<UnmanagedGrantObservation>,
59 #[serde(default)]
63 pub revokes_with_owner: Vec<RevokeWithOwnerObservation>,
64}
65
66impl ChangeSet {
67 pub const fn new() -> Self {
69 Self {
70 entries: Vec::new(),
71 unmanaged_grants: Vec::new(),
72 revokes_with_owner: Vec::new(),
73 }
74 }
75
76 pub fn push(&mut self, change: Change, destructiveness: Destructiveness) {
78 self.entries.push(ChangeEntry {
79 change,
80 destructiveness,
81 });
82 }
83
84 pub const fn is_empty(&self) -> bool {
86 self.entries.is_empty()
87 }
88
89 pub const fn len(&self) -> usize {
91 self.entries.len()
92 }
93
94 pub fn iter(&self) -> impl Iterator<Item = &ChangeEntry> {
96 self.entries.iter()
97 }
98
99 pub fn extend(&mut self, other: Self) {
101 self.entries.extend(other.entries);
102 self.unmanaged_grants.extend(other.unmanaged_grants);
103 self.revokes_with_owner.extend(other.revokes_with_owner);
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110 use crate::identifier::Identifier;
111
112 fn id(s: &str) -> Identifier {
113 Identifier::from_unquoted(s).unwrap()
114 }
115
116 #[test]
117 fn new_is_empty() {
118 let cs = ChangeSet::new();
119 assert!(cs.is_empty());
120 assert_eq!(cs.len(), 0);
121 assert!(cs.iter().next().is_none());
122 }
123
124 #[test]
125 fn push_then_len_is_one() {
126 let mut cs = ChangeSet::new();
127 cs.push(
128 Change::DropSchema(id("old")),
129 Destructiveness::RequiresApproval {
130 reason: "drops schema".into(),
131 },
132 );
133 assert!(!cs.is_empty());
134 assert_eq!(cs.len(), 1);
135 }
136
137 #[test]
138 fn extend_concatenates() {
139 let mut a = ChangeSet::new();
140 a.push(
141 Change::DropSchema(id("a")),
142 Destructiveness::RequiresApproval { reason: "x".into() },
143 );
144 let mut b = ChangeSet::new();
145 b.push(
146 Change::DropSchema(id("b")),
147 Destructiveness::RequiresApproval { reason: "x".into() },
148 );
149 a.extend(b);
150 assert_eq!(a.len(), 2);
151 }
152}