Skip to main content

sql_schema/
diff.rs

1use std::fmt;
2
3use bon::bon;
4use thiserror::Error;
5
6use crate::{
7    ast::{CreateDomain, CreateExtension, CreateIndex, CreateTable, CreateType, Statement},
8    dialect::{Generic, PostgreSQL, SQLite},
9    sealed::Sealed,
10};
11
12pub mod generic;
13
14#[derive(Error, Debug)]
15pub struct DiffError {
16    kind: DiffErrorKind,
17    statement_a: Option<Box<Statement>>,
18    statement_b: Option<Box<Statement>>,
19}
20
21impl fmt::Display for DiffError {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        write!(
24            f,
25            "Oops, we couldn't diff that: {reason}",
26            reason = self.kind
27        )?;
28        if let Some(statement_a) = &self.statement_a {
29            write!(f, "\n\nStatement A:\n{statement_a}")?;
30        }
31        if let Some(statement_b) = &self.statement_b {
32            write!(f, "\n\nStatement B:\n{statement_b}")?;
33        }
34        Ok(())
35    }
36}
37
38#[bon]
39impl DiffError {
40    #[builder]
41    pub(crate) fn new(
42        kind: DiffErrorKind,
43        #[builder(into)] statement_a: Option<Statement>,
44        #[builder(into)] statement_b: Option<Statement>,
45    ) -> Self {
46        Self {
47            kind,
48            statement_a: statement_a.map(Box::new),
49            statement_b: statement_b.map(Box::new),
50        }
51    }
52}
53
54#[derive(Error, Debug)]
55#[non_exhaustive]
56pub enum DiffErrorKind {
57    #[error("can't drop unnamed index")]
58    DropUnnamedIndex,
59    #[error("can't compare unnamed index")]
60    CompareUnnamedIndex,
61    #[error("removing enum labels is not supported")]
62    RemoveEnumLabel,
63    #[error("not yet supported")]
64    NotImplemented,
65}
66
67pub type Result<T, E = DiffError> = std::result::Result<T, E>;
68
69pub trait TreeDiffer: StatementDiffer + Sealed {
70    fn diff_tree(&self, a: &[Statement], b: &[Statement]) -> Result<Option<Vec<Statement>>> {
71        generic::tree::tree_diff(self, a, b)
72    }
73
74    fn find_and_compare_create_table(
75        &self,
76        sa: &Statement,
77        a: &CreateTable,
78        b: &[Statement],
79    ) -> Result<Option<Vec<Statement>>> {
80        generic::tree::find_and_compare_create_table(self, sa, a, b)
81    }
82
83    fn find_and_compare_create_index(
84        &self,
85        sa: &Statement,
86        a: &CreateIndex,
87        b: &[Statement],
88    ) -> Result<Option<Vec<Statement>>> {
89        generic::tree::find_and_compare_create_index(self, sa, a, b)
90    }
91
92    fn find_and_compare_create_type(
93        &self,
94        sa: &Statement,
95        a: &CreateType,
96        b: &[Statement],
97    ) -> Result<Option<Vec<Statement>>> {
98        generic::tree::find_and_compare_create_type(self, sa, a, b)
99    }
100
101    fn find_and_compare_create_extension(
102        &self,
103        sa: &Statement,
104        a: &CreateExtension,
105        b: &[Statement],
106    ) -> Result<Option<Vec<Statement>>> {
107        generic::tree::find_and_compare_create_extension(self, sa, a, b)
108    }
109
110    fn find_and_compare_create_domain(
111        &self,
112        sa: &Statement,
113        a: &CreateDomain,
114        b: &[Statement],
115    ) -> Result<Option<Vec<Statement>>> {
116        generic::tree::find_and_compare_create_domain(self, sa, a, b)
117    }
118}
119
120impl TreeDiffer for Generic {}
121
122impl TreeDiffer for PostgreSQL {}
123
124impl TreeDiffer for SQLite {}
125
126pub trait StatementDiffer: fmt::Debug + Default + Clone + Sized + Sealed {
127    fn diff(&self, sa: &Statement, sb: &Statement) -> Result<Option<Vec<Statement>>> {
128        generic::statement::diff(self, sa, sb)
129    }
130
131    fn compare_create_table(
132        &self,
133        a: &CreateTable,
134        b: &CreateTable,
135    ) -> Result<Option<Vec<Statement>>> {
136        generic::statement::compare_create_table(a, b)
137    }
138
139    fn compare_create_index(
140        &self,
141        a: &CreateIndex,
142        b: &CreateIndex,
143    ) -> Result<Option<Vec<Statement>>> {
144        generic::statement::compare_create_index(a, b)
145    }
146
147    fn compare_create_type(
148        &self,
149        a: &CreateType,
150        b: &CreateType,
151    ) -> Result<Option<Vec<Statement>>> {
152        generic::statement::compare_create_type(a, b)
153    }
154
155    fn compare_create_domain(
156        &self,
157        a: &CreateDomain,
158        b: &CreateDomain,
159    ) -> Result<Option<Vec<Statement>>> {
160        generic::statement::compare_create_domain(a, b)
161    }
162}
163
164impl StatementDiffer for Generic {}
165
166impl StatementDiffer for PostgreSQL {}
167
168impl StatementDiffer for SQLite {}