1use serde::{Deserialize, Serialize};
7use serde_json::{Map, Value};
8
9#[derive(Debug, Clone)]
13pub struct WindowBase {
14 pub table: String,
15 pub variable: String,
16 pub fixed_scopes: Vec<(String, Vec<String>)>,
18 pub params: Option<String>,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
28#[serde(rename_all = "camelCase")]
29pub struct WindowState {
30 pub units: Vec<String>,
32 pub pending: Vec<String>,
34}
35
36#[derive(Debug, Clone, Serialize)]
37#[serde(rename_all = "camelCase")]
38pub struct TableChange {
39 pub table: String,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub scope_keys: Option<Vec<String>>,
42}
43
44#[derive(Debug, Clone, Serialize)]
45#[serde(rename_all = "camelCase")]
46pub struct WindowChange {
47 pub base_key: String,
48 pub table: String,
49 pub units: Vec<String>,
50}
51
52#[derive(Debug, Clone, Serialize)]
53#[serde(rename_all = "camelCase")]
54pub struct SyncStatusSnapshot {
55 pub outbox: usize,
56 pub upgrading: bool,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub lease_state: Option<LeaseState>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub schema_floor: Option<SchemaFloor>,
61 pub sync_needed: bool,
62}
63
64#[derive(Debug, Clone, Serialize)]
66#[serde(rename_all = "camelCase")]
67pub struct ClientChangeBatch {
68 pub revision: String,
69 pub tables: Vec<TableChange>,
70 pub windows: Vec<WindowChange>,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub status: Option<SyncStatusSnapshot>,
73 pub conflicts_changed: bool,
74 pub rejections_changed: bool,
75}
76
77#[derive(Debug, Clone, Serialize)]
78#[serde(tag = "kind", rename_all = "camelCase")]
79pub enum SyncIntent {
80 None,
81 Interactive,
82 Background {
83 #[serde(rename = "delayMs")]
84 delay_ms: u64,
85 },
86}
87
88#[derive(Debug, Clone, Serialize)]
89pub struct CommandEffects {
90 pub sync: SyncIntent,
91}
92
93impl CommandEffects {
94 #[must_use]
95 pub fn none() -> Self {
96 Self {
97 sync: SyncIntent::None,
98 }
99 }
100
101 #[must_use]
102 pub fn interactive() -> Self {
103 Self {
104 sync: SyncIntent::Interactive,
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
110pub struct WindowCoverage {
111 pub base: WindowBase,
112 pub units: Vec<String>,
113}
114
115#[derive(Debug, Clone, Serialize)]
116#[serde(rename_all = "camelCase")]
117pub struct WindowUnitRef {
118 pub base_key: String,
119 pub unit: String,
120}
121
122#[derive(Debug, Clone, Serialize)]
123#[serde(rename_all = "camelCase")]
124pub struct CoverageSnapshot {
125 pub complete: bool,
126 pub pending: Vec<WindowUnitRef>,
127 pub missing: Vec<WindowUnitRef>,
128}
129
130#[derive(Debug, Clone, Serialize)]
131#[serde(rename_all = "camelCase")]
132pub struct QuerySnapshot {
133 pub revision: String,
134 pub rows: Vec<Map<String, Value>>,
135 pub coverage: CoverageSnapshot,
136}
137
138impl WindowState {
139 #[must_use]
141 pub fn complete(&self, unit: &str) -> bool {
142 self.units.iter().any(|u| u == unit) && !self.pending.iter().any(|u| u == unit)
143 }
144}
145
146#[derive(Debug, Clone)]
148pub enum Mutation {
149 Upsert {
150 table: String,
151 values: Map<String, Value>,
152 base_version: Option<i64>,
153 },
154 Delete {
155 table: String,
156 row_id: String,
157 base_version: Option<i64>,
158 },
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
162#[serde(rename_all = "camelCase")]
163pub struct SchemaFloor {
164 #[serde(skip_serializing_if = "Option::is_none")]
165 pub required_schema_version: Option<i32>,
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub latest_schema_version: Option<i32>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
174#[serde(rename_all = "camelCase")]
175pub struct LeaseState {
176 #[serde(skip_serializing_if = "Option::is_none")]
177 pub lease_id: Option<String>,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub expires_at_ms: Option<i64>,
180 #[serde(skip_serializing_if = "Option::is_none")]
181 pub error_code: Option<String>,
182}
183
184#[derive(Debug, Clone, Serialize)]
186#[serde(rename_all = "camelCase")]
187pub struct PresencePeer {
188 pub actor_id: String,
189 pub client_id: String,
190 pub doc: serde_json::Value,
191}
192
193#[derive(Debug, Clone, Serialize, Default)]
194#[serde(rename_all = "camelCase")]
195pub struct SyncReport {
196 pub pushed: u32,
197 pub applied: Vec<String>,
198 pub rejected: Vec<String>,
199 pub retryable: Vec<String>,
200 pub conflicts: u32,
201 pub commits_applied: u32,
202 pub segment_rows_applied: u32,
203 pub bootstrapping: Vec<String>,
204 pub resets: Vec<String>,
205 pub revoked: Vec<String>,
206 pub failed: Vec<String>,
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub schema_floor: Option<SchemaFloor>,
209}
210
211#[derive(Debug, Clone)]
214pub enum SyncOutcome {
215 Ok(SyncReport),
216 Failed { error_code: String, message: String },
217}
218
219impl SyncOutcome {
220 pub fn to_json(&self) -> Value {
221 match self {
222 SyncOutcome::Ok(report) => {
223 let mut map = Map::new();
224 map.insert("ok".to_owned(), Value::Bool(true));
225 map.insert(
226 "report".to_owned(),
227 serde_json::to_value(report).expect("report serializes"),
228 );
229 Value::Object(map)
230 }
231 SyncOutcome::Failed {
232 error_code,
233 message,
234 } => {
235 let mut map = Map::new();
236 map.insert("ok".to_owned(), Value::Bool(false));
237 map.insert("errorCode".to_owned(), Value::from(error_code.clone()));
238 map.insert("message".to_owned(), Value::from(message.clone()));
239 Value::Object(map)
240 }
241 }
242 }
243}
244
245#[derive(Debug, Clone, Serialize)]
246#[serde(rename_all = "camelCase")]
247pub struct ConflictRecord {
248 pub client_commit_id: String,
249 pub op_index: i32,
250 pub table: String,
251 pub row_id: String,
252 pub code: String,
253 pub server_version: i64,
254 pub server_row: Map<String, Value>,
257}
258
259#[derive(Debug, Clone, Serialize)]
260#[serde(rename_all = "camelCase")]
261pub struct RejectionRecord {
262 pub client_commit_id: String,
263 pub op_index: i32,
264 pub code: String,
265 pub retryable: bool,
266}
267
268#[derive(Debug, Clone, Serialize)]
269#[serde(rename_all = "camelCase")]
270pub struct RowState {
271 pub row_id: String,
272 pub version: i64,
275 pub values: Map<String, Value>,
276}
277
278#[derive(Debug, Clone, Serialize)]
279#[serde(rename_all = "camelCase")]
280pub struct SubscriptionStateView {
281 pub id: String,
282 pub table: String,
283 pub status: String,
285 pub cursor: i64,
286 pub has_resume_token: bool,
287 #[serde(skip_serializing_if = "Option::is_none")]
288 pub effective_scopes: Option<Value>,
289 #[serde(skip_serializing_if = "Option::is_none")]
290 pub reason_code: Option<String>,
291}
292
293#[derive(Debug, Clone, Default)]
295pub struct ClientLimits {
296 pub limit_commits: Option<i32>,
297 pub limit_snapshot_rows: Option<i32>,
298 pub max_snapshot_pages: Option<i32>,
299 pub accept: Option<u8>,
302 pub blob_cache_max_bytes: Option<i64>,
306}