cufflink_types/manifest/
schema_diff.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7pub struct SchemaDiff {
8 #[serde(default)]
9 pub added_tables: Vec<String>,
10 #[serde(default)]
11 pub dropped_tables: Vec<String>,
12 #[serde(default)]
16 pub added_columns: Vec<(String, String)>,
17 #[serde(default)]
21 pub dropped_columns: Vec<(String, String)>,
22 #[serde(default)]
24 pub type_changes: Vec<(String, String, String, String)>,
25 #[serde(default)]
27 pub nullability_changes: Vec<(String, String, bool)>,
28}
29
30impl SchemaDiff {
31 pub fn added_column(&self, table: &str, col: &str) -> bool {
32 self.added_columns
33 .iter()
34 .any(|(t, c)| t == table && c == col)
35 }
36
37 pub fn dropped_column(&self, table: &str, col: &str) -> bool {
38 self.dropped_columns
39 .iter()
40 .any(|(t, c)| t == table && c == col)
41 }
42
43 pub fn added_table(&self, table: &str) -> bool {
44 self.added_tables.iter().any(|t| t == table)
45 }
46
47 pub fn dropped_table(&self, table: &str) -> bool {
48 self.dropped_tables.iter().any(|t| t == table)
49 }
50
51 pub fn is_empty(&self) -> bool {
52 self.added_tables.is_empty()
53 && self.dropped_tables.is_empty()
54 && self.added_columns.is_empty()
55 && self.dropped_columns.is_empty()
56 && self.type_changes.is_empty()
57 && self.nullability_changes.is_empty()
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_schema_diff_helpers() {
67 let diff = SchemaDiff {
68 added_tables: vec!["batches".into()],
69 dropped_tables: vec!["legacy_batches".into()],
70 added_columns: vec![("pickups".into(), "min".into())],
71 dropped_columns: vec![("pickups".into(), "midpoint".into())],
72 type_changes: vec![],
73 nullability_changes: vec![],
74 };
75 assert!(!diff.is_empty());
76 assert!(diff.added_table("batches"));
77 assert!(!diff.added_table("nope"));
78 assert!(diff.dropped_table("legacy_batches"));
79 assert!(diff.added_column("pickups", "min"));
80 assert!(!diff.added_column("pickups", "max"));
81 assert!(diff.dropped_column("pickups", "midpoint"));
82 }
83
84 #[test]
85 fn test_schema_diff_is_empty_default() {
86 assert!(SchemaDiff::default().is_empty());
87 }
88
89 #[test]
90 fn test_schema_diff_serialization_roundtrip() {
91 let diff = SchemaDiff {
92 added_tables: vec![],
93 dropped_tables: vec![],
94 added_columns: vec![("t".into(), "c".into())],
95 dropped_columns: vec![],
96 type_changes: vec![("t".into(), "c".into(), "TEXT".into(), "INTEGER".into())],
97 nullability_changes: vec![("t".into(), "c".into(), false)],
98 };
99 let json = serde_json::to_string(&diff).unwrap();
100 let parsed: SchemaDiff = serde_json::from_str(&json).unwrap();
101 assert!(parsed.added_column("t", "c"));
102 assert_eq!(parsed.type_changes.len(), 1);
103 assert_eq!(parsed.nullability_changes.len(), 1);
104 }
105}