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 pub outcomes_changed: bool,
76}
77
78#[derive(Debug, Clone, Serialize)]
79#[serde(tag = "kind", rename_all = "camelCase")]
80pub enum SyncIntent {
81 None,
82 Interactive,
83 Background {
84 #[serde(rename = "delayMs")]
85 delay_ms: u64,
86 },
87}
88
89#[derive(Debug, Clone, Serialize)]
90pub struct CommandEffects {
91 pub sync: SyncIntent,
92}
93
94impl CommandEffects {
95 #[must_use]
96 pub fn none() -> Self {
97 Self {
98 sync: SyncIntent::None,
99 }
100 }
101
102 #[must_use]
103 pub fn interactive() -> Self {
104 Self {
105 sync: SyncIntent::Interactive,
106 }
107 }
108}
109
110#[derive(Debug, Clone)]
111pub struct WindowCoverage {
112 pub base: WindowBase,
113 pub units: Vec<String>,
114}
115
116#[derive(Debug, Clone, Serialize)]
117#[serde(rename_all = "camelCase")]
118pub struct WindowUnitRef {
119 pub base_key: String,
120 pub unit: String,
121}
122
123#[derive(Debug, Clone, Serialize)]
124#[serde(rename_all = "camelCase")]
125pub struct CoverageSnapshot {
126 pub complete: bool,
127 pub pending: Vec<WindowUnitRef>,
128 pub missing: Vec<WindowUnitRef>,
129}
130
131#[derive(Debug, Clone, Serialize)]
132#[serde(rename_all = "camelCase")]
133pub struct QuerySnapshot {
134 pub revision: String,
135 pub rows: Vec<Map<String, Value>>,
136 pub coverage: CoverageSnapshot,
137}
138
139impl WindowState {
140 #[must_use]
142 pub fn complete(&self, unit: &str) -> bool {
143 self.units.iter().any(|u| u == unit) && !self.pending.iter().any(|u| u == unit)
144 }
145}
146
147#[derive(Debug, Clone)]
149pub enum Mutation {
150 Upsert {
151 table: String,
152 values: Map<String, Value>,
153 base_version: Option<i64>,
154 },
155 Delete {
156 table: String,
157 row_id: String,
158 base_version: Option<i64>,
159 },
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
163#[serde(rename_all = "camelCase")]
164pub struct SchemaFloor {
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub required_schema_version: Option<i32>,
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub latest_schema_version: Option<i32>,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
175#[serde(rename_all = "camelCase")]
176pub struct LeaseState {
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub lease_id: Option<String>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub expires_at_ms: Option<i64>,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub error_code: Option<String>,
183}
184
185#[derive(Debug, Clone, Serialize)]
187#[serde(rename_all = "camelCase")]
188pub struct PresencePeer {
189 pub actor_id: String,
190 pub client_id: String,
191 pub doc: serde_json::Value,
192}
193
194#[derive(Debug, Clone, Serialize, Default)]
195#[serde(rename_all = "camelCase")]
196pub struct SyncReport {
197 pub pushed: u32,
198 pub applied: Vec<String>,
199 pub rejected: Vec<String>,
200 pub retryable: Vec<String>,
201 pub conflicts: u32,
202 pub commits_applied: u32,
203 pub segment_rows_applied: u32,
204 pub bootstrapping: Vec<String>,
205 pub resets: Vec<String>,
206 pub revoked: Vec<String>,
207 pub failed: Vec<String>,
208 #[serde(skip_serializing_if = "Option::is_none")]
209 pub schema_floor: Option<SchemaFloor>,
210}
211
212#[derive(Debug, Clone)]
215pub enum SyncOutcome {
216 Ok(SyncReport),
217 Failed { error_code: String, message: String },
218}
219
220impl SyncOutcome {
221 pub fn to_json(&self) -> Value {
222 match self {
223 SyncOutcome::Ok(report) => {
224 let mut map = Map::new();
225 map.insert("ok".to_owned(), Value::Bool(true));
226 map.insert(
227 "report".to_owned(),
228 serde_json::to_value(report).expect("report serializes"),
229 );
230 Value::Object(map)
231 }
232 SyncOutcome::Failed {
233 error_code,
234 message,
235 } => {
236 let mut map = Map::new();
237 map.insert("ok".to_owned(), Value::Bool(false));
238 map.insert("errorCode".to_owned(), Value::from(error_code.clone()));
239 map.insert("message".to_owned(), Value::from(message.clone()));
240 Value::Object(map)
241 }
242 }
243 }
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
247#[serde(rename_all = "camelCase")]
248pub struct ConflictRecord {
249 pub client_commit_id: String,
250 pub op_index: i32,
251 pub table: String,
252 pub row_id: String,
253 pub code: String,
254 pub message: String,
255 pub server_version: i64,
256 pub server_row: Map<String, Value>,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub operation: Option<CommitOperation>,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
264#[serde(rename_all = "camelCase")]
265pub struct RejectionRecord {
266 pub client_commit_id: String,
267 pub op_index: i32,
268 pub code: String,
269 pub message: String,
270 pub retryable: bool,
271 #[serde(skip_serializing_if = "Option::is_none")]
272 pub operation: Option<CommitOperation>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
277#[serde(rename_all = "camelCase")]
278pub struct CommitOperation {
279 pub table: String,
280 pub row_id: String,
281 pub op: String,
282 #[serde(skip_serializing_if = "Option::is_none")]
283 pub base_version: Option<i64>,
284 #[serde(skip_serializing_if = "Option::is_none")]
285 pub values: Option<Map<String, Value>>,
286}
287
288#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
289#[serde(rename_all = "snake_case")]
290pub enum CommitOutcomeStatus {
291 Applied,
292 Cached,
293 Conflict,
294 Rejected,
295}
296
297#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
298#[serde(rename_all = "snake_case")]
299pub enum CommitOutcomeResolution {
300 Active,
301 ResolvedKeepServer,
302 Superseded,
303 Dismissed,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
307#[serde(tag = "status", rename_all = "snake_case")]
308pub enum CommitOperationOutcome {
309 Applied {
310 #[serde(rename = "opIndex")]
311 op_index: i32,
312 },
313 Conflict {
314 conflict: ConflictRecord,
315 },
316 Error {
317 rejection: RejectionRecord,
318 },
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
322#[serde(rename_all = "camelCase")]
323pub struct CommitOutcome {
324 pub sequence: i64,
325 pub client_commit_id: String,
326 pub status: CommitOutcomeStatus,
327 pub recorded_at_ms: i64,
328 pub results: Vec<CommitOperationOutcome>,
329 pub resolution: CommitOutcomeResolution,
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub resolved_at_ms: Option<i64>,
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub replacement_client_commit_id: Option<String>,
334}
335
336#[derive(Debug, Clone, Deserialize, Default)]
337#[serde(rename_all = "camelCase")]
338pub struct CommitOutcomeQuery {
339 pub limit: Option<usize>,
340 #[serde(default)]
341 pub active_only: bool,
342}
343
344#[derive(Debug, Clone, Deserialize)]
345#[serde(rename_all = "camelCase")]
346pub struct ResolveCommitOutcomeInput {
347 pub client_commit_id: String,
348 pub resolution: CommitOutcomeResolution,
349 pub replacement_client_commit_id: Option<String>,
350}
351
352#[derive(Debug, Clone, Serialize)]
353#[serde(rename_all = "camelCase")]
354pub struct RowState {
355 pub row_id: String,
356 pub version: i64,
359 pub values: Map<String, Value>,
360}
361
362#[derive(Debug, Clone, Serialize)]
363#[serde(rename_all = "camelCase")]
364pub struct SubscriptionStateView {
365 pub id: String,
366 pub table: String,
367 pub status: String,
369 pub cursor: i64,
370 pub has_resume_token: bool,
371 #[serde(skip_serializing_if = "Option::is_none")]
372 pub effective_scopes: Option<Value>,
373 #[serde(skip_serializing_if = "Option::is_none")]
374 pub reason_code: Option<String>,
375}
376
377#[derive(Debug, Clone, Default)]
379pub struct ClientLimits {
380 pub limit_commits: Option<i32>,
381 pub limit_snapshot_rows: Option<i32>,
382 pub max_snapshot_pages: Option<i32>,
383 pub accept: Option<u8>,
386 pub blob_cache_max_bytes: Option<i64>,
390 pub outcome_retention_max_entries: Option<usize>,
393}