1use contextdb_core::{Lsn, RowId, Value};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use uuid::Uuid;
10
11#[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 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}