dirsql 0.3.90__tar.gz → 0.3.92__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.
Files changed (138) hide show
  1. {dirsql-0.3.90 → dirsql-0.3.92}/Cargo.lock +1 -1
  2. {dirsql-0.3.90 → dirsql-0.3.92}/PKG-INFO +1 -1
  3. {dirsql-0.3.90/packages/rust → dirsql-0.3.92}/docs/reference/sdk.md +6 -3
  4. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/Cargo.toml +1 -1
  5. {dirsql-0.3.90 → dirsql-0.3.92/packages/python}/docs/reference/sdk.md +6 -3
  6. dirsql-0.3.92/packages/python/e2e-attestations/worktree-agent-a1a637f59ef70f352.json +7 -0
  7. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/src/lib.rs +1 -4
  8. {dirsql-0.3.90/packages/python → dirsql-0.3.92/packages/rust}/docs/reference/sdk.md +6 -3
  9. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/bin/dirsql.rs +1 -4
  10. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/lib.rs +122 -28
  11. dirsql-0.3.90/packages/python/e2e-attestation.json +0 -6
  12. {dirsql-0.3.90 → dirsql-0.3.92}/Cargo.toml +0 -0
  13. {dirsql-0.3.90 → dirsql-0.3.92}/README.md +0 -0
  14. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/__init__.py +0 -0
  15. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/_async.py +0 -0
  16. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/_dirsql.pyi +0 -0
  17. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/cli/__init__.py +0 -0
  18. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/cli/binary_path.py +0 -0
  19. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/cli/is_windows.py +0 -0
  20. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/cli/main.py +0 -0
  21. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/cli/resolve_config_extensions.py +0 -0
  22. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/py.typed +0 -0
  23. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/resolve_config_extensions.py +0 -0
  24. {dirsql-0.3.90 → dirsql-0.3.92}/dirsql/resolve_extension.py +0 -0
  25. {dirsql-0.3.90 → dirsql-0.3.92}/docs/.claude/CLAUDE.md +0 -0
  26. {dirsql-0.3.90 → dirsql-0.3.92}/docs/.vitepress/config.ts +0 -0
  27. {dirsql-0.3.90 → dirsql-0.3.92}/docs/.vitepress/theme/index.ts +0 -0
  28. {dirsql-0.3.90 → dirsql-0.3.92}/docs/.vitepress/theme/lang.ts +0 -0
  29. {dirsql-0.3.90 → dirsql-0.3.92}/docs/AGENTS.md +0 -0
  30. {dirsql-0.3.90 → dirsql-0.3.92}/docs/explanation.md +0 -0
  31. {dirsql-0.3.90 → dirsql-0.3.92}/docs/getting-started.md +0 -0
  32. {dirsql-0.3.90 → dirsql-0.3.92}/docs/howto/columns-from-paths.md +0 -0
  33. {dirsql-0.3.90 → dirsql-0.3.92}/docs/howto/define-tables.md +0 -0
  34. {dirsql-0.3.90 → dirsql-0.3.92}/docs/howto/embed.md +0 -0
  35. {dirsql-0.3.90 → dirsql-0.3.92}/docs/howto/extract-from-contents.md +0 -0
  36. {dirsql-0.3.90 → dirsql-0.3.92}/docs/howto/load-extension.md +0 -0
  37. {dirsql-0.3.90 → dirsql-0.3.92}/docs/howto/persist.md +0 -0
  38. {dirsql-0.3.90 → dirsql-0.3.92}/docs/howto/react-to-changes.md +0 -0
  39. {dirsql-0.3.90 → dirsql-0.3.92}/docs/howto/search-by-meaning.md +0 -0
  40. {dirsql-0.3.90 → dirsql-0.3.92}/docs/howto/skip-files.md +0 -0
  41. {dirsql-0.3.90 → dirsql-0.3.92}/docs/index.md +0 -0
  42. {dirsql-0.3.90 → dirsql-0.3.92}/docs/migrations.md +0 -0
  43. {dirsql-0.3.90 → dirsql-0.3.92}/docs/package.json +0 -0
  44. {dirsql-0.3.90 → dirsql-0.3.92}/docs/playwright.config.ts +0 -0
  45. {dirsql-0.3.90 → dirsql-0.3.92}/docs/pnpm-lock.yaml +0 -0
  46. {dirsql-0.3.90 → dirsql-0.3.92}/docs/pnpm-workspace.yaml +0 -0
  47. {dirsql-0.3.90 → dirsql-0.3.92}/docs/reference/cli.md +0 -0
  48. {dirsql-0.3.90 → dirsql-0.3.92}/docs/reference/columns.md +0 -0
  49. {dirsql-0.3.90 → dirsql-0.3.92}/docs/reference/config.md +0 -0
  50. {dirsql-0.3.90 → dirsql-0.3.92}/docs/reference/hooks.md +0 -0
  51. {dirsql-0.3.90 → dirsql-0.3.92}/docs/reference/http-api.md +0 -0
  52. {dirsql-0.3.90 → dirsql-0.3.92}/docs/tests/integration/home.spec.ts +0 -0
  53. {dirsql-0.3.90 → dirsql-0.3.92}/docs/tests/integration/language-flag.spec.ts +0 -0
  54. {dirsql-0.3.90 → dirsql-0.3.92}/docs/tests/integration/sidebar.spec.ts +0 -0
  55. {dirsql-0.3.90 → dirsql-0.3.92}/docs/tests/unit/config.test.ts +0 -0
  56. {dirsql-0.3.90 → dirsql-0.3.92}/docs/tests/unit/lang.test.ts +0 -0
  57. {dirsql-0.3.90 → dirsql-0.3.92}/docs/vitest.config.ts +0 -0
  58. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/README.md +0 -0
  59. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/conftest.py +0 -0
  60. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/.claude/CLAUDE.md +0 -0
  61. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/.vitepress/config.ts +0 -0
  62. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  63. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  64. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/AGENTS.md +0 -0
  65. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/explanation.md +0 -0
  66. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/getting-started.md +0 -0
  67. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/howto/columns-from-paths.md +0 -0
  68. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/howto/define-tables.md +0 -0
  69. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/howto/embed.md +0 -0
  70. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/howto/extract-from-contents.md +0 -0
  71. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/howto/load-extension.md +0 -0
  72. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/howto/persist.md +0 -0
  73. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/howto/react-to-changes.md +0 -0
  74. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/howto/search-by-meaning.md +0 -0
  75. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/howto/skip-files.md +0 -0
  76. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/index.md +0 -0
  77. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/migrations.md +0 -0
  78. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/package.json +0 -0
  79. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/playwright.config.ts +0 -0
  80. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/pnpm-lock.yaml +0 -0
  81. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/pnpm-workspace.yaml +0 -0
  82. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/reference/cli.md +0 -0
  83. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/reference/columns.md +0 -0
  84. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/reference/config.md +0 -0
  85. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/reference/hooks.md +0 -0
  86. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/reference/http-api.md +0 -0
  87. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  88. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  89. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
  90. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/tests/unit/config.test.ts +0 -0
  91. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  92. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/docs/vitest.config.ts +0 -0
  93. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/tests/__init__.py +0 -0
  94. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/tests/conftest.py +0 -0
  95. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/tests/e2e/__init__.py +0 -0
  96. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/tests/integration/__init__.py +0 -0
  97. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/tests/integration/binding/__init__.py +0 -0
  98. {dirsql-0.3.90 → dirsql-0.3.92}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  99. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/Cargo.toml +0 -0
  100. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/README.md +0 -0
  101. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/benches/db_bench.rs +0 -0
  102. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/benches/differ_bench.rs +0 -0
  103. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/benches/matcher_bench.rs +0 -0
  104. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/benches/scanner_bench.rs +0 -0
  105. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/explanation.md +0 -0
  106. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/getting-started.md +0 -0
  107. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  108. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/howto/define-tables.md +0 -0
  109. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/howto/embed.md +0 -0
  110. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  111. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/howto/load-extension.md +0 -0
  112. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/howto/persist.md +0 -0
  113. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/howto/react-to-changes.md +0 -0
  114. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  115. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/howto/skip-files.md +0 -0
  116. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/index.md +0 -0
  117. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/migrations.md +0 -0
  118. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/reference/cli.md +0 -0
  119. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/reference/columns.md +0 -0
  120. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/reference/config.md +0 -0
  121. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/reference/hooks.md +0 -0
  122. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/docs/reference/http-api.md +0 -0
  123. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/cli/execute.rs +0 -0
  124. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/cli/init.rs +0 -0
  125. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/cli/mod.rs +0 -0
  126. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/cli/router.rs +0 -0
  127. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/cli/serialize.rs +0 -0
  128. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/cli/server.rs +0 -0
  129. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/command.rs +0 -0
  130. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/config.rs +0 -0
  131. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/db.rs +0 -0
  132. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/default_config.toml +0 -0
  133. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/differ.rs +0 -0
  134. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/matcher.rs +0 -0
  135. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/persist.rs +0 -0
  136. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/scanner.rs +0 -0
  137. {dirsql-0.3.90 → dirsql-0.3.92}/packages/rust/src/watcher.rs +0 -0
  138. {dirsql-0.3.90 → dirsql-0.3.92}/pyproject.toml +0 -0
