Skip to main content

contextdb_engine/
sync_types.rs

1//! Sync contract types for contextDB change-tracking and replication.
2//!
3//! These types define the public API for sync operations. The actual
4//! implementations are pending (all methods currently `unimplemented!()`).
5
6use contextdb_core::{Lsn, RowId, Value};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use uuid::Uuid;
10
11/// A set of changes extracted from a database since a given LSN.
12#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13pub struct ChangeSet {
14    pub rows: Vec<RowChange>,
15    pub edges: Vec<EdgeChange>,
16    pub vectors: Vec<VectorChange>,
17    pub ddl: Vec<DdlChange>,
18}
19
20impl ChangeSet {
21    /// Filters this changeset to only include tables matching the given directions.
22    pub fn filter_by_direction(
23        &self,
24        directions: &HashMap<String, SyncDirection>,
25        include: &[SyncDirection],
26    ) -> ChangeSet {
27        let include_dir = |table: &str| {
28            let dir = directions
29                .get(table)
30                .copied()
31                .unwrap_or(SyncDirection::Both);
32            include.contains(&dir)
33        };
34
35        ChangeSet {
36            rows: self
37                .rows
38                .iter()
39                .filter(|r| include_dir(&r.table))
40                .cloned()
41                .collect(),
42            edges: self.edges.clone(),
43            vectors: self.vectors.clone(),
44            ddl: self
45                .ddl
46                .iter()
47                .filter(|d| match d {
48                    DdlChange::CreateTable { name, .. } => include_dir(name),
49                    DdlChange::DropTable { name } => include_dir(name),
50                    DdlChange::AlterTable { name, .. } => include_dir(name),
51                    DdlChange::CreateIndex { table, .. } => include_dir(table),
52                    DdlChange::DropIndex { table, .. } => include_dir(table),
53                })
54                .cloned()
55                .collect(),
56        }
57    }
58}
59
60#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
61pub struct RowChange {
62    pub table: String,
63    pub natural_key: NaturalKey,
64    pub values: HashMap<String, Value>,
65    pub deleted: bool,
66    pub lsn: Lsn,
67}
68
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70pub struct EdgeChange {
71    pub source: Uuid,
72    pub target: Uuid,
73    pub edge_type: String,
74    pub properties: HashMap<String, Value>,
75    pub lsn: Lsn,
76}
77
78#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub struct VectorChange {
80    pub row_id: RowId,
81    pub vector: Vec<f32>,
82    pub lsn: Lsn,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub enum DdlChange {
87    CreateTable {
88        name: String,
89        columns: Vec<(String, String)>,
90        constraints: Vec<String>,
91    },
92    DropTable {
93        name: String,
94    },
95    AlterTable {
96        name: String,
97        columns: Vec<(String, String)>,
98        constraints: Vec<String>,
99    },
100    CreateIndex {
101        table: String,
102        name: String,
103        columns: Vec<(String, contextdb_core::SortDirection)>,
104    },
105    DropIndex {
106        table: String,
107        name: String,
108    },
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
112pub struct NaturalKey {
113    pub column: String,
114    pub value: Value,
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
118pub enum ConflictPolicy {
119    InsertIfNotExists,
120    ServerWins,
121    EdgeWins,
122    LatestWins,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ConflictPolicies {
127    pub per_table: HashMap<String, ConflictPolicy>,
128    pub default: ConflictPolicy,
129}
130
131impl ConflictPolicies {
132    pub fn uniform(policy: ConflictPolicy) -> Self {
133        Self {
134            per_table: HashMap::new(),
135            default: policy,
136        }
137    }
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ApplyResult {
142    pub applied_rows: usize,
143    pub skipped_rows: usize,
144    pub conflicts: Vec<Conflict>,
145    pub new_lsn: Lsn,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct Conflict {
150    pub natural_key: NaturalKey,
151    pub resolution: ConflictPolicy,
152    pub reason: Option<String>,
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
156pub enum SyncDirection {
157    Push,
158    Pull,
159    Both,
160    None,
161}