dirsql 0.3.22__tar.gz → 0.3.24__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.3.22 → dirsql-0.3.24}/Cargo.lock +1 -1
- {dirsql-0.3.22 → dirsql-0.3.24}/PKG-INFO +1 -1
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/_async_test.py +19 -19
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/binary_path_test.py +1 -1
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/main_test.py +13 -16
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/api/index.md +6 -4
- {dirsql-0.3.22/packages/python → dirsql-0.3.24}/docs/cli/config.md +73 -32
- {dirsql-0.3.22/packages/rust → dirsql-0.3.24}/docs/cli/http-api.md +16 -4
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/cli/index.md +2 -2
- {dirsql-0.3.22/packages/rust → dirsql-0.3.24}/docs/cli/server.md +2 -2
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/getting-started.md +1 -1
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/guide/async.md +55 -12
- {dirsql-0.3.22/packages/python → dirsql-0.3.24}/docs/guide/crdt.md +1 -1
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/guide/querying.md +3 -2
- {dirsql-0.3.22/packages/rust → dirsql-0.3.24}/docs/guide/tables.md +1 -1
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/guide/watching.md +6 -2
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/Cargo.toml +1 -1
- {dirsql-0.3.22/packages/rust → dirsql-0.3.24/packages/python}/docs/api/index.md +6 -4
- {dirsql-0.3.22 → dirsql-0.3.24/packages/python}/docs/cli/config.md +73 -32
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/cli/http-api.md +16 -4
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/cli/index.md +2 -2
- {dirsql-0.3.22 → dirsql-0.3.24/packages/python}/docs/cli/server.md +2 -2
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/getting-started.md +1 -1
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/guide/async.md +55 -12
- {dirsql-0.3.22 → dirsql-0.3.24/packages/python}/docs/guide/crdt.md +1 -1
- {dirsql-0.3.22/packages/rust → dirsql-0.3.24/packages/python}/docs/guide/querying.md +3 -2
- {dirsql-0.3.22 → dirsql-0.3.24/packages/python}/docs/guide/tables.md +1 -1
- {dirsql-0.3.22/packages/rust → dirsql-0.3.24/packages/python}/docs/guide/watching.md +6 -2
- {dirsql-0.3.22/packages/python → dirsql-0.3.24/packages/rust}/docs/api/index.md +6 -4
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/docs/cli/config.md +73 -32
- {dirsql-0.3.22 → dirsql-0.3.24/packages/rust}/docs/cli/http-api.md +16 -4
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/docs/cli/index.md +2 -2
- {dirsql-0.3.22/packages/python → dirsql-0.3.24/packages/rust}/docs/cli/server.md +2 -2
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/docs/getting-started.md +1 -1
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/docs/guide/async.md +55 -12
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/docs/guide/crdt.md +1 -1
- {dirsql-0.3.22/packages/python → dirsql-0.3.24/packages/rust}/docs/guide/querying.md +3 -2
- {dirsql-0.3.22/packages/python → dirsql-0.3.24/packages/rust}/docs/guide/tables.md +1 -1
- {dirsql-0.3.22/packages/python → dirsql-0.3.24/packages/rust}/docs/guide/watching.md +6 -2
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/watcher.rs +5 -124
- dirsql-0.3.24/packages/rust/tests/watcher.rs +139 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/Cargo.toml +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/README.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/__init__.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/_async.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/_dirsql.pyi +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/__init__.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/binary_path.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/interpret/__init__.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/interpret/dispatch_extract.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/interpret/dispatch_extract_test.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/interpret/load_app.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/interpret/load_app_test.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/interpret/run.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/interpret/run_test.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/interpret/write_message.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/interpret/write_message_test.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/is_windows.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/is_windows_test.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/cli/main.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/py.typed +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/resolve_config.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/dirsql/resolve_config_test.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/.vitepress/config.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/AGENTS.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/cli/init.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/guide/persistence.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/index.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/migrations.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/package.json +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/playwright.config.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/tests/integration/home.spec.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/tests/integration/language-flag.spec.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/tests/unit/config.test.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/tests/unit/lang.test.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/docs/vitest.config.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/README.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/conftest.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/.vitepress/config.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/AGENTS.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/cli/init.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/guide/persistence.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/index.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/migrations.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/package.json +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/playwright.config.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/tests/integration/home.spec.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/tests/unit/config.test.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/tests/unit/lang.test.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/docs/vitest.config.ts +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/src/lib.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/conftest.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/e2e/__init__.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/__fixtures__/data/a/meta.json +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/__fixtures__/data/b/meta.json +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/__fixtures__/dirsql.config.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/__fixtures__/interpret/data/a/meta.json +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/__fixtures__/interpret/data/b/meta.json +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/__fixtures__/interpret/dirsql.config.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/__fixtures__/interpret/dirsql.config_no_app.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/__fixtures__/interpret/dirsql.config_raises.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/interpret_subprocess.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_async_dirsql.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_binding.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_dirsql.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_docs_examples.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_docs_gaps.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_from_config.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_interpret.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_native_config.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_persist.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/python/tests/integration/test_serialization.py +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/Cargo.toml +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/README.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/benches/differ_bench.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/benches/scanner_bench.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/docs/cli/init.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/docs/guide/persistence.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/docs/index.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/docs/migrations.md +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/bin/dirsql.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/cli/init.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/cli/mod.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/cli/native_config.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/cli/router.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/cli/serialize.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/cli/server.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/config.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/db.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/differ.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/lib.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/matcher.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/persist.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/src/scanner.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/async_sdk.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/cli_e2e.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/cli_integration.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/code_review_findings.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/docs_examples.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/docs_gaps.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/extensions.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/from_config.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/init_e2e.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/init_integration.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/persist.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/readonly_query.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/sdk.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/packages/rust/tests/serialization.rs +0 -0
- {dirsql-0.3.22 → dirsql-0.3.24}/pyproject.toml +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Unit tests for the DirSQL async wrapper."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from unittest.mock import patch
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import dirsql._async as async_mod
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class _FakeRustDirSQL:
|
|
@@ -50,32 +50,32 @@ class _FakeWatcherDb:
|
|
|
50
50
|
def describe_DirSQL_async():
|
|
51
51
|
def describe_ready_and_query():
|
|
52
52
|
@pytest.mark.asyncio
|
|
53
|
-
async def it_uses_the_background_db(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
async def it_uses_the_background_db():
|
|
54
|
+
with patch.object(async_mod, "_RustDirSQL", _FakeRustDirSQL):
|
|
55
|
+
db = async_mod.DirSQL(
|
|
56
|
+
"/tmp/root", tables=["table-a"], ignore=["**/*.tmp"]
|
|
57
|
+
)
|
|
58
|
+
await db.ready()
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
results = await db.query("SELECT 1")
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
assert db._db.root == "/tmp/root"
|
|
63
|
+
assert db._db.tables == ["table-a"]
|
|
64
|
+
assert db._db.ignore == ["**/*.tmp"]
|
|
65
|
+
assert db._db.query_calls == ["SELECT 1"]
|
|
66
|
+
assert results == [{"sql": "SELECT 1"}]
|
|
66
67
|
|
|
67
68
|
@pytest.mark.asyncio
|
|
68
|
-
async def it_propagates_initialization_errors(
|
|
69
|
+
async def it_propagates_initialization_errors():
|
|
69
70
|
class _BoomDirSQL:
|
|
70
71
|
def __init__(self, *args, **kwargs):
|
|
71
72
|
raise RuntimeError("boom")
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
with patch.object(async_mod, "_RustDirSQL", _BoomDirSQL):
|
|
75
|
+
db = async_mod.DirSQL("/tmp/root", tables=["table-a"])
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
with pytest.raises(RuntimeError, match="boom"):
|
|
78
|
-
await db.ready()
|
|
77
|
+
with pytest.raises(RuntimeError, match="boom"):
|
|
78
|
+
await db.ready()
|
|
79
79
|
|
|
80
80
|
def describe_watch_stream():
|
|
81
81
|
@pytest.mark.asyncio
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import io
|
|
4
4
|
import os
|
|
5
|
-
import subprocess
|
|
6
5
|
import sys
|
|
7
6
|
from unittest.mock import patch
|
|
8
7
|
|
|
@@ -35,7 +34,9 @@ def describe_main():
|
|
|
35
34
|
with (
|
|
36
35
|
patch.object(main_module, "binary_path", return_value="C:/dirsql.exe"),
|
|
37
36
|
patch.object(main_module, "is_windows", return_value=True),
|
|
38
|
-
patch
|
|
37
|
+
patch(
|
|
38
|
+
"dirsql.cli.main.subprocess.run", return_value=_Completed(7)
|
|
39
|
+
) as run,
|
|
39
40
|
):
|
|
40
41
|
assert main(["--version"]) == 7
|
|
41
42
|
run.assert_called_once_with(["C:/dirsql.exe", "--version"])
|
|
@@ -44,7 +45,7 @@ def describe_main():
|
|
|
44
45
|
with (
|
|
45
46
|
patch.object(main_module, "binary_path", return_value="C:/dirsql.exe"),
|
|
46
47
|
patch.object(main_module, "is_windows", return_value=True),
|
|
47
|
-
patch.
|
|
48
|
+
patch("dirsql.cli.main.subprocess.run", return_value=_Completed(0)),
|
|
48
49
|
patch.object(os, "execv") as execv,
|
|
49
50
|
):
|
|
50
51
|
main([])
|
|
@@ -81,7 +82,9 @@ def describe_main():
|
|
|
81
82
|
patch.object(sys, "argv", ["dirsql", "--help"]),
|
|
82
83
|
patch.object(main_module, "binary_path", return_value="C:/dirsql.exe"),
|
|
83
84
|
patch.object(main_module, "is_windows", return_value=True),
|
|
84
|
-
patch
|
|
85
|
+
patch(
|
|
86
|
+
"dirsql.cli.main.subprocess.run", return_value=_Completed(0)
|
|
87
|
+
) as run,
|
|
85
88
|
):
|
|
86
89
|
main(argv=None)
|
|
87
90
|
run.assert_called_once_with(["C:/dirsql.exe", "--help"])
|
|
@@ -93,12 +96,10 @@ def describe_main():
|
|
|
93
96
|
|
|
94
97
|
def it_dispatches_to_interpret_run_and_returns_its_exit_code():
|
|
95
98
|
# main.py does `from .interpret.run import run` lazily, so
|
|
96
|
-
# patching the `run` function on
|
|
97
|
-
# -- each call resolves the name freshly
|
|
98
|
-
from .interpret import run as run_submod
|
|
99
|
-
|
|
99
|
+
# patching the `run` function on its module by its dotted path
|
|
100
|
+
# is sufficient -- each call resolves the name freshly.
|
|
100
101
|
with (
|
|
101
|
-
patch
|
|
102
|
+
patch("dirsql.cli.interpret.run.run", return_value=0) as interpret_run,
|
|
102
103
|
patch.object(main_module, "binary_path") as binary_path,
|
|
103
104
|
):
|
|
104
105
|
assert main(["interpret", "config.py"]) == 0
|
|
@@ -106,15 +107,11 @@ def describe_main():
|
|
|
106
107
|
binary_path.assert_not_called()
|
|
107
108
|
|
|
108
109
|
def it_propagates_a_nonzero_interpret_exit():
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
with patch.object(run_submod, "run", return_value=2):
|
|
110
|
+
with patch("dirsql.cli.interpret.run.run", return_value=2):
|
|
112
111
|
assert main(["interpret", "bad.py"]) == 2
|
|
113
112
|
|
|
114
113
|
def it_returns_130_on_keyboard_interrupt():
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
with patch.object(run_submod, "run", side_effect=KeyboardInterrupt()):
|
|
114
|
+
with patch("dirsql.cli.interpret.run.run", side_effect=KeyboardInterrupt()):
|
|
118
115
|
assert main(["interpret", "config.py"]) == 130
|
|
119
116
|
|
|
120
117
|
def it_does_not_intercept_when_interpret_is_not_argv_0():
|
|
@@ -123,7 +120,7 @@ def describe_main():
|
|
|
123
120
|
with (
|
|
124
121
|
patch.object(main_module, "binary_path", return_value="/bin/dirsql"),
|
|
125
122
|
patch.object(main_module, "is_windows", return_value=True),
|
|
126
|
-
patch.
|
|
123
|
+
patch("dirsql.cli.main.subprocess.run", return_value=_Completed(0)),
|
|
127
124
|
):
|
|
128
125
|
main(["--verbose", "interpret", "config.py"])
|
|
129
126
|
# binary_path was reached, meaning we did NOT take the
|
|
@@ -188,11 +188,12 @@ import { Table } from 'dirsql';
|
|
|
188
188
|
::: code-group
|
|
189
189
|
|
|
190
190
|
```python [Python]
|
|
191
|
-
Table(*, ddl: str, glob: str, extract: Callable[[str], list[dict]])
|
|
191
|
+
Table(*, ddl: str, glob: str, extract: Callable[[str], list[dict]], strict: bool = False)
|
|
192
192
|
```
|
|
193
193
|
|
|
194
194
|
```rust [Rust]
|
|
195
|
-
|
|
195
|
+
// Row = HashMap<String, Value>
|
|
196
|
+
Table::new(ddl: &str, glob: &str, extract: fn(&str) -> Vec<HashMap<String, Value>>)
|
|
196
197
|
```
|
|
197
198
|
|
|
198
199
|
```typescript [TypeScript]
|
|
@@ -207,7 +208,8 @@ Defines a mapping from files to SQLite table rows.
|
|
|
207
208
|
|
|
208
209
|
- `ddl` -- A `CREATE TABLE` statement. The table name is parsed from this DDL.
|
|
209
210
|
- `glob` -- A glob pattern matched against file paths relative to the root directory.
|
|
210
|
-
- `extract` -- A callable `(path) -> list[dict]`. Receives the
|
|
211
|
+
- `extract` -- A callable `(path) -> list[dict]`. Receives the path of the matched file -- relative to the scan root, or absolute when `root` is absolute. `dirsql` does not read file contents; a callback that needs the file body reads `path` itself. Returns a list of dicts/maps mapping column names to values. Return an empty list to skip a file.
|
|
212
|
+
- `strict` -- Optional (default `False`). Controls row/schema validation. In the default relaxed mode, extra row keys are dropped and missing columns become `NULL`. When `True`, every row key must be a valid column identifier and any extra or missing key raises an error. Surfaced in [serialization](#serialization) above as part of each table's `{ ddl, glob, strict }`.
|
|
211
213
|
|
|
212
214
|
**Attributes:**
|
|
213
215
|
|
|
@@ -231,7 +233,7 @@ use dirsql::RowEvent;
|
|
|
231
233
|
```
|
|
232
234
|
|
|
233
235
|
```typescript [TypeScript]
|
|
234
|
-
import { RowEvent } from 'dirsql';
|
|
236
|
+
import type { RowEvent } from 'dirsql';
|
|
235
237
|
```
|
|
236
238
|
|
|
237
239
|
:::
|
|
@@ -6,12 +6,24 @@ canonical: https://thekevinscott.github.io/dirsql/cli/config
|
|
|
6
6
|
|
|
7
7
|
> Online: <https://thekevinscott.github.io/dirsql/cli/config>
|
|
8
8
|
|
|
9
|
-
`dirsql`
|
|
9
|
+
`dirsql` is configured with an optional config file; with none, the server
|
|
10
|
+
falls back to [zero-config defaults](./server.md#defaults). Choose a format by
|
|
11
|
+
what you need:
|
|
10
12
|
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
+
- **[TOML](#toml)** — declarative; defines filesystem-fact tables (the path,
|
|
14
|
+
glob captures, and stat metadata). Works with any installation.
|
|
15
|
+
- **[Python](#python)** and **[JavaScript](#javascript)** — native-language
|
|
16
|
+
configs that build tables from the *contents* of files (frontmatter, JSON
|
|
17
|
+
values, CSV cells) through a dynamic `extract` callback. CLI-only; only the
|
|
18
|
+
launcher matching the file's language can run it.
|
|
13
19
|
|
|
14
|
-
##
|
|
20
|
+
## TOML
|
|
21
|
+
|
|
22
|
+
Reach for a TOML config — the default `.dirsql.toml` — to declare tables from
|
|
23
|
+
filesystem facts: a glob selects files, and columns come from path captures and
|
|
24
|
+
stat metadata. No code required, and it works with every installation.
|
|
25
|
+
|
|
26
|
+
### Basic Example
|
|
15
27
|
|
|
16
28
|
```toml
|
|
17
29
|
[dirsql]
|
|
@@ -24,9 +36,11 @@ glob = "posts/*.md"
|
|
|
24
36
|
|
|
25
37
|
Each `posts/*.md` file produces one row in the `posts` table.
|
|
26
38
|
|
|
27
|
-
|
|
39
|
+
### Loading a Config File
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
The CLI loads `./.dirsql.toml` by default; pass `--config <path>` to point at
|
|
42
|
+
another file. To load the same `.toml` from the SDK, pass its path to the
|
|
43
|
+
`DirSQL` constructor:
|
|
30
44
|
|
|
31
45
|
::: code-group
|
|
32
46
|
|
|
@@ -60,7 +74,7 @@ directory. Override it by passing `root` explicitly (the explicit value
|
|
|
60
74
|
wins and a warning is emitted) or by declaring `[dirsql].root` in the
|
|
61
75
|
config file itself.
|
|
62
76
|
|
|
63
|
-
|
|
77
|
+
### Root Directory
|
|
64
78
|
|
|
65
79
|
By default, the config file's parent directory is the scan root. To index
|
|
66
80
|
a different location, declare `[dirsql].root` (relative paths are resolved
|
|
@@ -72,7 +86,7 @@ root = "../data"
|
|
|
72
86
|
ignore = ["node_modules/**"]
|
|
73
87
|
```
|
|
74
88
|
|
|
75
|
-
|
|
89
|
+
### Stat Virtuals
|
|
76
90
|
|
|
77
91
|
Every config-defined table can expose any of these reserved columns. Add
|
|
78
92
|
the ones you want to your DDL; the rest are silently dropped.
|
|
@@ -96,7 +110,7 @@ WHERE _mtime > strftime('%s', '2024-01-01')
|
|
|
96
110
|
ORDER BY _mtime DESC;
|
|
97
111
|
```
|
|
98
112
|
|
|
99
|
-
|
|
113
|
+
### Path Captures
|
|
100
114
|
|
|
101
115
|
Use `{name}` in glob patterns to extract path segments as columns. Add a
|
|
102
116
|
matching column name to the DDL and the capture is auto-populated:
|
|
@@ -111,7 +125,7 @@ A file at `_comments/abc123/2024-05-05.jsonl` produces a row with
|
|
|
111
125
|
`thread_id = "abc123"`, `_basename = "2024-05-05.jsonl"`, and `_mtime` set
|
|
112
126
|
to the file's modification time.
|
|
113
127
|
|
|
114
|
-
|
|
128
|
+
### Ignore Patterns
|
|
115
129
|
|
|
116
130
|
The `ignore` list skips files and directories entirely (not even scanned):
|
|
117
131
|
|
|
@@ -124,7 +138,7 @@ The top-level `.dirsql/` directory is always excluded, whether you list it
|
|
|
124
138
|
or not — it is a reserved namespace for `dirsql`'s own metadata (see
|
|
125
139
|
[Persistence](../guide/persistence.md)).
|
|
126
140
|
|
|
127
|
-
|
|
141
|
+
### Persistence
|
|
128
142
|
|
|
129
143
|
Set `persist = true` to keep the SQLite database on disk between runs
|
|
130
144
|
instead of rebuilding from scratch on every startup:
|
|
@@ -138,7 +152,7 @@ persist = true
|
|
|
138
152
|
See [Persistence](../guide/persistence.md) for the full reconcile algorithm,
|
|
139
153
|
storage layout, and limitations.
|
|
140
154
|
|
|
141
|
-
|
|
155
|
+
### Loading extensions
|
|
142
156
|
|
|
143
157
|
You can load SQLite extensions by specifying them in a config.
|
|
144
158
|
|
|
@@ -165,7 +179,7 @@ be declared as a `[[table]]` — `dirsql` tables are per-file row tables — so
|
|
|
165
179
|
`CREATE VIRTUAL TABLE` DDL is rejected; call the extension's functions in your
|
|
166
180
|
queries instead.
|
|
167
181
|
|
|
168
|
-
|
|
182
|
+
### Strict Mode
|
|
169
183
|
|
|
170
184
|
By default, auto-injected virtuals that aren't in the DDL are silently
|
|
171
185
|
dropped, and undeclared user-extract keys are dropped. Enable strict mode
|
|
@@ -180,10 +194,11 @@ strict = true
|
|
|
180
194
|
|
|
181
195
|
Strict mode does **not** apply to auto-injected stat virtuals — those are
|
|
182
196
|
always filtered to the DDL's declared columns regardless. Strict mode
|
|
183
|
-
applies only to keys produced by an extract callback (relevant for
|
|
184
|
-
|
|
197
|
+
applies only to keys produced by an extract callback (relevant for the
|
|
198
|
+
[Python](#python) / [JavaScript](#javascript) configs below and programmatic
|
|
199
|
+
[tables](../guide/tables.md)).
|
|
185
200
|
|
|
186
|
-
|
|
201
|
+
### Full Example
|
|
187
202
|
|
|
188
203
|
```toml
|
|
189
204
|
[dirsql]
|
|
@@ -202,20 +217,18 @@ ddl = "CREATE TABLE logs (_path TEXT, _size INTEGER, _mtime INTEGER)"
|
|
|
202
217
|
glob = "logs/*.csv"
|
|
203
218
|
```
|
|
204
219
|
|
|
205
|
-
##
|
|
220
|
+
## Python
|
|
206
221
|
|
|
207
|
-
|
|
222
|
+
Reach for a Python config when your columns come from the *contents* of a
|
|
223
|
+
file — parsed JSON, frontmatter, CSV cells — rather than from filesystem
|
|
224
|
+
facts alone. You write a dynamic `extract` callback in Python, and the file
|
|
225
|
+
otherwise looks exactly like the in-process SDK construction (same `DirSQL` /
|
|
226
|
+
`Table` API):
|
|
208
227
|
|
|
209
228
|
```bash
|
|
210
229
|
dirsql --config dirsql.config.py
|
|
211
|
-
dirsql --config dirsql.config.js
|
|
212
230
|
```
|
|
213
231
|
|
|
214
|
-
The file looks exactly like the in-process SDK construction — same
|
|
215
|
-
`DirSQL` / `Table` API:
|
|
216
|
-
|
|
217
|
-
::: code-group
|
|
218
|
-
|
|
219
232
|
```python [dirsql.config.py]
|
|
220
233
|
import json
|
|
221
234
|
from dirsql import DirSQL, Table
|
|
@@ -224,8 +237,9 @@ def extract_meta(path):
|
|
|
224
237
|
with open(path) as f:
|
|
225
238
|
return [json.load(f)]
|
|
226
239
|
|
|
227
|
-
# Python must export
|
|
240
|
+
# Python must export a module-level `app`.
|
|
228
241
|
app = DirSQL(
|
|
242
|
+
root="papers", # required — see "Set a root" below
|
|
229
243
|
tables=[
|
|
230
244
|
Table(
|
|
231
245
|
ddl="CREATE TABLE papers (title TEXT, _path TEXT)",
|
|
@@ -236,11 +250,26 @@ app = DirSQL(
|
|
|
236
250
|
)
|
|
237
251
|
```
|
|
238
252
|
|
|
253
|
+
`extract` receives the path of each matched file and returns a list of rows
|
|
254
|
+
(one dict per row).
|
|
255
|
+
|
|
256
|
+
## JavaScript
|
|
257
|
+
|
|
258
|
+
A JavaScript config gives you the same contents-driven `extract` in Node,
|
|
259
|
+
in either ES module or CommonJS form:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
dirsql --config dirsql.config.mjs
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
::: code-group
|
|
266
|
+
|
|
239
267
|
```javascript [dirsql.config.mjs]
|
|
240
268
|
import { readFileSync } from "node:fs";
|
|
241
269
|
import { DirSQL } from "dirsql";
|
|
242
270
|
|
|
243
271
|
export default new DirSQL({
|
|
272
|
+
root: "papers", // required — see "Set a root" below
|
|
244
273
|
tables: [
|
|
245
274
|
{
|
|
246
275
|
ddl: "CREATE TABLE papers (title TEXT, _path TEXT)",
|
|
@@ -256,6 +285,7 @@ const { readFileSync } = require("node:fs");
|
|
|
256
285
|
const { DirSQL } = require("dirsql");
|
|
257
286
|
|
|
258
287
|
module.exports = new DirSQL({
|
|
288
|
+
root: "papers", // required — see "Set a root" below
|
|
259
289
|
tables: [
|
|
260
290
|
{
|
|
261
291
|
ddl: "CREATE TABLE papers (title TEXT, _path TEXT)",
|
|
@@ -268,10 +298,21 @@ module.exports = new DirSQL({
|
|
|
268
298
|
|
|
269
299
|
:::
|
|
270
300
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
- **Python
|
|
276
|
-
|
|
277
|
-
|
|
301
|
+
## Notes for native-language configs
|
|
302
|
+
|
|
303
|
+
These apply to both the Python and JavaScript forms above.
|
|
304
|
+
|
|
305
|
+
- **Export the config.** Python exposes a module-level `app = DirSQL(...)`; an
|
|
306
|
+
ES module (`.mjs`, or `.js` in an ESM package) uses
|
|
307
|
+
`export default new DirSQL(...)`; CommonJS (`.cjs`, or `.js` in a CJS
|
|
308
|
+
package) uses `module.exports = new DirSQL(...)`. Only the extension
|
|
309
|
+
matters — the file can be named anything; `dirsql.config.{py,mjs,cjs}` is the
|
|
310
|
+
suggested convention, not a requirement.
|
|
311
|
+
- **Set a `root`.** Unlike TOML configs (which default the scan root to the
|
|
312
|
+
config file's directory), native-language configs require an explicit `root`.
|
|
313
|
+
Without one the Python launcher errors and the JavaScript launcher silently
|
|
314
|
+
indexes nothing.
|
|
315
|
+
- **Install the launcher on your `PATH`.** To run your `extract`, the server
|
|
316
|
+
spawns `dirsql interpret`, so the matching `dirsql` launcher must be installed
|
|
317
|
+
and on your `PATH` — a global `pip`/`uv` install for `.py`, or `npm` for
|
|
318
|
+
`.mjs` / `.cjs`. Only the launcher matching the file's language can run it.
|
|
@@ -33,12 +33,12 @@ On error, the server returns a non-2xx status with a JSON body:
|
|
|
33
33
|
{"error": "syntax error near \"SLECT\""}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Malformed SQL returns `400
|
|
36
|
+
Malformed SQL returns `400`. An unreadable or malformed config returns `503`; a *missing* config is not an error — the server serves the default `files` table.
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
39
|
curl -s http://localhost:7117/query \
|
|
40
40
|
-H 'content-type: application/json' \
|
|
41
|
-
-d '{"sql":"SELECT COUNT(*) AS n FROM
|
|
41
|
+
-d '{"sql":"SELECT COUNT(*) AS n FROM files"}' \
|
|
42
42
|
| jq
|
|
43
43
|
```
|
|
44
44
|
|
|
@@ -47,9 +47,14 @@ for SQL semantics, the read-only restriction, and the return format.
|
|
|
47
47
|
|
|
48
48
|
## `GET /events`
|
|
49
49
|
|
|
50
|
-
Opens a [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) stream of change events.
|
|
50
|
+
Opens a [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) stream of change events.
|
|
51
|
+
|
|
52
|
+
On stream open the server emits a single `ready` frame so clients have a reliable signal that the subscription is attached. Every subsequent frame is named `row`, and its `data:` payload is the same JSON schema the SDK emits from [`db.watch()`](../guide/watching.md#event-types):
|
|
51
53
|
|
|
52
54
|
```
|
|
55
|
+
event: ready
|
|
56
|
+
data: {}
|
|
57
|
+
|
|
53
58
|
event: row
|
|
54
59
|
data: {"action":"insert","table":"posts","file_path":"posts/hello.json","row":{"title":"Hello World","author":"alice"},"old_row":null}
|
|
55
60
|
|
|
@@ -60,7 +65,14 @@ event: row
|
|
|
60
65
|
data: {"action":"delete","table":"posts","file_path":"posts/second.json","row":{"title":"Second Post","author":"bob"},"old_row":null}
|
|
61
66
|
```
|
|
62
67
|
|
|
63
|
-
Errors during extraction appear as `
|
|
68
|
+
Errors during extraction appear as `error` action events on the same `row` stream. An error event carries an `error` message string and does **not** include `row` or `old_row`:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
event: row
|
|
72
|
+
data: {"action":"error","table":"posts","file_path":"posts/broken.json","error":"Extract error: ..."}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
(`table` is `null` when the failure isn't tied to a specific table.) Errors do **not** terminate the stream — a malformed file is a per-event problem, not a server-wide one.
|
|
64
76
|
|
|
65
77
|
```bash
|
|
66
78
|
curl -N http://localhost:7117/events
|
|
@@ -53,7 +53,7 @@ for the library-vs-CLI feature split.
|
|
|
53
53
|
|
|
54
54
|
## Quick start
|
|
55
55
|
|
|
56
|
-
From
|
|
56
|
+
From any directory containing your files (no config needed):
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
dirsql
|
|
@@ -66,7 +66,7 @@ Then query it over HTTP:
|
|
|
66
66
|
```bash
|
|
67
67
|
curl -s http://localhost:7117/query \
|
|
68
68
|
-H 'content-type: application/json' \
|
|
69
|
-
-d '{"sql":"SELECT COUNT(*) AS n FROM
|
|
69
|
+
-d '{"sql":"SELECT COUNT(*) AS n FROM files"}' \
|
|
70
70
|
| jq
|
|
71
71
|
```
|
|
72
72
|
|
|
@@ -28,7 +28,7 @@ $ Running at localhost:7117
|
|
|
28
28
|
|
|
29
29
|
The server reads tables from a [config file](./config.md). By default it
|
|
30
30
|
looks for `./.dirsql.toml`; pass `--config <path>` to point at a different
|
|
31
|
-
`.toml` file or a [native-language config](./config.md
|
|
31
|
+
`.toml` file or a [native-language config](./config.md)
|
|
32
32
|
(`.py` / `.js`).
|
|
33
33
|
|
|
34
34
|
## Defaults
|
|
@@ -53,7 +53,7 @@ A config file will override the default.
|
|
|
53
53
|
|
|
54
54
|
| Flag | Default | Description |
|
|
55
55
|
|---|---|---|
|
|
56
|
-
| `--config <path>` | `./.dirsql.toml` | Path to the config file (`.toml` or [native-language](./config.md
|
|
56
|
+
| `--config <path>` | `./.dirsql.toml` | Path to the config file (`.toml` or [native-language](./config.md)). The index is rooted at the directory containing this file. |
|
|
57
57
|
| `--host <addr>` | `localhost` | Bind address |
|
|
58
58
|
| `--port <n>` | `7117` | TCP port to bind |
|
|
59
59
|
|
|
@@ -171,7 +171,7 @@ const results = await db.query(`
|
|
|
171
171
|
|
|
172
172
|
1. `dirsql` walks the directory tree
|
|
173
173
|
2. Files matching each table's glob pattern are identified
|
|
174
|
-
3. The `extract` function receives each matched file's absolute
|
|
174
|
+
3. The `extract` function receives each matched file's path (relative to the scan root, or absolute when `root` is absolute) and returns rows
|
|
175
175
|
4. Rows are inserted into an in-memory SQLite database
|
|
176
176
|
5. SQL queries run against that database
|
|
177
177
|
|
|
@@ -28,6 +28,7 @@ async def main():
|
|
|
28
28
|
),
|
|
29
29
|
],
|
|
30
30
|
)
|
|
31
|
+
await db.ready()
|
|
31
32
|
|
|
32
33
|
# Query (runs in a thread, does not block the event loop)
|
|
33
34
|
results = await db.query("SELECT * FROM items WHERE value > 10")
|
|
@@ -37,7 +38,31 @@ asyncio.run(main())
|
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
```rust [Rust]
|
|
40
|
-
use dirsql::{DirSQL, Table};
|
|
41
|
+
use dirsql::{DirSQL, Table, Value};
|
|
42
|
+
use std::collections::HashMap;
|
|
43
|
+
|
|
44
|
+
// See `row_from_json` in getting-started.md for a reusable helper that
|
|
45
|
+
// turns a JSON object into a dirsql row (dirsql::Value is not Deserialize,
|
|
46
|
+
// so a row can't be produced by serde_json::from_str directly).
|
|
47
|
+
fn row_from_json(raw: &str) -> HashMap<String, Value> {
|
|
48
|
+
let v: serde_json::Value = serde_json::from_str(raw).unwrap();
|
|
49
|
+
let serde_json::Value::Object(obj) = v else { return HashMap::new() };
|
|
50
|
+
obj.into_iter()
|
|
51
|
+
.map(|(k, val)| {
|
|
52
|
+
let v = match val {
|
|
53
|
+
serde_json::Value::String(s) => Value::Text(s),
|
|
54
|
+
serde_json::Value::Number(n) => n
|
|
55
|
+
.as_i64()
|
|
56
|
+
.map(Value::Integer)
|
|
57
|
+
.unwrap_or_else(|| Value::Real(n.as_f64().unwrap_or(0.0))),
|
|
58
|
+
serde_json::Value::Bool(b) => Value::Integer(b as i64),
|
|
59
|
+
serde_json::Value::Null => Value::Null,
|
|
60
|
+
other => Value::Text(other.to_string()),
|
|
61
|
+
};
|
|
62
|
+
(k, v)
|
|
63
|
+
})
|
|
64
|
+
.collect()
|
|
65
|
+
}
|
|
41
66
|
|
|
42
67
|
#[tokio::main]
|
|
43
68
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
@@ -47,7 +72,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
47
72
|
Table::new(
|
|
48
73
|
"CREATE TABLE items (name TEXT, value INTEGER)",
|
|
49
74
|
"data/*.json",
|
|
50
|
-
|path| vec![
|
|
75
|
+
|path| vec![row_from_json(&std::fs::read_to_string(path).unwrap())],
|
|
51
76
|
),
|
|
52
77
|
],
|
|
53
78
|
)?;
|
|
@@ -83,7 +108,7 @@ console.log(results);
|
|
|
83
108
|
## Constructor
|
|
84
109
|
|
|
85
110
|
```python
|
|
86
|
-
DirSQL(root=None, *, tables=None, ignore=None, config=None)
|
|
111
|
+
DirSQL(root=None, *, tables=None, ignore=None, config=None, persist=False, persist_path=None)
|
|
87
112
|
```
|
|
88
113
|
|
|
89
114
|
The constructor immediately starts scanning in a background thread via `asyncio.ensure_future`. The constructor itself returns immediately without blocking.
|
|
@@ -132,15 +157,29 @@ async for event in db.watch():
|
|
|
132
157
|
```
|
|
133
158
|
|
|
134
159
|
```rust [Rust]
|
|
160
|
+
// `RowEvent` is an enum; match on the variant to destructure its fields.
|
|
161
|
+
// `StreamExt` (for `.next()`) comes from the `futures` crate, which is only a
|
|
162
|
+
// dirsql dependency under its `cli` feature -- add it to your own project:
|
|
163
|
+
//
|
|
164
|
+
// cargo add futures
|
|
165
|
+
use dirsql::RowEvent;
|
|
135
166
|
use futures::StreamExt;
|
|
136
167
|
|
|
137
|
-
let mut stream = db.watch()
|
|
168
|
+
let mut stream = db.watch()?; // watch() returns Result<WatchStream>
|
|
138
169
|
while let Some(event) = stream.next().await {
|
|
139
|
-
match event
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
170
|
+
match event {
|
|
171
|
+
RowEvent::Insert { table, row, file_path } => {
|
|
172
|
+
println!("New row in {table} ({file_path}): {row:?}")
|
|
173
|
+
}
|
|
174
|
+
RowEvent::Update { table, new_row, file_path, .. } => {
|
|
175
|
+
println!("Updated row in {table} ({file_path}): {new_row:?}")
|
|
176
|
+
}
|
|
177
|
+
RowEvent::Delete { table, row, file_path } => {
|
|
178
|
+
println!("Deleted row from {table} ({file_path}): {row:?}")
|
|
179
|
+
}
|
|
180
|
+
RowEvent::Error { file_path, error, .. } => {
|
|
181
|
+
eprintln!("Error on {file_path:?}: {error}")
|
|
182
|
+
}
|
|
144
183
|
}
|
|
145
184
|
}
|
|
146
185
|
```
|
|
@@ -188,16 +227,20 @@ async def main():
|
|
|
188
227
|
```
|
|
189
228
|
|
|
190
229
|
```rust [Rust]
|
|
191
|
-
|
|
192
|
-
|
|
230
|
+
// `.next()` needs `StreamExt` from the `futures` crate (`cargo add futures`).
|
|
231
|
+
use futures::StreamExt;
|
|
232
|
+
|
|
233
|
+
async fn watch_and_serve(db: &DirSQL) -> Result<(), Box<dyn std::error::Error>> {
|
|
234
|
+
let mut stream = db.watch()?; // watch() returns Result<WatchStream>
|
|
193
235
|
while let Some(event) = stream.next().await {
|
|
194
236
|
notify_clients(&event).await;
|
|
195
237
|
}
|
|
238
|
+
Ok(())
|
|
196
239
|
}
|
|
197
240
|
|
|
198
241
|
#[tokio::main]
|
|
199
242
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
200
|
-
let db = DirSQL::new("./data", vec![
|
|
243
|
+
let db = DirSQL::new("./data", vec![/* tables */])?;
|
|
201
244
|
|
|
202
245
|
tokio::join!(
|
|
203
246
|
watch_and_serve(&db),
|