create-syncular-app 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -27
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +131 -179
- package/dist/constants.d.ts +40 -0
- package/dist/constants.js +46 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/scaffold.d.ts +44 -0
- package/dist/scaffold.js +106 -0
- package/package.json +22 -15
- package/src/cli.ts +174 -0
- package/src/constants.ts +53 -0
- package/src/index.ts +3 -0
- package/src/scaffold.ts +161 -0
- package/template/minimal/README.md +45 -0
- package/template/minimal/gitignore +5 -0
- package/template/minimal/migrations/0001_initial/up.sql +9 -0
- package/template/minimal/package.json +22 -0
- package/template/minimal/src/clients.ts +54 -0
- package/template/minimal/src/make-client.ts +26 -0
- package/template/minimal/src/server.ts +39 -0
- package/template/minimal/src/smoke.test.ts +70 -0
- package/template/minimal/src/syncular.generated.ts +66 -0
- package/template/minimal/syncular.ir.json +66 -0
- package/template/minimal/syncular.json +17 -0
- package/template/minimal/tsconfig.json +16 -0
- package/template/web/README.md +63 -0
- package/template/web/gitignore +5 -0
- package/template/web/migrations/0001_initial/up.sql +11 -0
- package/template/web/package.json +22 -0
- package/template/web/src/frontend/index.html +37 -0
- package/template/web/src/frontend/main.ts +191 -0
- package/template/web/src/frontend/worker.ts +9 -0
- package/template/web/src/server.ts +183 -0
- package/template/web/src/smoke.test.ts +91 -0
- package/template/web/src/syncular.generated.ts +74 -0
- package/template/web/syncular.ir.json +76 -0
- package/template/web/syncular.json +17 -0
- package/template/web/tsconfig.json +16 -0
- package/template/README.md +0 -122
- package/template/_gitignore +0 -5
- package/template/generated/kotlin/SyncularApp.kt +0 -1005
- package/template/generated/rust/diesel_tables.rs +0 -142
- package/template/generated/rust/migrations.rs +0 -32
- package/template/generated/rust/schema.rs +0 -15
- package/template/generated/rust/syncular.rs +0 -926
- package/template/generated/swift/SyncularApp.swift +0 -1191
- package/template/generated/syncular.codegen.json +0 -19
- package/template/index.html +0 -13
- package/template/migrations/0001_initial/down.sql +0 -1
- package/template/migrations/0001_initial/up.sql +0 -8
- package/template/package.json +0 -33
- package/template/scripts/dev.ts +0 -42
- package/template/src/app.tsx +0 -231
- package/template/src/client/syncular.ts +0 -61
- package/template/src/generated/syncular.generated.ts +0 -769
- package/template/src/generated/syncular.server.generated.ts +0 -512
- package/template/src/main.tsx +0 -5
- package/template/src/server/sync-server.ts +0 -129
- package/template/src/styles.css +0 -233
- package/template/syncular.app.ts +0 -23
- package/template/syncular.schema.json +0 -145
- package/template/tsconfig.json +0 -18
- package/template/vite.config.ts +0 -9
|
@@ -1,926 +0,0 @@
|
|
|
1
|
-
// @generated by `cargo run --manifest-path rust/Cargo.toml -p syncular-codegen --`
|
|
2
|
-
// Source: migrations/*.sql
|
|
3
|
-
|
|
4
|
-
use serde_json::{json, Map, Value};
|
|
5
|
-
pub use syncular_client::app_schema::{
|
|
6
|
-
AppTableMetadata, ColumnMetadata, CrdtYjsFieldMetadata, EncryptedFieldMetadata, ScopeMetadata,
|
|
7
|
-
ScopeSource,
|
|
8
|
-
};
|
|
9
|
-
#[allow(unused_imports)]
|
|
10
|
-
use syncular_client::client::{
|
|
11
|
-
SubscriptionSpec, SyncChangedRow, SyncularClientConfig, SyncularCommandHistoryExecutor,
|
|
12
|
-
SyncularEncryptedCrdtMutationExecutor, SyncularLeasedMutationExecutor,
|
|
13
|
-
SyncularMutationExecutor,
|
|
14
|
-
};
|
|
15
|
-
use syncular_client::command_history::{
|
|
16
|
-
CommandHistoryEntry, CommandHistoryReceipt, CommandHistoryRecord, CommandHistoryState,
|
|
17
|
-
};
|
|
18
|
-
#[allow(unused_imports)]
|
|
19
|
-
use syncular_client::crdt_yjs::{YjsUpdateEnvelope, YJS_PAYLOAD_KEY};
|
|
20
|
-
use syncular_client::encryption::FieldEncryptionRule;
|
|
21
|
-
use syncular_client::error::{Result, SyncularError};
|
|
22
|
-
use syncular_client::protocol::{
|
|
23
|
-
random_syncular_id, IntoSyncularMutation, MutationCommit, MutationReceipt,
|
|
24
|
-
PendingSyncularMutation, SyncOperation, SyncularMutationBatch, SyncularMutationKind,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
pub const APP_TABLES: &[&str] = &["tasks"];
|
|
28
|
-
|
|
29
|
-
pub const TASKS_TABLE: &str = "tasks";
|
|
30
|
-
|
|
31
|
-
pub const TASKS_COLUMNS: &[ColumnMetadata] = &[
|
|
32
|
-
ColumnMetadata {
|
|
33
|
-
name: "id",
|
|
34
|
-
type_family: "text",
|
|
35
|
-
notnull_required: false,
|
|
36
|
-
primary_key: true,
|
|
37
|
-
},
|
|
38
|
-
ColumnMetadata {
|
|
39
|
-
name: "title",
|
|
40
|
-
type_family: "text",
|
|
41
|
-
notnull_required: true,
|
|
42
|
-
primary_key: false,
|
|
43
|
-
},
|
|
44
|
-
ColumnMetadata {
|
|
45
|
-
name: "completed",
|
|
46
|
-
type_family: "integer",
|
|
47
|
-
notnull_required: true,
|
|
48
|
-
primary_key: false,
|
|
49
|
-
},
|
|
50
|
-
ColumnMetadata {
|
|
51
|
-
name: "user_id",
|
|
52
|
-
type_family: "text",
|
|
53
|
-
notnull_required: true,
|
|
54
|
-
primary_key: false,
|
|
55
|
-
},
|
|
56
|
-
ColumnMetadata {
|
|
57
|
-
name: "created_at",
|
|
58
|
-
type_family: "integer",
|
|
59
|
-
notnull_required: true,
|
|
60
|
-
primary_key: false,
|
|
61
|
-
},
|
|
62
|
-
ColumnMetadata {
|
|
63
|
-
name: "server_version",
|
|
64
|
-
type_family: "integer",
|
|
65
|
-
notnull_required: true,
|
|
66
|
-
primary_key: false,
|
|
67
|
-
},
|
|
68
|
-
];
|
|
69
|
-
|
|
70
|
-
pub const TASKS_BLOB_COLUMNS: &[&str] = &[];
|
|
71
|
-
|
|
72
|
-
pub const TASKS_CRDT_YJS_FIELDS: &[CrdtYjsFieldMetadata] = &[];
|
|
73
|
-
|
|
74
|
-
pub const TASKS_ENCRYPTED_FIELDS: &[EncryptedFieldMetadata] = &[];
|
|
75
|
-
|
|
76
|
-
pub const TASKS_SCOPES: &[ScopeMetadata] = &[ScopeMetadata {
|
|
77
|
-
name: "user_id",
|
|
78
|
-
column: "user_id",
|
|
79
|
-
source: ScopeSource::ActorId,
|
|
80
|
-
required: true,
|
|
81
|
-
}];
|
|
82
|
-
|
|
83
|
-
pub const TASKS_METADATA: AppTableMetadata = AppTableMetadata {
|
|
84
|
-
name: "tasks",
|
|
85
|
-
primary_key_column: "id",
|
|
86
|
-
server_version_column: "server_version",
|
|
87
|
-
soft_delete_column: None,
|
|
88
|
-
subscription_id: "sub-tasks",
|
|
89
|
-
columns: TASKS_COLUMNS,
|
|
90
|
-
blob_columns: TASKS_BLOB_COLUMNS,
|
|
91
|
-
crdt_yjs_fields: TASKS_CRDT_YJS_FIELDS,
|
|
92
|
-
encrypted_fields: TASKS_ENCRYPTED_FIELDS,
|
|
93
|
-
scopes: TASKS_SCOPES,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
pub const APP_TABLE_METADATA: &[AppTableMetadata] = &[TASKS_METADATA];
|
|
97
|
-
|
|
98
|
-
pub fn table_metadata(table: &str) -> Option<&'static AppTableMetadata> {
|
|
99
|
-
APP_TABLE_METADATA
|
|
100
|
-
.iter()
|
|
101
|
-
.find(|metadata| metadata.name == table)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
|
105
|
-
pub struct TaskChangedFields {
|
|
106
|
-
pub id: bool,
|
|
107
|
-
pub title: bool,
|
|
108
|
-
pub completed: bool,
|
|
109
|
-
pub user_id: bool,
|
|
110
|
-
pub created_at: bool,
|
|
111
|
-
pub server_version: bool,
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
impl TaskChangedFields {
|
|
115
|
-
pub fn from_columns(columns: &[String]) -> Self {
|
|
116
|
-
Self {
|
|
117
|
-
id: columns.iter().any(|column| column == "id"),
|
|
118
|
-
title: columns.iter().any(|column| column == "title"),
|
|
119
|
-
completed: columns.iter().any(|column| column == "completed"),
|
|
120
|
-
user_id: columns.iter().any(|column| column == "user_id"),
|
|
121
|
-
created_at: columns.iter().any(|column| column == "created_at"),
|
|
122
|
-
server_version: columns.iter().any(|column| column == "server_version"),
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
pub fn contains(&self, column: &str) -> bool {
|
|
127
|
-
match column {
|
|
128
|
-
"id" => self.id,
|
|
129
|
-
"title" => self.title,
|
|
130
|
-
"completed" => self.completed,
|
|
131
|
-
"user_id" => self.user_id,
|
|
132
|
-
"created_at" => self.created_at,
|
|
133
|
-
"server_version" => self.server_version,
|
|
134
|
-
_ => false,
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
#[derive(Debug, Clone, Copy)]
|
|
140
|
-
pub struct TaskChangedRow<'a> {
|
|
141
|
-
pub raw: &'a SyncChangedRow,
|
|
142
|
-
pub changed: TaskChangedFields,
|
|
143
|
-
pub crdt: TaskChangedFields,
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
impl<'a> TaskChangedRow<'a> {
|
|
147
|
-
pub fn from_raw(row: &'a SyncChangedRow) -> Option<Self> {
|
|
148
|
-
if row.table != "tasks" {
|
|
149
|
-
return None;
|
|
150
|
-
}
|
|
151
|
-
Some(Self {
|
|
152
|
-
raw: row,
|
|
153
|
-
changed: TaskChangedFields::from_columns(&row.changed_fields),
|
|
154
|
-
crdt: TaskChangedFields::from_columns(&row.crdt_fields),
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
pub fn row_id(&self) -> Option<&str> {
|
|
159
|
-
self.raw.row_id.as_deref()
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
pub fn is_insert(&self) -> bool {
|
|
163
|
-
self.raw.operation == "insert"
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
pub fn is_update(&self) -> bool {
|
|
167
|
-
self.raw.operation == "update"
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
pub fn is_delete(&self) -> bool {
|
|
171
|
-
self.raw.operation == "delete"
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
pub fn task_changed_rows<'a>(
|
|
176
|
-
rows: impl IntoIterator<Item = &'a SyncChangedRow>,
|
|
177
|
-
) -> Vec<TaskChangedRow<'a>> {
|
|
178
|
-
rows.into_iter()
|
|
179
|
-
.filter_map(TaskChangedRow::from_raw)
|
|
180
|
-
.collect()
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
pub fn generated_field_encryption_rules() -> Vec<FieldEncryptionRule> {
|
|
184
|
-
Vec::new()
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
pub fn default_subscriptions(config: &SyncularClientConfig) -> Vec<SubscriptionSpec> {
|
|
188
|
-
vec![tasks_subscription(&config.actor_id)]
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
pub fn default_subscriptions_with_bootstrap_phases(
|
|
192
|
-
config: &SyncularClientConfig,
|
|
193
|
-
bootstrap_phases: &[(&str, i64)],
|
|
194
|
-
) -> Vec<SubscriptionSpec> {
|
|
195
|
-
let mut subscriptions = default_subscriptions(config);
|
|
196
|
-
apply_bootstrap_phases(&mut subscriptions, bootstrap_phases);
|
|
197
|
-
subscriptions
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
pub fn apply_bootstrap_phases(
|
|
201
|
-
subscriptions: &mut [SubscriptionSpec],
|
|
202
|
-
bootstrap_phases: &[(&str, i64)],
|
|
203
|
-
) {
|
|
204
|
-
for subscription in subscriptions {
|
|
205
|
-
if let Some((_, phase)) = bootstrap_phases
|
|
206
|
-
.iter()
|
|
207
|
-
.find(|(key, _)| *key == subscription.id || *key == subscription.table)
|
|
208
|
-
{
|
|
209
|
-
subscription.bootstrap_phase = *phase;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
pub fn with_bootstrap_phase(
|
|
215
|
-
mut subscription: SubscriptionSpec,
|
|
216
|
-
bootstrap_phase: i64,
|
|
217
|
-
) -> SubscriptionSpec {
|
|
218
|
-
subscription.bootstrap_phase = bootstrap_phase;
|
|
219
|
-
subscription
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
pub fn tasks_subscription(user_id: &str) -> SubscriptionSpec {
|
|
223
|
-
let mut scopes = Map::new();
|
|
224
|
-
scopes.insert("user_id".to_string(), json!(user_id));
|
|
225
|
-
let params = Map::new();
|
|
226
|
-
|
|
227
|
-
SubscriptionSpec {
|
|
228
|
-
id: "sub-tasks".to_string(),
|
|
229
|
-
table: "tasks".to_string(),
|
|
230
|
-
scopes,
|
|
231
|
-
params,
|
|
232
|
-
bootstrap_phase: 0,
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
#[derive(Debug, Clone)]
|
|
237
|
-
pub struct NewTask {
|
|
238
|
-
pub id: String,
|
|
239
|
-
pub title: String,
|
|
240
|
-
pub completed: i32,
|
|
241
|
-
pub user_id: String,
|
|
242
|
-
pub created_at: i64,
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
impl NewTask {
|
|
246
|
-
pub fn new(id: &str, title: &str, user_id: &str) -> Self {
|
|
247
|
-
Self {
|
|
248
|
-
id: id.to_string(),
|
|
249
|
-
title: title.to_string(),
|
|
250
|
-
completed: 0,
|
|
251
|
-
user_id: user_id.to_string(),
|
|
252
|
-
created_at: 0,
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
pub fn with_generated_id(title: &str, user_id: &str) -> Self {
|
|
257
|
-
Self {
|
|
258
|
-
id: random_syncular_id(),
|
|
259
|
-
title: title.to_string(),
|
|
260
|
-
completed: 0,
|
|
261
|
-
user_id: user_id.to_string(),
|
|
262
|
-
created_at: 0,
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
pub fn row_json(&self) -> Value {
|
|
267
|
-
let mut row = Map::new();
|
|
268
|
-
row.insert("id".to_string(), json!(&self.id));
|
|
269
|
-
row.insert("title".to_string(), json!(&self.title));
|
|
270
|
-
row.insert("completed".to_string(), json!(&self.completed));
|
|
271
|
-
row.insert("user_id".to_string(), json!(&self.user_id));
|
|
272
|
-
row.insert("created_at".to_string(), json!(&self.created_at));
|
|
273
|
-
Value::Object(row)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
pub fn sync_operation(&self) -> SyncOperation {
|
|
277
|
-
let mut payload = Map::new();
|
|
278
|
-
payload.insert("title".to_string(), json!(&self.title));
|
|
279
|
-
payload.insert("completed".to_string(), json!(&self.completed));
|
|
280
|
-
payload.insert("user_id".to_string(), json!(&self.user_id));
|
|
281
|
-
payload.insert("created_at".to_string(), json!(&self.created_at));
|
|
282
|
-
|
|
283
|
-
SyncOperation {
|
|
284
|
-
table: "tasks".to_string(),
|
|
285
|
-
row_id: self.id.clone(),
|
|
286
|
-
op: "upsert".to_string(),
|
|
287
|
-
payload: Some(Value::Object(payload)),
|
|
288
|
-
base_version: Some(0),
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
impl IntoSyncularMutation for NewTask {
|
|
294
|
-
fn into_syncular_mutation(self) -> PendingSyncularMutation {
|
|
295
|
-
let row_id = self.id.clone();
|
|
296
|
-
PendingSyncularMutation {
|
|
297
|
-
kind: SyncularMutationKind::Insert,
|
|
298
|
-
table: "tasks".to_string(),
|
|
299
|
-
row_id,
|
|
300
|
-
payload: self.sync_operation().payload,
|
|
301
|
-
base_version: None,
|
|
302
|
-
local_row: Some(self.row_json()),
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
#[derive(Debug, Clone)]
|
|
308
|
-
pub struct TaskPatch {
|
|
309
|
-
row_id: String,
|
|
310
|
-
base_version: Option<i64>,
|
|
311
|
-
title: Option<String>,
|
|
312
|
-
completed: Option<i32>,
|
|
313
|
-
user_id: Option<String>,
|
|
314
|
-
created_at: Option<i64>,
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
impl TaskPatch {
|
|
318
|
-
pub fn new(row_id: &str) -> Self {
|
|
319
|
-
Self {
|
|
320
|
-
row_id: row_id.to_string(),
|
|
321
|
-
base_version: None,
|
|
322
|
-
title: None,
|
|
323
|
-
completed: None,
|
|
324
|
-
user_id: None,
|
|
325
|
-
created_at: None,
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
pub fn base_version(mut self, base_version: i64) -> Self {
|
|
330
|
-
self.base_version = Some(base_version);
|
|
331
|
-
self
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
pub fn title(mut self, title: &str) -> Self {
|
|
335
|
-
self.title = Some(title.to_string());
|
|
336
|
-
self
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
pub fn completed(mut self, completed: i32) -> Self {
|
|
340
|
-
self.completed = Some(completed);
|
|
341
|
-
self
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
pub fn user_id(mut self, user_id: &str) -> Self {
|
|
345
|
-
self.user_id = Some(user_id.to_string());
|
|
346
|
-
self
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
pub fn created_at(mut self, created_at: i64) -> Self {
|
|
350
|
-
self.created_at = Some(created_at);
|
|
351
|
-
self
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
pub fn payload_json(&self) -> Value {
|
|
355
|
-
let mut payload = Map::new();
|
|
356
|
-
if let Some(value) = &self.title {
|
|
357
|
-
payload.insert("title".to_string(), json!(value));
|
|
358
|
-
}
|
|
359
|
-
if let Some(value) = &self.completed {
|
|
360
|
-
payload.insert("completed".to_string(), json!(value));
|
|
361
|
-
}
|
|
362
|
-
if let Some(value) = &self.user_id {
|
|
363
|
-
payload.insert("user_id".to_string(), json!(value));
|
|
364
|
-
}
|
|
365
|
-
if let Some(value) = &self.created_at {
|
|
366
|
-
payload.insert("created_at".to_string(), json!(value));
|
|
367
|
-
}
|
|
368
|
-
Value::Object(payload)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
pub fn sync_operation(&self) -> SyncOperation {
|
|
372
|
-
SyncOperation {
|
|
373
|
-
table: "tasks".to_string(),
|
|
374
|
-
row_id: self.row_id.clone(),
|
|
375
|
-
op: "upsert".to_string(),
|
|
376
|
-
payload: Some(self.payload_json()),
|
|
377
|
-
base_version: self.base_version,
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
impl IntoSyncularMutation for TaskPatch {
|
|
383
|
-
fn into_syncular_mutation(self) -> PendingSyncularMutation {
|
|
384
|
-
let payload = self.payload_json();
|
|
385
|
-
PendingSyncularMutation {
|
|
386
|
-
kind: SyncularMutationKind::Update,
|
|
387
|
-
table: "tasks".to_string(),
|
|
388
|
-
row_id: self.row_id,
|
|
389
|
-
payload: Some(payload),
|
|
390
|
-
base_version: self.base_version,
|
|
391
|
-
local_row: None,
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
#[derive(Debug, Clone)]
|
|
397
|
-
pub struct DeleteTask {
|
|
398
|
-
row_id: String,
|
|
399
|
-
base_version: Option<i64>,
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
impl DeleteTask {
|
|
403
|
-
pub fn new(row_id: &str) -> Self {
|
|
404
|
-
Self {
|
|
405
|
-
row_id: row_id.to_string(),
|
|
406
|
-
base_version: None,
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
pub fn base_version(mut self, base_version: i64) -> Self {
|
|
411
|
-
self.base_version = Some(base_version);
|
|
412
|
-
self
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
pub fn sync_operation(&self) -> SyncOperation {
|
|
416
|
-
SyncOperation {
|
|
417
|
-
table: "tasks".to_string(),
|
|
418
|
-
row_id: self.row_id.clone(),
|
|
419
|
-
op: "delete".to_string(),
|
|
420
|
-
payload: None,
|
|
421
|
-
base_version: self.base_version,
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
impl IntoSyncularMutation for DeleteTask {
|
|
427
|
-
fn into_syncular_mutation(self) -> PendingSyncularMutation {
|
|
428
|
-
PendingSyncularMutation {
|
|
429
|
-
kind: SyncularMutationKind::Delete,
|
|
430
|
-
table: "tasks".to_string(),
|
|
431
|
-
row_id: self.row_id,
|
|
432
|
-
payload: None,
|
|
433
|
-
base_version: self.base_version,
|
|
434
|
-
local_row: None,
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
pub fn delete_task(row_id: &str, base_version: Option<i64>) -> SyncOperation {
|
|
440
|
-
SyncOperation {
|
|
441
|
-
table: "tasks".to_string(),
|
|
442
|
-
row_id: row_id.to_string(),
|
|
443
|
-
op: "delete".to_string(),
|
|
444
|
-
payload: None,
|
|
445
|
-
base_version,
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
#[derive(Debug, Clone)]
|
|
450
|
-
pub struct InsertReceipt {
|
|
451
|
-
pub id: String,
|
|
452
|
-
pub commit: MutationReceipt,
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
#[derive(Debug, Clone)]
|
|
456
|
-
pub struct InsertManyReceipt {
|
|
457
|
-
pub ids: Vec<String>,
|
|
458
|
-
pub commit: MutationReceipt,
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
pub trait SyncularGeneratedMutationsExt: SyncularMutationExecutor {
|
|
462
|
-
fn mutations(&mut self) -> SyncularAppMutations<'_, Self>
|
|
463
|
-
where
|
|
464
|
-
Self: Sized,
|
|
465
|
-
{
|
|
466
|
-
SyncularAppMutations { client: self }
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
fn leased_mutations(&mut self) -> SyncularAppLeasedMutations<'_, Self>
|
|
470
|
-
where
|
|
471
|
-
Self: Sized + SyncularLeasedMutationExecutor,
|
|
472
|
-
{
|
|
473
|
-
SyncularAppLeasedMutations { client: self }
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
fn command_history(&mut self) -> SyncularAppCommandHistory<'_, Self>
|
|
477
|
-
where
|
|
478
|
-
Self: Sized + SyncularCommandHistoryExecutor,
|
|
479
|
-
{
|
|
480
|
-
SyncularAppCommandHistory { client: self }
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
fn commit<R>(
|
|
484
|
-
&mut self,
|
|
485
|
-
f: impl FnOnce(&mut SyncularAppMutationTx<'_>) -> Result<R>,
|
|
486
|
-
) -> Result<MutationCommit<R>>
|
|
487
|
-
where
|
|
488
|
-
Self: Sized,
|
|
489
|
-
{
|
|
490
|
-
let mut batch = SyncularMutationBatch::new();
|
|
491
|
-
let result = {
|
|
492
|
-
let mut tx = SyncularAppMutationTx { batch: &mut batch };
|
|
493
|
-
f(&mut tx)?
|
|
494
|
-
};
|
|
495
|
-
let commit = self.apply_mutation_batch(batch)?;
|
|
496
|
-
Ok(MutationCommit { result, commit })
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
fn commit_with_history<R>(
|
|
500
|
-
&mut self,
|
|
501
|
-
f: impl FnOnce(&mut SyncularAppMutationTx<'_>) -> Result<R>,
|
|
502
|
-
) -> Result<MutationCommit<R>>
|
|
503
|
-
where
|
|
504
|
-
Self: Sized + SyncularCommandHistoryExecutor,
|
|
505
|
-
{
|
|
506
|
-
syncular_commit_with_history(self, "mutations", f)
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
fn commit_leased<R>(
|
|
510
|
-
&mut self,
|
|
511
|
-
f: impl FnOnce(&mut SyncularAppMutationTx<'_>) -> Result<R>,
|
|
512
|
-
) -> Result<MutationCommit<R>>
|
|
513
|
-
where
|
|
514
|
-
Self: Sized + SyncularLeasedMutationExecutor,
|
|
515
|
-
{
|
|
516
|
-
let mut batch = SyncularMutationBatch::new();
|
|
517
|
-
let result = {
|
|
518
|
-
let mut tx = SyncularAppMutationTx { batch: &mut batch };
|
|
519
|
-
f(&mut tx)?
|
|
520
|
-
};
|
|
521
|
-
let commit = self.apply_leased_mutation_batch(batch)?;
|
|
522
|
-
Ok(MutationCommit { result, commit })
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
fn commit_leased_with_history<R>(
|
|
526
|
-
&mut self,
|
|
527
|
-
f: impl FnOnce(&mut SyncularAppMutationTx<'_>) -> Result<R>,
|
|
528
|
-
) -> Result<MutationCommit<R>>
|
|
529
|
-
where
|
|
530
|
-
Self: Sized + SyncularCommandHistoryExecutor,
|
|
531
|
-
{
|
|
532
|
-
syncular_commit_with_history(self, "leasedMutations", f)
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
impl<C> SyncularGeneratedMutationsExt for C where C: SyncularMutationExecutor {}
|
|
537
|
-
|
|
538
|
-
pub struct SyncularAppMutations<'a, C: SyncularMutationExecutor + ?Sized> {
|
|
539
|
-
client: &'a mut C,
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
pub struct SyncularAppLeasedMutations<'a, C: SyncularLeasedMutationExecutor + ?Sized> {
|
|
543
|
-
client: &'a mut C,
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
pub struct SyncularAppMutationTx<'a> {
|
|
547
|
-
batch: &'a mut SyncularMutationBatch,
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
pub struct SyncularAppCommandHistory<'a, C: SyncularCommandHistoryExecutor + ?Sized> {
|
|
551
|
-
client: &'a mut C,
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
impl<C> SyncularAppCommandHistory<'_, C>
|
|
555
|
-
where
|
|
556
|
-
C: SyncularCommandHistoryExecutor + ?Sized,
|
|
557
|
-
{
|
|
558
|
-
pub fn can_undo(&mut self) -> Result<bool> {
|
|
559
|
-
Ok(self
|
|
560
|
-
.client
|
|
561
|
-
.command_history_latest(CommandHistoryState::Done)?
|
|
562
|
-
.is_some())
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
pub fn can_redo(&mut self) -> Result<bool> {
|
|
566
|
-
Ok(self
|
|
567
|
-
.client
|
|
568
|
-
.command_history_latest(CommandHistoryState::Undone)?
|
|
569
|
-
.is_some())
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
pub fn latest_undo(&mut self) -> Result<Option<CommandHistoryRecord>> {
|
|
573
|
-
self.client
|
|
574
|
-
.command_history_latest(CommandHistoryState::Done)
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
pub fn latest_redo(&mut self) -> Result<Option<CommandHistoryRecord>> {
|
|
578
|
-
self.client
|
|
579
|
-
.command_history_latest(CommandHistoryState::Undone)
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
pub fn undo_last(&mut self) -> Result<CommandHistoryReceipt> {
|
|
583
|
-
syncular_replay_command_history(self.client, CommandHistoryState::Done)
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
pub fn redo_last(&mut self) -> Result<CommandHistoryReceipt> {
|
|
587
|
-
syncular_replay_command_history(self.client, CommandHistoryState::Undone)
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
fn syncular_commit_with_history<C, R>(
|
|
592
|
-
client: &mut C,
|
|
593
|
-
mutation_scope: &str,
|
|
594
|
-
f: impl FnOnce(&mut SyncularAppMutationTx<'_>) -> Result<R>,
|
|
595
|
-
) -> Result<MutationCommit<R>>
|
|
596
|
-
where
|
|
597
|
-
C: SyncularCommandHistoryExecutor + ?Sized,
|
|
598
|
-
{
|
|
599
|
-
let mut batch = SyncularMutationBatch::new();
|
|
600
|
-
let result = {
|
|
601
|
-
let mut tx = SyncularAppMutationTx { batch: &mut batch };
|
|
602
|
-
f(&mut tx)?
|
|
603
|
-
};
|
|
604
|
-
let commit = client.apply_command_history_tracked_batch(mutation_scope, batch)?;
|
|
605
|
-
Ok(MutationCommit { result, commit })
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
fn syncular_replay_command_history<C>(
|
|
609
|
-
client: &mut C,
|
|
610
|
-
state: CommandHistoryState,
|
|
611
|
-
) -> Result<CommandHistoryReceipt>
|
|
612
|
-
where
|
|
613
|
-
C: SyncularCommandHistoryExecutor + ?Sized,
|
|
614
|
-
{
|
|
615
|
-
let command = client.command_history_latest(state)?.ok_or_else(|| {
|
|
616
|
-
syncular_command_history_error(
|
|
617
|
-
"sync.command_history_empty",
|
|
618
|
-
"there is no Syncular command to replay",
|
|
619
|
-
)
|
|
620
|
-
})?;
|
|
621
|
-
syncular_assert_command_history_safe(&command)?;
|
|
622
|
-
let undo = state == CommandHistoryState::Done;
|
|
623
|
-
syncular_assert_command_history_current_rows(client, &command, undo)?;
|
|
624
|
-
|
|
625
|
-
let mut batch = SyncularMutationBatch::new();
|
|
626
|
-
if undo {
|
|
627
|
-
for entry in command.entries.iter().rev() {
|
|
628
|
-
batch.push(syncular_command_history_mutation_for_snapshot(
|
|
629
|
-
&entry.table,
|
|
630
|
-
&entry.row_id,
|
|
631
|
-
&entry.before,
|
|
632
|
-
)?);
|
|
633
|
-
}
|
|
634
|
-
} else {
|
|
635
|
-
for entry in &command.entries {
|
|
636
|
-
batch.push(syncular_command_history_mutation_for_snapshot(
|
|
637
|
-
&entry.table,
|
|
638
|
-
&entry.row_id,
|
|
639
|
-
&entry.after,
|
|
640
|
-
)?);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
let commit = client.apply_command_history_batch(&command.mutation_scope, batch)?;
|
|
645
|
-
let next_state = if undo {
|
|
646
|
-
CommandHistoryState::Undone
|
|
647
|
-
} else {
|
|
648
|
-
CommandHistoryState::Done
|
|
649
|
-
};
|
|
650
|
-
client.command_history_mark(&command.id, next_state, &commit)?;
|
|
651
|
-
Ok(CommandHistoryReceipt {
|
|
652
|
-
command_id: command.id,
|
|
653
|
-
commit,
|
|
654
|
-
})
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
fn syncular_assert_command_history_current_rows<C>(
|
|
658
|
-
client: &mut C,
|
|
659
|
-
command: &CommandHistoryRecord,
|
|
660
|
-
undo: bool,
|
|
661
|
-
) -> Result<()>
|
|
662
|
-
where
|
|
663
|
-
C: SyncularCommandHistoryExecutor + ?Sized,
|
|
664
|
-
{
|
|
665
|
-
for entry in &command.entries {
|
|
666
|
-
let current = client.command_history_current_row_json(&entry.table, &entry.row_id)?;
|
|
667
|
-
let expected = if undo { &entry.after } else { &entry.before };
|
|
668
|
-
if ¤t != expected {
|
|
669
|
-
return Err(syncular_command_history_error(
|
|
670
|
-
"sync.command_history_conflict",
|
|
671
|
-
&format!(
|
|
672
|
-
"cannot replay Syncular command {} because {}.{} changed since it was recorded",
|
|
673
|
-
command.id, entry.table, entry.row_id
|
|
674
|
-
),
|
|
675
|
-
));
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
Ok(())
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
fn syncular_assert_command_history_safe(command: &CommandHistoryRecord) -> Result<()> {
|
|
682
|
-
for entry in &command.entries {
|
|
683
|
-
let unsafe_fields = syncular_command_history_unsafe_fields(entry)?;
|
|
684
|
-
if !unsafe_fields.is_empty() {
|
|
685
|
-
return Err(syncular_command_history_error(
|
|
686
|
-
"sync.command_history_unsafe_field",
|
|
687
|
-
&format!(
|
|
688
|
-
"cannot replay Syncular command {} because {}.{} changed unsafe fields: {}",
|
|
689
|
-
command.id,
|
|
690
|
-
entry.table,
|
|
691
|
-
entry.row_id,
|
|
692
|
-
unsafe_fields.join(", ")
|
|
693
|
-
),
|
|
694
|
-
));
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
Ok(())
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
fn syncular_command_history_mutation_for_snapshot(
|
|
701
|
-
table: &str,
|
|
702
|
-
row_id: &str,
|
|
703
|
-
snapshot: &Option<Value>,
|
|
704
|
-
) -> Result<PendingSyncularMutation> {
|
|
705
|
-
match snapshot {
|
|
706
|
-
None => Ok(PendingSyncularMutation {
|
|
707
|
-
kind: SyncularMutationKind::Delete,
|
|
708
|
-
table: table.to_string(),
|
|
709
|
-
row_id: row_id.to_string(),
|
|
710
|
-
payload: None,
|
|
711
|
-
base_version: None,
|
|
712
|
-
local_row: None,
|
|
713
|
-
}),
|
|
714
|
-
Some(snapshot) => Ok(PendingSyncularMutation {
|
|
715
|
-
kind: SyncularMutationKind::Upsert,
|
|
716
|
-
table: table.to_string(),
|
|
717
|
-
row_id: row_id.to_string(),
|
|
718
|
-
payload: Some(syncular_command_history_payload_for_snapshot(
|
|
719
|
-
table, snapshot,
|
|
720
|
-
)?),
|
|
721
|
-
base_version: None,
|
|
722
|
-
local_row: None,
|
|
723
|
-
}),
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
fn syncular_command_history_payload_for_snapshot(table: &str, snapshot: &Value) -> Result<Value> {
|
|
728
|
-
let Some(object) = snapshot.as_object() else {
|
|
729
|
-
return Err(syncular_command_history_error(
|
|
730
|
-
"sync.command_history_conflict",
|
|
731
|
-
"command history snapshot must be a JSON object",
|
|
732
|
-
));
|
|
733
|
-
};
|
|
734
|
-
let mut payload: Map<String, Value> = object.clone();
|
|
735
|
-
match table {
|
|
736
|
-
"tasks" => {
|
|
737
|
-
payload.remove("id");
|
|
738
|
-
payload.remove("server_version");
|
|
739
|
-
Ok(())
|
|
740
|
-
}
|
|
741
|
-
_ => Err(syncular_command_history_error(
|
|
742
|
-
"sync.command_history_table_unsupported",
|
|
743
|
-
&format!("cannot replay undo history for unsupported table {table}"),
|
|
744
|
-
)),
|
|
745
|
-
}?;
|
|
746
|
-
Ok(Value::Object(payload))
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
fn syncular_command_history_unsafe_fields(
|
|
750
|
-
entry: &CommandHistoryEntry,
|
|
751
|
-
) -> Result<Vec<&'static str>> {
|
|
752
|
-
#[allow(unused_mut)]
|
|
753
|
-
let mut fields = Vec::new();
|
|
754
|
-
match entry.table.as_str() {
|
|
755
|
-
"tasks" => Ok(fields),
|
|
756
|
-
_ => Err(syncular_command_history_error(
|
|
757
|
-
"sync.command_history_table_unsupported",
|
|
758
|
-
&format!(
|
|
759
|
-
"cannot replay undo history for unsupported table {}",
|
|
760
|
-
entry.table
|
|
761
|
-
),
|
|
762
|
-
)),
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
#[allow(dead_code)]
|
|
767
|
-
fn syncular_command_history_push_unsafe_field(
|
|
768
|
-
fields: &mut Vec<&'static str>,
|
|
769
|
-
entry: &CommandHistoryEntry,
|
|
770
|
-
field: &'static str,
|
|
771
|
-
) {
|
|
772
|
-
if syncular_command_history_snapshot_field(&entry.before, field)
|
|
773
|
-
!= syncular_command_history_snapshot_field(&entry.after, field)
|
|
774
|
-
&& !fields.contains(&field)
|
|
775
|
-
{
|
|
776
|
-
fields.push(field);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
#[allow(dead_code)]
|
|
781
|
-
fn syncular_command_history_snapshot_field<'a>(
|
|
782
|
-
snapshot: &'a Option<Value>,
|
|
783
|
-
field: &str,
|
|
784
|
-
) -> Option<&'a Value> {
|
|
785
|
-
snapshot.as_ref()?.as_object()?.get(field)
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
fn syncular_command_history_error(code: &str, message: &str) -> SyncularError {
|
|
789
|
-
SyncularError::config(format!("{code}: {message}"))
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
impl<'a, C> SyncularAppMutations<'a, C>
|
|
793
|
-
where
|
|
794
|
-
C: SyncularMutationExecutor + ?Sized,
|
|
795
|
-
{
|
|
796
|
-
pub fn tasks(self) -> TaskMutations<'a, C> {
|
|
797
|
-
TaskMutations {
|
|
798
|
-
client: self.client,
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
impl<'a, C> SyncularAppLeasedMutations<'a, C>
|
|
804
|
-
where
|
|
805
|
-
C: SyncularLeasedMutationExecutor + ?Sized,
|
|
806
|
-
{
|
|
807
|
-
pub fn tasks(self) -> TaskLeasedMutations<'a, C> {
|
|
808
|
-
TaskLeasedMutations {
|
|
809
|
-
client: self.client,
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
impl<'a> SyncularAppMutationTx<'a> {
|
|
815
|
-
pub fn tasks(&mut self) -> TaskMutationTx<'_> {
|
|
816
|
-
TaskMutationTx { batch: self.batch }
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
pub struct TaskMutations<'a, C: SyncularMutationExecutor + ?Sized> {
|
|
821
|
-
client: &'a mut C,
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
impl<C> TaskMutations<'_, C>
|
|
825
|
-
where
|
|
826
|
-
C: SyncularMutationExecutor + ?Sized,
|
|
827
|
-
{
|
|
828
|
-
pub fn insert(self, row: NewTask) -> Result<InsertReceipt> {
|
|
829
|
-
let id = row.id.clone();
|
|
830
|
-
let commit = self.client.apply_mutation(row)?;
|
|
831
|
-
Ok(InsertReceipt { id, commit })
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
pub fn insert_many(self, rows: impl IntoIterator<Item = NewTask>) -> Result<InsertManyReceipt> {
|
|
835
|
-
let mut batch = SyncularMutationBatch::new();
|
|
836
|
-
let mut ids = Vec::new();
|
|
837
|
-
for row in rows {
|
|
838
|
-
ids.push(row.id.clone());
|
|
839
|
-
batch.push(row);
|
|
840
|
-
}
|
|
841
|
-
let commit = self.client.apply_mutation_batch(batch)?;
|
|
842
|
-
Ok(InsertManyReceipt { ids, commit })
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
pub fn update(self, patch: TaskPatch) -> Result<MutationReceipt> {
|
|
846
|
-
self.client.apply_mutation(patch)
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
pub fn delete(self, row_id: &str) -> Result<MutationReceipt> {
|
|
850
|
-
self.client.apply_mutation(DeleteTask::new(row_id))
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
pub struct TaskLeasedMutations<'a, C: SyncularLeasedMutationExecutor + ?Sized> {
|
|
855
|
-
client: &'a mut C,
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
impl<C> TaskLeasedMutations<'_, C>
|
|
859
|
-
where
|
|
860
|
-
C: SyncularLeasedMutationExecutor + ?Sized,
|
|
861
|
-
{
|
|
862
|
-
pub fn insert(self, row: NewTask) -> Result<InsertReceipt> {
|
|
863
|
-
let id = row.id.clone();
|
|
864
|
-
let commit = self.client.apply_leased_mutation(row)?;
|
|
865
|
-
Ok(InsertReceipt { id, commit })
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
pub fn insert_many(self, rows: impl IntoIterator<Item = NewTask>) -> Result<InsertManyReceipt> {
|
|
869
|
-
let mut batch = SyncularMutationBatch::new();
|
|
870
|
-
let mut ids = Vec::new();
|
|
871
|
-
for row in rows {
|
|
872
|
-
ids.push(row.id.clone());
|
|
873
|
-
batch.push(row);
|
|
874
|
-
}
|
|
875
|
-
let commit = self.client.apply_leased_mutation_batch(batch)?;
|
|
876
|
-
Ok(InsertManyReceipt { ids, commit })
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
pub fn update(self, patch: TaskPatch) -> Result<MutationReceipt> {
|
|
880
|
-
self.client.apply_leased_mutation(patch)
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
pub fn delete(self, row_id: &str) -> Result<MutationReceipt> {
|
|
884
|
-
self.client.apply_leased_mutation(DeleteTask::new(row_id))
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
pub struct TaskMutationTx<'a> {
|
|
889
|
-
batch: &'a mut SyncularMutationBatch,
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
impl TaskMutationTx<'_> {
|
|
893
|
-
pub fn insert(self, row: NewTask) -> Result<String> {
|
|
894
|
-
let id = row.id.clone();
|
|
895
|
-
self.batch.push(row);
|
|
896
|
-
Ok(id)
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
pub fn insert_many(self, rows: impl IntoIterator<Item = NewTask>) -> Result<Vec<String>> {
|
|
900
|
-
let mut ids = Vec::new();
|
|
901
|
-
for row in rows {
|
|
902
|
-
ids.push(row.id.clone());
|
|
903
|
-
self.batch.push(row);
|
|
904
|
-
}
|
|
905
|
-
Ok(ids)
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
pub fn update(self, patch: TaskPatch) -> Result<()> {
|
|
909
|
-
self.batch.push(patch);
|
|
910
|
-
Ok(())
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
pub fn delete(self, row_id: &str) -> Result<()> {
|
|
914
|
-
self.batch.push(DeleteTask::new(row_id));
|
|
915
|
-
Ok(())
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
pub mod prelude {
|
|
920
|
-
pub use super::{
|
|
921
|
-
DeleteTask, InsertManyReceipt, InsertReceipt, NewTask, SyncularAppCommandHistory,
|
|
922
|
-
SyncularAppLeasedMutations, SyncularAppMutationTx, SyncularAppMutations,
|
|
923
|
-
SyncularGeneratedMutationsExt, TaskLeasedMutations, TaskMutationTx, TaskMutations,
|
|
924
|
-
TaskPatch,
|
|
925
|
-
};
|
|
926
|
-
}
|