dirsql 0.1.3__tar.gz → 0.1.4__tar.gz
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.
- {dirsql-0.1.3 → dirsql-0.1.4}/PKG-INFO +1 -1
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/src/lib.rs +14 -6
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/tests/integration/test_async_dirsql.py +4 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/src/differ.rs +4 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/src/lib.rs +13 -11
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/tests/sdk.rs +11 -1
- {dirsql-0.1.3 → dirsql-0.1.4}/pyproject.toml +1 -1
- {dirsql-0.1.3 → dirsql-0.1.4}/Cargo.lock +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/Cargo.toml +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/README.md +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/Cargo.toml +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/README.md +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/tests/conftest.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/tests/integration/test_binding.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/tests/integration/test_dirsql.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/tests/integration/test_docs_examples.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/tests/integration/test_docs_gaps.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/python/tests/integration/test_from_config.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/Cargo.toml +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/README.md +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/benches/differ_bench.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/benches/scanner_bench.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/src/config.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/src/db.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/src/matcher.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/src/parser.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/src/scanner.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/src/watcher.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/tests/async_sdk.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/tests/docs_examples.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/tests/docs_gaps.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/packages/rust/tests/from_config.rs +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/python/dirsql/__init__.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/python/dirsql/_async.py +0 -0
- {dirsql-0.1.3 → dirsql-0.1.4}/python/dirsql/test_async.py +0 -0
|
@@ -49,10 +49,14 @@ mod python {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/// A row event produced by the watch loop.
|
|
52
|
+
///
|
|
53
|
+
/// `table` is `Optional[str]` because error events may occur before a
|
|
54
|
+
/// file has been attributed to any table (e.g. a watch-channel failure).
|
|
55
|
+
/// For insert / update / delete events it is always set.
|
|
52
56
|
#[pyclass(name = "RowEvent", frozen)]
|
|
53
57
|
struct PyRowEvent {
|
|
54
58
|
#[pyo3(get)]
|
|
55
|
-
table: String
|
|
59
|
+
table: Option<String>,
|
|
56
60
|
#[pyo3(get)]
|
|
57
61
|
action: String,
|
|
58
62
|
#[pyo3(get)]
|
|
@@ -199,7 +203,7 @@ mod python {
|
|
|
199
203
|
row,
|
|
200
204
|
file_path,
|
|
201
205
|
} => PyRowEvent {
|
|
202
|
-
table: table.clone(),
|
|
206
|
+
table: Some(table.clone()),
|
|
203
207
|
action: "insert".to_string(),
|
|
204
208
|
row: Some(value_row_to_py_dict(py, row)?),
|
|
205
209
|
old_row: None,
|
|
@@ -212,7 +216,7 @@ mod python {
|
|
|
212
216
|
new_row,
|
|
213
217
|
file_path,
|
|
214
218
|
} => PyRowEvent {
|
|
215
|
-
table: table.clone(),
|
|
219
|
+
table: Some(table.clone()),
|
|
216
220
|
action: "update".to_string(),
|
|
217
221
|
row: Some(value_row_to_py_dict(py, new_row)?),
|
|
218
222
|
old_row: Some(value_row_to_py_dict(py, old_row)?),
|
|
@@ -224,15 +228,19 @@ mod python {
|
|
|
224
228
|
row,
|
|
225
229
|
file_path,
|
|
226
230
|
} => PyRowEvent {
|
|
227
|
-
table: table.clone(),
|
|
231
|
+
table: Some(table.clone()),
|
|
228
232
|
action: "delete".to_string(),
|
|
229
233
|
row: Some(value_row_to_py_dict(py, row)?),
|
|
230
234
|
old_row: None,
|
|
231
235
|
error: None,
|
|
232
236
|
file_path: Some(file_path.clone()),
|
|
233
237
|
},
|
|
234
|
-
RowEvent::Error {
|
|
235
|
-
table
|
|
238
|
+
RowEvent::Error {
|
|
239
|
+
table,
|
|
240
|
+
file_path,
|
|
241
|
+
error,
|
|
242
|
+
} => PyRowEvent {
|
|
243
|
+
table: table.clone(),
|
|
236
244
|
action: "error".to_string(),
|
|
237
245
|
row: None,
|
|
238
246
|
old_row: None,
|
|
@@ -341,6 +341,10 @@ def describe_DirSQL_async():
|
|
|
341
341
|
assert len(events) >= 1
|
|
342
342
|
assert events[0].action == "error"
|
|
343
343
|
assert events[0].error is not None
|
|
344
|
+
# The failing file matched the `items` table's glob; the error
|
|
345
|
+
# event must carry that attribution so multi-table consumers can
|
|
346
|
+
# route the error to the right handler.
|
|
347
|
+
assert events[0].table == "items"
|
|
344
348
|
|
|
345
349
|
@pytest.mark.asyncio
|
|
346
350
|
async def it_updates_db_on_file_changes(tmp_dir):
|
|
@@ -26,6 +26,10 @@ pub enum RowEvent {
|
|
|
26
26
|
file_path: String,
|
|
27
27
|
},
|
|
28
28
|
Error {
|
|
29
|
+
/// The table whose glob matched the failing file, when known.
|
|
30
|
+
/// `None` for errors that aren't tied to a specific table (e.g.
|
|
31
|
+
/// a watch-channel failure before file attribution).
|
|
32
|
+
table: Option<String>,
|
|
29
33
|
file_path: PathBuf,
|
|
30
34
|
error: String,
|
|
31
35
|
},
|
|
@@ -341,18 +341,18 @@ impl DirSQL {
|
|
|
341
341
|
fn handle_delete(&self, table: &str, rel_path: &str) -> Vec<RowEvent> {
|
|
342
342
|
let old_rows = match self.inner.file_rows.lock() {
|
|
343
343
|
Ok(mut file_rows) => file_rows.remove(rel_path).map(|(_, r)| r),
|
|
344
|
-
Err(e) => return vec![error_event(rel_path, e.to_string())],
|
|
344
|
+
Err(e) => return vec![error_event(Some(table), rel_path, e.to_string())],
|
|
345
345
|
};
|
|
346
346
|
|
|
347
347
|
let row_events = differ::diff(table, old_rows.as_deref(), None, rel_path);
|
|
348
348
|
|
|
349
349
|
let delete_result = match self.inner.db.lock() {
|
|
350
350
|
Ok(db) => db.delete_rows_by_file(table, rel_path),
|
|
351
|
-
Err(e) => return vec![error_event(rel_path, e.to_string())],
|
|
351
|
+
Err(e) => return vec![error_event(Some(table), rel_path, e.to_string())],
|
|
352
352
|
};
|
|
353
353
|
|
|
354
354
|
if let Err(e) = delete_result {
|
|
355
|
-
return vec![error_event(rel_path, e.to_string())];
|
|
355
|
+
return vec![error_event(Some(table), rel_path, e.to_string())];
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
row_events
|
|
@@ -362,7 +362,7 @@ impl DirSQL {
|
|
|
362
362
|
let content = match std::fs::read_to_string(abs_path) {
|
|
363
363
|
Ok(c) => c,
|
|
364
364
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Vec::new(),
|
|
365
|
-
Err(e) => return vec![error_event(rel_path, e.to_string())],
|
|
365
|
+
Err(e) => return vec![error_event(Some(table), rel_path, e.to_string())],
|
|
366
366
|
};
|
|
367
367
|
|
|
368
368
|
let extract = match self.inner.extract_map.get(table) {
|
|
@@ -372,7 +372,7 @@ impl DirSQL {
|
|
|
372
372
|
|
|
373
373
|
let raw_rows = match extract(rel_path, &content) {
|
|
374
374
|
Ok(r) => r,
|
|
375
|
-
Err(e) => return vec![error_event(rel_path, e.to_string())],
|
|
375
|
+
Err(e) => return vec![error_event(Some(table), rel_path, e.to_string())],
|
|
376
376
|
};
|
|
377
377
|
|
|
378
378
|
let strict = *self.inner.strict_map.get(table).unwrap_or(&false);
|
|
@@ -380,13 +380,13 @@ impl DirSQL {
|
|
|
380
380
|
let new_rows = {
|
|
381
381
|
let db = match self.inner.db.lock() {
|
|
382
382
|
Ok(g) => g,
|
|
383
|
-
Err(e) => return vec![error_event(rel_path, e.to_string())],
|
|
383
|
+
Err(e) => return vec![error_event(Some(table), rel_path, e.to_string())],
|
|
384
384
|
};
|
|
385
385
|
let mut normalized = Vec::with_capacity(raw_rows.len());
|
|
386
386
|
for raw in &raw_rows {
|
|
387
387
|
match db.normalize_row(table, raw, strict) {
|
|
388
388
|
Ok(row) => normalized.push(row),
|
|
389
|
-
Err(e) => return vec![error_event(rel_path, e.to_string())],
|
|
389
|
+
Err(e) => return vec![error_event(Some(table), rel_path, e.to_string())],
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
normalized
|
|
@@ -394,7 +394,7 @@ impl DirSQL {
|
|
|
394
394
|
|
|
395
395
|
let old_rows = match self.inner.file_rows.lock() {
|
|
396
396
|
Ok(guard) => guard.get(rel_path).map(|(_, r)| r.clone()),
|
|
397
|
-
Err(e) => return vec![error_event(rel_path, e.to_string())],
|
|
397
|
+
Err(e) => return vec![error_event(Some(table), rel_path, e.to_string())],
|
|
398
398
|
};
|
|
399
399
|
|
|
400
400
|
let row_events = differ::diff(table, old_rows.as_deref(), Some(&new_rows), rel_path);
|
|
@@ -406,11 +406,11 @@ impl DirSQL {
|
|
|
406
406
|
}
|
|
407
407
|
Ok(())
|
|
408
408
|
}),
|
|
409
|
-
Err(e) => return vec![error_event(rel_path, e.to_string())],
|
|
409
|
+
Err(e) => return vec![error_event(Some(table), rel_path, e.to_string())],
|
|
410
410
|
};
|
|
411
411
|
|
|
412
412
|
if let Err(e) = db_result {
|
|
413
|
-
return vec![error_event(rel_path, e.to_string())];
|
|
413
|
+
return vec![error_event(Some(table), rel_path, e.to_string())];
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
if let Ok(mut guard) = self.inner.file_rows.lock() {
|
|
@@ -491,8 +491,9 @@ impl DirSQL {
|
|
|
491
491
|
}
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
-
fn error_event(rel_path: &str, error: String) -> RowEvent {
|
|
494
|
+
fn error_event(table: Option<&str>, rel_path: &str, error: String) -> RowEvent {
|
|
495
495
|
RowEvent::Error {
|
|
496
|
+
table: table.map(str::to_string),
|
|
496
497
|
file_path: PathBuf::from(rel_path),
|
|
497
498
|
error,
|
|
498
499
|
}
|
|
@@ -510,6 +511,7 @@ fn run_channel_loop(db: DirSQL, tx: UnboundedSender<RowEvent>) {
|
|
|
510
511
|
}
|
|
511
512
|
Err(e) => {
|
|
512
513
|
let _ = tx.unbounded_send(RowEvent::Error {
|
|
514
|
+
table: None,
|
|
513
515
|
file_path: db.inner.root.clone(),
|
|
514
516
|
error: e.to_string(),
|
|
515
517
|
});
|
|
@@ -382,8 +382,18 @@ fn it_streams_watch_error_events() {
|
|
|
382
382
|
|
|
383
383
|
let event = block_on(stream.next()).expect("watch event");
|
|
384
384
|
match event {
|
|
385
|
-
dirsql::RowEvent::Error {
|
|
385
|
+
dirsql::RowEvent::Error {
|
|
386
|
+
table,
|
|
387
|
+
error,
|
|
388
|
+
file_path,
|
|
389
|
+
} => {
|
|
386
390
|
assert!(error.contains("intentional parse failure"));
|
|
391
|
+
assert_eq!(
|
|
392
|
+
table.as_deref(),
|
|
393
|
+
Some("items"),
|
|
394
|
+
"error event should attribute the failure to the matching table"
|
|
395
|
+
);
|
|
396
|
+
assert!(file_path.to_string_lossy().contains("bad.txt"));
|
|
387
397
|
}
|
|
388
398
|
other => panic!("expected error event, got: {other:?}"),
|
|
389
399
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|