@@ -500,7 +500,7 @@ dependencies = [
500
500
 
501
501
  [[package]]
502
502
  name = "dirsql-py-ext"
503
- version = "0.3.90"
503
+ version = "0.3.92"
504
504
  dependencies = [
505
505
  "dirsql",
506
506
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.90
3
+ Version: 0.3.92
4
4
  Summary: Ephemeral SQL index over a local directory
5
5
  Keywords: sql,filesystem,directory,sqlite,index
6
6
  Author: Kevin Scott
@@ -81,8 +81,8 @@ DirSQL::builder()
81
81
  .tables(tables) // optional; append one with .table(t)
82
82
  .ignore(patterns) // optional
83
83
  .config(config_toml_path) // optional
84
- .persist(true) // optional; default false
85
- .persist_path(path) // optional
84
+ .persist(cache_path) // optional; Some(path), or None for the
85
+ // default <root>/.dirsql/cache.db
86
86
  .extensions(extensions) // optional; append one with .extension(e)
87
87
  .poll_interval(duration) // optional; watch-loop cadence, default 200ms
88
88
  .build() // -> Result<DirSQL> (synchronous scan)
@@ -114,7 +114,10 @@ directory" error.
114
114
  changed are re-parsed. (The CLI exposes the same switch as the
115
115
  [`--persist [PATH]`](./cli.md#server-mode) flag; it is not a config key.)
116
116
  - `persist_path` / `persistPath` — Override the cache location. Ignored when
117
- persistence is off. Constructor values are used as given.
117
+ persistence is off. Constructor values are used as given. In Rust these two
118
+ parameters collapse into a single builder method: `.persist(None)` enables
119
+ persistence at the default location, `.persist(Some(path))` enables it at
120
+ `path`.
118
121
  - `extensions` — SQLite extensions to load at startup, before any table
119
122
  DDL (enable → load → disable, so SQL `load_extension()` is never
120
123
  exposed). Each entry pairs a shared-library `path` with an optional
@@ -4,7 +4,7 @@ name = "dirsql-py-ext"
4
4
  # pypi/maturin handler can rewrite it via `write-version` before
5
5
  # `maturin build`. `pyproject.toml` declares `dynamic = ["version"]`
6
6
  # and maturin reads this field. Mirrors `packages/rust/Cargo.toml`.
7
- version = "0.3.90"
7
+ version = "0.3.92"
8
8
  # abi3 note: the `abi3-py311` pyo3 feature (below) builds ONE stable-ABI
9
9
  # `cp311-abi3` wheel per platform that loads on every CPython >= 3.11
10
10
  # (matching `pyproject.toml`'s `requires-python = ">=3.11"`), instead of
@@ -81,8 +81,8 @@ DirSQL::builder()
81
81
  .tables(tables) // optional; append one with .table(t)
82
82
  .ignore(patterns) // optional
83
83
  .config(config_toml_path) // optional
84
- .persist(true) // optional; default false
85
- .persist_path(path) // optional
84
+ .persist(cache_path) // optional; Some(path), or None for the
85
+ // default <root>/.dirsql/cache.db
86
86
  .extensions(extensions) // optional; append one with .extension(e)
87
87
  .poll_interval(duration) // optional; watch-loop cadence, default 200ms
88
88
  .build() // -> Result<DirSQL> (synchronous scan)
@@ -114,7 +114,10 @@ directory" error.
114
114
  changed are re-parsed. (The CLI exposes the same switch as the
115
115
  [`--persist [PATH]`](./cli.md#server-mode) flag; it is not a config key.)
116
116
  - `persist_path` / `persistPath` — Override the cache location. Ignored when
117
- persistence is off. Constructor values are used as given.
117
+ persistence is off. Constructor values are used as given. In Rust these two
118
+ parameters collapse into a single builder method: `.persist(None)` enables
119
+ persistence at the default location, `.persist(Some(path))` enables it at
120
+ `path`.
118
121
  - `extensions` — SQLite extensions to load at startup, before any table
119
122
  DDL (enable → load → disable, so SQL `load_extension()` is never
120
123
  exposed). Each entry pairs a shared-library `path` with an optional
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "uv run python -m pytest tests/e2e/ -x -q",
3
+ "ran_at": 1783641235,
4
+ "exit_code": 0,
5
+ "commit": "e8cda44abdfccb421ad06ec83c77bd644fb2bb83",
6
+ "branch": "worktree-agent-a1a637f59ef70f352"
7
+ }
@@ -154,10 +154,7 @@ mod python {
154
154
  builder = builder.config(c);
155
155
  }
156
156
  if persist {
157
- builder = builder.persist(true);
158
- }
159
- if let Some(p) = persist_path {
160
- builder = builder.persist_path(p);
157
+ builder = builder.persist(persist_path);
161
158
  }
162
159
  if !rust_extensions.is_empty() {
163
160
  builder = builder.extensions(rust_extensions);
@@ -81,8 +81,8 @@ DirSQL::builder()
81
81
  .tables(tables) // optional; append one with .table(t)
82
82
  .ignore(patterns) // optional
83
83
  .config(config_toml_path) // optional
84
- .persist(true) // optional; default false
85
- .persist_path(path) // optional
84
+ .persist(cache_path) // optional; Some(path), or None for the
85
+ // default <root>/.dirsql/cache.db
86
86
  .extensions(extensions) // optional; append one with .extension(e)
87
87
  .poll_interval(duration) // optional; watch-loop cadence, default 200ms
88
88
  .build() // -> Result<DirSQL> (synchronous scan)
@@ -114,7 +114,10 @@ directory" error.
114
114
  changed are re-parsed. (The CLI exposes the same switch as the
115
115
  [`--persist [PATH]`](./cli.md#server-mode) flag; it is not a config key.)
116
116
  - `persist_path` / `persistPath` — Override the cache location. Ignored when
117
- persistence is off. Constructor values are used as given.
117
+ persistence is off. Constructor values are used as given. In Rust these two
118
+ parameters collapse into a single builder method: `.persist(None)` enables
119
+ persistence at the default location, `.persist(Some(path))` enables it at
120
+ `path`.
118
121
  - `extensions` — SQLite extensions to load at startup, before any table
119
122
  DDL (enable → load → disable, so SQL `load_extension()` is never
120
123
  exposed). Each entry pairs a shared-library `path` with an optional
@@ -76,10 +76,7 @@ impl Cli {
76
76
  /// → persist at `<path>`.
77
77
  fn apply_persist(&self, mut builder: dirsql::DirSQLBuilder) -> dirsql::DirSQLBuilder {
78
78
  if let Some(path) = &self.persist {
79
- builder = builder.persist(true);
80
- if let Some(path) = path {
81
- builder = builder.persist_path(path);
82
- }
79
+ builder = builder.persist(path.as_ref());
83
80
  }
84
81
  builder
85
82
  }
@@ -932,20 +932,15 @@ impl DirSQLBuilder {
932
932
  self
933
933
  }
934
934
 
935
- /// Enable persistent on-disk storage. When `true`, the SQLite database is
936
- /// written to `<root>/.dirsql/cache.db` (override via
937
- /// [`persist_path`](Self::persist_path)) so subsequent startups only
938
- /// re-parse files that have actually changed. See
939
- /// `docs/howto/persist.md` for the reconcile contract.
940
- pub fn persist(mut self, persist: bool) -> Self {
941
- self.persist = persist;
942
- self
943
- }
944
-
945
- /// Override the location of the persistent cache file. Ignored when
946
- /// [`persist`](Self::persist) is `false`.
947
- pub fn persist_path(mut self, path: impl AsRef<Path>) -> Self {
948
- self.persist_path = Some(path.as_ref().to_path_buf());
935
+ /// Enable persistent on-disk storage. `None` writes the SQLite database to
936
+ /// the default `<root>/.dirsql/cache.db`; `Some(path)` writes it to `path`.
937
+ /// Either way, subsequent startups only re-parse files that have actually
938
+ /// changed. See `docs/howto/persist.md` for the reconcile contract.
939
+ pub fn persist(mut self, path: Option<impl AsRef<Path>>) -> Self {
940
+ self.persist = true;
941
+ if let Some(path) = path {
942
+ self.persist_path = Some(path.as_ref().to_path_buf());
943
+ }
949
944
  self
950
945
  }
951
946
 
@@ -972,8 +967,13 @@ impl DirSQLBuilder {
972
967
  poll_interval,
973
968
  } = self;
974
969
 
975
- let mut config_root: Option<PathBuf> = None;
976
-
970
+ // The config layer is an ordered list of entries, each carrying its
971
+ // loaded `config`, its `config_dir` (where `on-file` hooks run and the
972
+ // base for extension/persist path resolution), its resolved index
973
+ // `root` (the base for `{path}`), and its `hook_timeout`. Every caller
974
+ // supplies at most one config, so the list holds 0 or 1 entries and the
975
+ // in-order merge below is byte-for-byte identical to a single pass.
976
+ let mut config_entries: Vec<ResolvedConfigEntry> = Vec::new();
977
977
  if let Some(ref cfg_path) = config_path {
978
978
  let cfg = config::load_config(cfg_path).map_err(DirSqlError::config)?;
979
979
 
@@ -990,12 +990,30 @@ impl DirSQLBuilder {
990
990
  } else {
991
991
  cfg_parent.clone()
992
992
  };
993
+ let hook_timeout = cfg.hook_timeout.unwrap_or(command::DEFAULT_COMMAND_TIMEOUT);
994
+ config_entries.push(ResolvedConfigEntry {
995
+ config: cfg,
996
+ config_dir: cfg_parent,
997
+ root: resolved_root,
998
+ hook_timeout,
999
+ });
1000
+ }
1001
+
1002
+ let mut config_root: Option<PathBuf> = None;
1003
+ for entry in config_entries {
1004
+ let ResolvedConfigEntry {
1005
+ config: cfg,
1006
+ config_dir: cfg_parent,
1007
+ root: resolved_root,
1008
+ hook_timeout,
1009
+ } = entry;
993
1010
  config_root = Some(resolved_root.clone());
994
1011
 
995
1012
  // `on-file` commands run in the config file's directory; `{path}`
996
1013
  // is the matched file's absolute path and `{root}` the resolved
997
1014
  // index root.
998
- let cfg_tables = build_tables_from_config(&cfg, &cfg_parent, &resolved_root)?;
1015
+ let cfg_tables =
1016
+ build_tables_from_config(&cfg, &cfg_parent, &resolved_root, hook_timeout)?;
999
1017
  tables.extend(cfg_tables);
1000
1018
  ignore.extend(cfg.ignore);
1001
1019
 
@@ -1075,6 +1093,18 @@ impl DirSQLBuilder {
1075
1093
  /// Default poll interval for the channel-based watch loop.
1076
1094
  const DEFAULT_POLL_INTERVAL: Duration = Duration::from_millis(200);
1077
1095
 
1096
+ /// One resolved config file in the ordered list [`DirSQLBuilder::resolve`]
1097
+ /// merges over. Carries the loaded `config`, its `config_dir` (the config
1098
+ /// file's parent -- where `on-file` hooks run and the base for extension /
1099
+ /// persist path resolution), the resolved index `root` (the base for the
1100
+ /// `{path}` placeholder), and the `hook_timeout` bounding each `on-file` run.
1101
+ struct ResolvedConfigEntry {
1102
+ config: config::Config,
1103
+ config_dir: PathBuf,
1104
+ root: PathBuf,
1105
+ hook_timeout: Duration,
1106
+ }
1107
+
1078
1108
  /// Fully-resolved builder inputs: the result of merging programmatic
1079
1109
  /// settings with values loaded from a `.dirsql.toml` config file.
1080
1110
  #[doc(hidden)]
@@ -1366,16 +1396,17 @@ fn relative_path(root: &Path, path: &Path) -> String {
1366
1396
  /// array of row objects on stdout, which becomes the file's rows (filesystem
1367
1397
  /// facts are still merged on top, user values winning). `config_dir` is the
1368
1398
  /// command's working directory (the config file's parent) and `root` is the
1369
- /// resolved index root exposed as the `{root}` placeholder. Each run is
1370
- /// bounded by the global `[dirsql].hook-timeout` key when present, falling
1371
- /// back to [`command::DEFAULT_COMMAND_TIMEOUT`].
1399
+ /// resolved index root exposed as the `{root}` placeholder. `timeout` bounds
1400
+ /// each `on-file` run; the caller resolves it from the global
1401
+ /// `[dirsql].hook-timeout` key, falling back to
1402
+ /// [`command::DEFAULT_COMMAND_TIMEOUT`].
1372
1403
  fn build_tables_from_config(
1373
1404
  cfg: &config::Config,
1374
1405
  config_dir: &Path,
1375
1406
  root: &Path,
1407
+ timeout: Duration,
1376
1408
  ) -> Result<Vec<Table>> {
1377
1409
  let mut tables = Vec::with_capacity(cfg.tables.len());
1378
- let timeout = cfg.hook_timeout.unwrap_or(command::DEFAULT_COMMAND_TIMEOUT);
1379
1410
 
1380
1411
  for table_cfg in &cfg.tables {
1381
1412
  let mut table = match &table_cfg.on_file {
@@ -2651,8 +2682,7 @@ mod internal_tests {
2651
2682
  .ignore(["skip/**"])
2652
2683
  .extensions(Vec::<Extension>::new())
2653
2684
  .suppress_config_extensions(true)
2654
- .persist(true)
2655
- .persist_path(&cache)
2685
+ .persist(Some(&cache))
2656
2686
  .poll_interval(Duration::from_millis(50))
2657
2687
  .build()
2658
2688
  .unwrap();
@@ -2661,6 +2691,38 @@ mod internal_tests {
2661
2691
  assert_eq!(db.inner.poll_interval, Duration::from_millis(50));
2662
2692
  }
2663
2693
 
2694
+ #[test]
2695
+ fn persist_none_enables_default_path() {
2696
+ let resolved = DirSQL::builder()
2697
+ .root("/tmp/x")
2698
+ .persist(None::<&Path>)
2699
+ .resolve()
2700
+ .unwrap();
2701
+ assert!(resolved.persist);
2702
+ assert!(resolved.persist_path.is_none());
2703
+ }
2704
+
2705
+ #[test]
2706
+ fn persist_some_enables_explicit_path() {
2707
+ let resolved = DirSQL::builder()
2708
+ .root("/tmp/x")
2709
+ .persist(Some("/tmp/x/custom.db"))
2710
+ .resolve()
2711
+ .unwrap();
2712
+ assert!(resolved.persist);
2713
+ assert_eq!(
2714
+ resolved.persist_path,
2715
+ Some(PathBuf::from("/tmp/x/custom.db"))
2716
+ );
2717
+ }
2718
+
2719
+ #[test]
2720
+ fn persist_unset_leaves_persistence_off() {
2721
+ let resolved = DirSQL::builder().root("/tmp/x").resolve().unwrap();
2722
+ assert!(!resolved.persist);
2723
+ assert!(resolved.persist_path.is_none());
2724
+ }
2725
+
2664
2726
  /// A second persist build over the same root+cache finds a compatible
2665
2727
  /// meta block and reuses the cache instead of a cold rebuild.
2666
2728
  #[test]
@@ -2674,8 +2736,7 @@ mod internal_tests {
2674
2736
  "*.txt",
2675
2737
  |_| vec![],
2676
2738
  )])
2677
- .persist(true)
2678
- .persist_path(&cache)
2739
+ .persist(Some(&cache))
2679
2740
  .build()
2680
2741
  .unwrap();
2681
2742
  drop(first);
@@ -2686,8 +2747,7 @@ mod internal_tests {
2686
2747
  "*.txt",
2687
2748
  |_| vec![],
2688
2749
  )])
2689
- .persist(true)
2690
- .persist_path(&cache)
2750
+ .persist(Some(&cache))
2691
2751
  .build()
2692
2752
  .unwrap();
2693
2753
  assert!(second.query("SELECT * FROM t").is_ok());
@@ -2847,12 +2907,46 @@ mod internal_tests {
2847
2907
  ))
2848
2908
  .unwrap();
2849
2909
  let dir = TempDir::new().unwrap();
2850
- let tables = build_tables_from_config(&cfg, dir.path(), dir.path()).unwrap();
2910
+ let tables = build_tables_from_config(
2911
+ &cfg,
2912
+ dir.path(),
2913
+ dir.path(),
2914
+ command::DEFAULT_COMMAND_TIMEOUT,
2915
+ )
2916
+ .unwrap();
2851
2917
  assert_eq!(tables.len(), 2);
2852
2918
  assert_eq!((tables[0].extract)("/whatever").unwrap().len(), 1);
2853
2919
  assert!(tables[1].strict, "on-file table preserves strict flag");
2854
2920
  }
2855
2921
 
2922
+ #[test]
2923
+ fn build_tables_from_config_uses_the_caller_supplied_timeout() {
2924
+ // The timeout is now threaded in as an explicit argument rather than
2925
+ // re-derived from `cfg.hook_timeout` inside the function; a table built
2926
+ // from a config declaring its own `hook-timeout` must honor the value
2927
+ // the caller passes, independent of the config key.
2928
+ let cfg = config::load_config_str(concat!(
2929
+ "[dirsql]\n",
2930
+ "hook-timeout = 999\n\n",
2931
+ "[[table]]\n",
2932
+ "ddl = \"CREATE TABLE b (y TEXT)\"\n",
2933
+ "glob = \"*.b\"\n",
2934
+ "on-file = \"printf '[{\\\"y\\\":1}]'\"\n",
2935
+ ))
2936
+ .unwrap();
2937
+ let dir = TempDir::new().unwrap();
2938
+ let abs = dir.path().join("f.b");
2939
+ let tables =
2940
+ build_tables_from_config(&cfg, dir.path(), dir.path(), Duration::from_secs(5)).unwrap();
2941
+ assert_eq!(tables.len(), 1);
2942
+ // The passed timeout is generous, so the on-file command runs and its
2943
+ // row is produced -- proving the argument path is live.
2944
+ assert_eq!(
2945
+ (tables[0].extract)(&abs.to_string_lossy()).unwrap().len(),
2946
+ 1
2947
+ );
2948
+ }
2949
+
2856
2950
  #[test]
2857
2951
  fn run_on_file_parses_command_json_output() {
2858
2952
  let dir = TempDir::new().unwrap();
@@ -1,6 +0,0 @@
1
- {
2
- "command": "uv run python -m pytest tests/e2e/ -x -q",
3
- "ran_at": 1783637710,
4
- "exit_code": 0,
5
- "commit": "c7c626ca03beb1b23e26ab96febf3c9df5d4a67f"
6
- }
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