Skip to main content

patchworks/db/
types.rs

1//! Core data types used across Patchworks.
2
3use serde::{Deserialize, Serialize};
4
5/// Metadata about a SQLite table.
6#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
7pub struct TableInfo {
8    /// Table name.
9    pub name: String,
10    /// Column metadata in declaration order.
11    pub columns: Vec<ColumnInfo>,
12    /// Total number of rows in the table.
13    pub row_count: u64,
14    /// Column names that form the primary key.
15    pub primary_key: Vec<String>,
16    /// Original `CREATE TABLE` SQL, if available.
17    pub create_sql: Option<String>,
18}
19
20/// Metadata about a SQLite view.
21#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
22pub struct ViewInfo {
23    /// View name.
24    pub name: String,
25    /// Original `CREATE VIEW` SQL, if available.
26    pub create_sql: Option<String>,
27}
28
29/// Metadata about a SQLite index or trigger.
30#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
31pub struct SchemaObjectInfo {
32    /// Schema object name.
33    pub name: String,
34    /// Table this object is attached to.
35    pub table_name: String,
36    /// Original `CREATE ...` SQL, if available.
37    pub create_sql: Option<String>,
38}
39
40/// Metadata about a SQLite column.
41#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
42pub struct ColumnInfo {
43    /// Column name.
44    pub name: String,
45    /// SQLite declared type or inferred affinity label.
46    pub col_type: String,
47    /// Whether the column accepts `NULL`.
48    pub nullable: bool,
49    /// Column default expression if one exists.
50    pub default_value: Option<String>,
51    /// Whether the column participates in the primary key.
52    pub is_primary_key: bool,
53}
54
55/// A generic SQLite value for display and comparison.
56#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
57pub enum SqlValue {
58    /// SQL `NULL`.
59    Null,
60    /// Signed integer value.
61    Integer(i64),
62    /// Floating-point value.
63    Real(f64),
64    /// UTF-8 text value.
65    Text(String),
66    /// Binary value.
67    Blob(Vec<u8>),
68}
69
70/// Full schema summary for a database file.
71#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
72pub struct DatabaseSummary {
73    /// Original database path.
74    pub path: String,
75    /// Discovered tables.
76    pub tables: Vec<TableInfo>,
77    /// Discovered views.
78    pub views: Vec<ViewInfo>,
79    /// Discovered indexes.
80    pub indexes: Vec<SchemaObjectInfo>,
81    /// Discovered triggers.
82    pub triggers: Vec<SchemaObjectInfo>,
83}
84
85/// A paginated slice of table data.
86#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
87pub struct TablePage {
88    /// Table name being viewed.
89    pub table_name: String,
90    /// Column metadata for the rendered rows.
91    pub columns: Vec<ColumnInfo>,
92    /// Row values for the current page.
93    pub rows: Vec<Vec<SqlValue>>,
94    /// Zero-based page index.
95    pub page: usize,
96    /// Requested page size.
97    pub page_size: usize,
98    /// Total rows in the table.
99    pub total_rows: u64,
100    /// Applied sort, if any.
101    pub sort: Option<TableSort>,
102}
103
104/// Sorting information for table reads.
105#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
106pub struct TableSort {
107    /// Column name used for sorting.
108    pub column: String,
109    /// Sort direction.
110    pub direction: SortDirection,
111}
112
113/// Sort direction for paginated table reads.
114#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
115pub enum SortDirection {
116    /// Ascending order.
117    Asc,
118    /// Descending order.
119    Desc,
120}
121
122/// Input parameters for reading a paginated table page.
123#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
124pub struct TableQuery {
125    /// Zero-based page index.
126    pub page: usize,
127    /// Maximum rows to return.
128    pub page_size: usize,
129    /// Optional sort selection.
130    pub sort: Option<TableSort>,
131}
132
133impl Default for TableQuery {
134    fn default() -> Self {
135        Self {
136            page: 0,
137            page_size: 100,
138            sort: None,
139        }
140    }
141}
142
143/// Schema-level diff between two databases.
144#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
145pub struct SchemaDiff {
146    /// Tables only present on the right side.
147    pub added_tables: Vec<TableInfo>,
148    /// Tables only present on the left side.
149    pub removed_tables: Vec<TableInfo>,
150    /// Tables present on both sides with column-level changes.
151    pub modified_tables: Vec<TableSchemaDiff>,
152    /// Tables whose schemas match exactly.
153    pub unchanged_tables: Vec<String>,
154    /// Indexes only present on the right side.
155    pub added_indexes: Vec<SchemaObjectInfo>,
156    /// Indexes only present on the left side.
157    pub removed_indexes: Vec<SchemaObjectInfo>,
158    /// Indexes present on both sides but with different definitions.
159    pub modified_indexes: Vec<(SchemaObjectInfo, SchemaObjectInfo)>,
160    /// Triggers only present on the right side.
161    pub added_triggers: Vec<SchemaObjectInfo>,
162    /// Triggers only present on the left side.
163    pub removed_triggers: Vec<SchemaObjectInfo>,
164    /// Triggers present on both sides but with different definitions.
165    pub modified_triggers: Vec<(SchemaObjectInfo, SchemaObjectInfo)>,
166}
167
168/// Schema changes within a single table.
169#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
170pub struct TableSchemaDiff {
171    /// Table name.
172    pub table_name: String,
173    /// Columns only present on the right side.
174    pub added_columns: Vec<ColumnInfo>,
175    /// Columns only present on the left side.
176    pub removed_columns: Vec<ColumnInfo>,
177    /// Columns with the same name but different definitions.
178    pub modified_columns: Vec<(ColumnInfo, ColumnInfo)>,
179}
180
181/// Row-level diff for a single table.
182#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
183pub struct TableDataDiff {
184    /// Table name.
185    pub table_name: String,
186    /// Column names used for row context.
187    pub columns: Vec<String>,
188    /// Right-side rows not found on the left side.
189    pub added_rows: Vec<Vec<SqlValue>>,
190    /// Left-side rows not found on the right side.
191    pub removed_rows: Vec<Vec<SqlValue>>,
192    /// Identity values for removed rows, aligned by index with `removed_rows`.
193    pub removed_row_keys: Vec<Vec<SqlValue>>,
194    /// Rows with matching identity but different cell values.
195    pub modified_rows: Vec<RowModification>,
196    /// Aggregate diff statistics.
197    pub stats: DiffStats,
198    /// User-facing warnings about diff reliability or scale.
199    pub warnings: Vec<String>,
200}
201
202/// Changes to a single row.
203#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
204pub struct RowModification {
205    /// Primary key or synthetic row identity values.
206    pub primary_key: Vec<SqlValue>,
207    /// Per-cell modifications inside the row.
208    pub changes: Vec<CellChange>,
209}
210
211/// Change to a single cell.
212#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
213pub struct CellChange {
214    /// Column name that changed.
215    pub column: String,
216    /// Left-side value.
217    pub old_value: SqlValue,
218    /// Right-side value.
219    pub new_value: SqlValue,
220}
221
222/// Diff summary counts.
223#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
224pub struct DiffStats {
225    /// Total rows on the left side.
226    pub total_rows_left: u64,
227    /// Total rows on the right side.
228    pub total_rows_right: u64,
229    /// Count of added rows.
230    pub added: u64,
231    /// Count of removed rows.
232    pub removed: u64,
233    /// Count of modified rows.
234    pub modified: u64,
235    /// Count of unchanged rows.
236    pub unchanged: u64,
237}
238
239/// A saved snapshot of a database state.
240#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
241pub struct Snapshot {
242    /// Snapshot UUID.
243    pub id: String,
244    /// Human-friendly snapshot name.
245    pub name: String,
246    /// Original source database file path.
247    pub source_path: String,
248    /// Snapshot creation timestamp in ISO 8601 form.
249    pub created_at: String,
250    /// Number of tables captured in the snapshot.
251    pub table_count: u32,
252    /// Total rows across all tables.
253    pub total_rows: u64,
254}
255
256impl SqlValue {
257    /// Returns a short display string suitable for table cells.
258    pub fn display(&self) -> String {
259        match self {
260            Self::Null => "NULL".to_owned(),
261            Self::Integer(value) => value.to_string(),
262            Self::Real(value) => value.to_string(),
263            Self::Text(value) => value.clone(),
264            Self::Blob(bytes) => format!("[BLOB: {} bytes]", bytes.len()),
265        }
266    }
267}