dirsql 0.4.42__tar.gz → 0.4.44__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 (234) hide show
  1. {dirsql-0.4.42 → dirsql-0.4.44}/Cargo.lock +41 -2
  2. {dirsql-0.4.42 → dirsql-0.4.44}/PKG-INFO +1 -1
  3. {dirsql-0.4.42 → dirsql-0.4.44}/docs/reference/cli.md +5 -0
  4. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/Cargo.toml +1 -1
  5. {dirsql-0.4.42/packages/rust → dirsql-0.4.44/packages/python}/docs/reference/cli.md +5 -0
  6. dirsql-0.4.44/packages/python/e2e-attestations/claude-1081-indicatif-progress.json +7 -0
  7. dirsql-0.4.44/packages/python/e2e-attestations/claude-1119-ext-plan.json +7 -0
  8. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/Cargo.toml +3 -1
  9. {dirsql-0.4.42/packages/python → dirsql-0.4.44/packages/rust}/docs/reference/cli.md +5 -0
  10. dirsql-0.4.44/packages/rust/src/extension_resolution.rs +421 -0
  11. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/lib.rs +2 -0
  12. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/progress.rs +339 -126
  13. {dirsql-0.4.42 → dirsql-0.4.44}/Cargo.toml +0 -0
  14. {dirsql-0.4.42 → dirsql-0.4.44}/README.md +0 -0
  15. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/__init__.py +0 -0
  16. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/_async.py +0 -0
  17. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/_dirsql.pyi +0 -0
  18. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/cli/__init__.py +0 -0
  19. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/cli/discover_plugins/__init__.py +0 -0
  20. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
  21. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
  22. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
  23. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
  24. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
  25. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/cli/main.py +0 -0
  26. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/cli/resolve_config_extensions.py +0 -0
  27. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/has_bare_name.py +0 -0
  28. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/is_bare_name.py +0 -0
  29. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/load_extension_entries.py +0 -0
  30. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/platform_patterns.py +0 -0
  31. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/py.typed +0 -0
  32. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/resolve_config_extensions.py +0 -0
  33. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/resolve_configs_extension_specs.py +0 -0
  34. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/resolve_entries.py +0 -0
  35. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/resolve_extension.py +0 -0
  36. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/resolve_package.py +0 -0
  37. {dirsql-0.4.42 → dirsql-0.4.44}/dirsql/toml_module.py +0 -0
  38. {dirsql-0.4.42 → dirsql-0.4.44}/docs/.claude/CLAUDE.md +0 -0
  39. {dirsql-0.4.42 → dirsql-0.4.44}/docs/.vitepress/config.ts +0 -0
  40. {dirsql-0.4.42 → dirsql-0.4.44}/docs/.vitepress/theme/index.ts +0 -0
  41. {dirsql-0.4.42 → dirsql-0.4.44}/docs/.vitepress/theme/lang.ts +0 -0
  42. {dirsql-0.4.42 → dirsql-0.4.44}/docs/AGENTS.md +0 -0
  43. {dirsql-0.4.42 → dirsql-0.4.44}/docs/explanation.md +0 -0
  44. {dirsql-0.4.42 → dirsql-0.4.44}/docs/getting-started.md +0 -0
  45. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/columns-from-paths.md +0 -0
  46. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/define-tables.md +0 -0
  47. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/embed.md +0 -0
  48. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/extract-from-contents.md +0 -0
  49. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/load-extension.md +0 -0
  50. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/parse-files-into-columns.md +0 -0
  51. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/persist.md +0 -0
  52. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/query-json.md +0 -0
  53. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/query-without-config.md +0 -0
  54. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/react-to-changes.md +0 -0
  55. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/search-by-meaning.md +0 -0
  56. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/search-indexes.md +0 -0
  57. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/skip-files.md +0 -0
  58. {dirsql-0.4.42 → dirsql-0.4.44}/docs/howto/write-a-plugin.md +0 -0
  59. {dirsql-0.4.42 → dirsql-0.4.44}/docs/index.md +0 -0
  60. {dirsql-0.4.42 → dirsql-0.4.44}/docs/package.json +0 -0
  61. {dirsql-0.4.42 → dirsql-0.4.44}/docs/plugins.md +0 -0
  62. {dirsql-0.4.42 → dirsql-0.4.44}/docs/pnpm-lock.yaml +0 -0
  63. {dirsql-0.4.42 → dirsql-0.4.44}/docs/pnpm-workspace.yaml +0 -0
  64. {dirsql-0.4.42 → dirsql-0.4.44}/docs/reference/columns.md +0 -0
  65. {dirsql-0.4.42 → dirsql-0.4.44}/docs/reference/config.md +0 -0
  66. {dirsql-0.4.42 → dirsql-0.4.44}/docs/reference/hooks.md +0 -0
  67. {dirsql-0.4.42 → dirsql-0.4.44}/docs/reference/http-api.md +0 -0
  68. {dirsql-0.4.42 → dirsql-0.4.44}/docs/reference/path-tables.md +0 -0
  69. {dirsql-0.4.42 → dirsql-0.4.44}/docs/reference/sdk.md +0 -0
  70. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/CHANGELOG.md +0 -0
  71. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/MIGRATIONS.md +0 -0
  72. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/README.md +0 -0
  73. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  74. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
  75. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  76. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
  77. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  78. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
  79. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
  80. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
  81. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  82. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
  83. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
  84. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
  85. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
  86. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
  87. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
  88. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
  89. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
  90. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
  91. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
  92. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-09-04-config-paths-from-argv-module.md +0 -0
  93. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-09-05-config-extension-loader-split.md +0 -0
  94. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-09-05-resolve-config-extensions-split.md +0 -0
  95. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-09-05-resolve-extension-split.md +0 -0
  96. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md +0 -0
  97. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/changelog.d/README.md +0 -0
  98. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/conftest.py +0 -0
  99. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/.claude/CLAUDE.md +0 -0
  100. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/.vitepress/config.ts +0 -0
  101. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  102. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  103. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/AGENTS.md +0 -0
  104. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/explanation.md +0 -0
  105. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/getting-started.md +0 -0
  106. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/columns-from-paths.md +0 -0
  107. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/define-tables.md +0 -0
  108. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/embed.md +0 -0
  109. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/extract-from-contents.md +0 -0
  110. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/load-extension.md +0 -0
  111. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
  112. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/persist.md +0 -0
  113. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/query-json.md +0 -0
  114. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/query-without-config.md +0 -0
  115. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/react-to-changes.md +0 -0
  116. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/search-by-meaning.md +0 -0
  117. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/search-indexes.md +0 -0
  118. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/skip-files.md +0 -0
  119. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/howto/write-a-plugin.md +0 -0
  120. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/index.md +0 -0
  121. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/package.json +0 -0
  122. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/plugins.md +0 -0
  123. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/pnpm-lock.yaml +0 -0
  124. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/pnpm-workspace.yaml +0 -0
  125. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/reference/columns.md +0 -0
  126. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/reference/config.md +0 -0
  127. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/reference/hooks.md +0 -0
  128. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/reference/http-api.md +0 -0
  129. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/reference/path-tables.md +0 -0
  130. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/docs/reference/sdk.md +0 -0
  131. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
  132. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-1056-py-ofpf9.json +0 -0
  133. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-1066-p1-resolve-extension.json +0 -0
  134. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-1078-argv-config-paths-core.json +0 -0
  135. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-1079-p2-resolve-config-extensions.json +0 -0
  136. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-1080-share-vtab-scaffolding.json +0 -0
  137. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-1096-watch-moved-dir.json +0 -0
  138. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-1109-watch-moved-out.json +0 -0
  139. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
  140. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
  141. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
  142. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
  143. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
  144. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
  145. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
  146. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
  147. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
  148. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-tackle-1034-cachesplit.json +0 -0
  149. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-tackle-1042-py12.json +0 -0
  150. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
  151. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6-worker-calls.json +0 -0
  152. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +0 -0
  153. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +0 -0
  154. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  155. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  156. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  157. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
  158. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
  159. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
  160. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  161. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
  162. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
  163. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
  164. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/migrations.d/README.md +0 -0
  165. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/src/lib.rs +0 -0
  166. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/testing-conventions.toml +0 -0
  167. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/tests/__init__.py +0 -0
  168. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/tests/conftest.py +0 -0
  169. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/tests/e2e/__init__.py +0 -0
  170. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
  171. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
  172. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/tests/integration/__init__.py +0 -0
  173. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/tests/integration/binding/__init__.py +0 -0
  174. {dirsql-0.4.42 → dirsql-0.4.44}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  175. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/CHANGELOG.md +0 -0
  176. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/MIGRATIONS.md +0 -0
  177. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/README.md +0 -0
  178. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/benches/db_bench.rs +0 -0
  179. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/benches/differ_bench.rs +0 -0
  180. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/benches/matcher_bench.rs +0 -0
  181. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/benches/scanner_bench.rs +0 -0
  182. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/explanation.md +0 -0
  183. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/getting-started.md +0 -0
  184. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  185. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/define-tables.md +0 -0
  186. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/embed.md +0 -0
  187. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  188. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/load-extension.md +0 -0
  189. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
  190. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/persist.md +0 -0
  191. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/query-json.md +0 -0
  192. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/query-without-config.md +0 -0
  193. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/react-to-changes.md +0 -0
  194. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  195. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/search-indexes.md +0 -0
  196. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/skip-files.md +0 -0
  197. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/howto/write-a-plugin.md +0 -0
  198. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/index.md +0 -0
  199. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/plugins.md +0 -0
  200. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/reference/columns.md +0 -0
  201. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/reference/config.md +0 -0
  202. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/reference/hooks.md +0 -0
  203. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/reference/http-api.md +0 -0
  204. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/reference/path-tables.md +0 -0
  205. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/docs/reference/sdk.md +0 -0
  206. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/bin/dirsql.rs +0 -0
  207. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/cli/execute.rs +0 -0
  208. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/cli/init.rs +0 -0
  209. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/cli/mod.rs +0 -0
  210. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/cli/repl.rs +0 -0
  211. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/cli/router.rs +0 -0
  212. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/cli/run.rs +0 -0
  213. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/cli/serialize.rs +0 -0
  214. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/cli/server.rs +0 -0
  215. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/cli/table.rs +0 -0
  216. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/command.rs +0 -0
  217. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/config.rs +0 -0
  218. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/db.rs +0 -0
  219. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/default_config.toml +0 -0
  220. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/differ.rs +0 -0
  221. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/functions.rs +0 -0
  222. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/infer.rs +0 -0
  223. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/launcher.rs +0 -0
  224. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/matcher.rs +0 -0
  225. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/parsed_cache.rs +0 -0
  226. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/parsed_vtab.rs +0 -0
  227. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/path_table.rs +0 -0
  228. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/persist.rs +0 -0
  229. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/scanner.rs +0 -0
  230. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/sql_literal.rs +0 -0
  231. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/vtab.rs +0 -0
  232. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/vtab_scaffold.rs +0 -0
  233. {dirsql-0.4.42 → dirsql-0.4.44}/packages/rust/src/watcher.rs +0 -0
  234. {dirsql-0.4.42 → dirsql-0.4.44}/pyproject.toml +0 -0
@@ -350,6 +350,18 @@ version = "1.0.5"
350
350
  source = "registry+https://github.com/rust-lang/crates.io-index"
351
351
  checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
352
352
 
353
+ [[package]]
354
+ name = "console"
355
+ version = "0.16.6"
356
+ source = "registry+https://github.com/rust-lang/crates.io-index"
357
+ checksum = "e96a4956774c13c126a8b5af4daa79384f4d826534c95a02d76afb39e2ab64e3"
358
+ dependencies = [
359
+ "encode_unicode",
360
+ "libc",
361
+ "unicode-width",
362
+ "windows-sys 0.61.2",
363
+ ]
364
+
353
365
  [[package]]
354
366
  name = "constant_time_eq"
355
367
  version = "0.4.2"
@@ -540,12 +552,13 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
540
552
 
541
553
  [[package]]
542
554
  name = "dirsql"
543
- version = "0.4.42"
555
+ version = "0.4.44"
544
556
  dependencies = [
545
557
  "assert_cmd",
546
558
  "axum",
547
559
  "blake3",
548
560
  "clap",
561
+ "console",
549
562
  "criterion",
550
563
  "eventsource-client",
551
564
  "futures",
@@ -554,6 +567,7 @@ dependencies = [
554
567
  "futures-util",
555
568
  "globset",
556
569
  "ignore",
570
+ "indicatif",
557
571
  "libc",
558
572
  "notify",
559
573
  "predicates",
@@ -583,7 +597,7 @@ dependencies = [
583
597
 
584
598
  [[package]]
585
599
  name = "dirsql-py-ext"
586
- version = "0.4.42"
600
+ version = "0.4.44"
587
601
  dependencies = [
588
602
  "dirsql",
589
603
  "pyo3",
@@ -630,6 +644,12 @@ version = "1.15.0"
630
644
  source = "registry+https://github.com/rust-lang/crates.io-index"
631
645
  checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
632
646
 
647
+ [[package]]
648
+ name = "encode_unicode"
649
+ version = "1.0.0"
650
+ source = "registry+https://github.com/rust-lang/crates.io-index"
651
+ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
652
+
633
653
  [[package]]
634
654
  name = "equivalent"
635
655
  version = "1.0.2"
@@ -1294,6 +1314,19 @@ dependencies = [
1294
1314
  "serde_core",
1295
1315
  ]
1296
1316
 
1317
+ [[package]]
1318
+ name = "indicatif"
1319
+ version = "0.18.6"
1320
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1321
+ checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c"
1322
+ dependencies = [
1323
+ "console",
1324
+ "portable-atomic",
1325
+ "unicode-width",
1326
+ "unit-prefix",
1327
+ "web-time",
1328
+ ]
1329
+
1297
1330
  [[package]]
1298
1331
  name = "inotify"
1299
1332
  version = "0.10.2"
@@ -2902,6 +2935,12 @@ version = "0.2.6"
2902
2935
  source = "registry+https://github.com/rust-lang/crates.io-index"
2903
2936
  checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
2904
2937
 
2938
+ [[package]]
2939
+ name = "unit-prefix"
2940
+ version = "0.5.2"
2941
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2942
+ checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
2943
+
2905
2944
  [[package]]
2906
2945
  name = "untrusted"
2907
2946
  version = "0.9.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.4.42
3
+ Version: 0.4.44
4
4
  Requires-Dist: tomli>=2 ; python_full_version < '3.11'
5
5
  Requires-Dist: bin-shim>=0.1
6
6
  Summary: Ephemeral SQL index over a local directory
@@ -536,6 +536,11 @@ Override with `DIRSQL_PROGRESS`:
536
536
  Values are case-insensitive and surrounding whitespace is ignored; anything
537
537
  unrecognized reads as `auto`, so a typo cannot stop a scan from running.
538
538
 
539
+ Each redraw clears the line and pads the new one out to the full width (the
540
+ terminal's, or 80 columns when there is none), so a capture of an `always` run
541
+ carries trailing spaces. It carries no escape sequences, and the summary that
542
+ survives the phase is a whole, newline-terminated line.
543
+
539
544
  The setting is read by the **core**, not the CLI, so it governs an index built
540
545
  from any SDK as well — a Python or TypeScript program that builds a `DirSQL`
541
546
  with a terminal attached gets the same two phases on stderr, and the same
@@ -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.4.42"
7
+ version = "0.4.44"
8
8
  # abi3 note: the `abi3-py310` pyo3 feature (below) builds ONE stable-ABI
9
9
  # `cp310-abi3` wheel per platform that loads on every CPython >= 3.10
10
10
  # (matching `pyproject.toml`'s `requires-python = ">=3.10"`), instead of
@@ -536,6 +536,11 @@ Override with `DIRSQL_PROGRESS`:
536
536
  Values are case-insensitive and surrounding whitespace is ignored; anything
537
537
  unrecognized reads as `auto`, so a typo cannot stop a scan from running.
538
538
 
539
+ Each redraw clears the line and pads the new one out to the full width (the
540
+ terminal's, or 80 columns when there is none), so a capture of an `always` run
541
+ carries trailing spaces. It carries no escape sequences, and the summary that
542
+ survives the phase is a whole, newline-terminated line.
543
+
539
544
  The setting is read by the **core**, not the CLI, so it governs an index built
540
545
  from any SDK as well — a Python or TypeScript program that builds a `DirSQL`
541
546
  with a terminal attached gets the same two phases on stderr, and the same
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "just test-e2e",
3
+ "ran_at": 1789666612,
4
+ "exit_code": 0,
5
+ "commit": "991bcb921ba010ac18968740a79fd1d6fce05e11",
6
+ "branch": "claude/1081-indicatif-progress"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "just test-e2e",
3
+ "ran_at": 1789672718,
4
+ "exit_code": 0,
5
+ "commit": "9f049fb0a4012807ce2b31c220df75b0b33e88e1",
6
+ "branch": "claude/1119-ext-plan"
7
+ }
@@ -6,7 +6,7 @@ description = "Ephemeral SQL index over a local directory"
6
6
  # version stays for the binding crates (publish = false). The literal
7
7
  # intentionally lags the published version: putitoutthere rewrites it to
8
8
  # the planned release version at build time and never commits it back.
9
- version = "0.4.42"
9
+ version = "0.4.44"
10
10
  edition.workspace = true
11
11
  # Literal `license` (not `license.workspace = true`) because
12
12
  # putitoutthere's preflight check reads `[package].license` directly
@@ -59,12 +59,14 @@ cli = [
59
59
  axum = { version = "0.8", optional = true }
60
60
  blake3 = "1"
61
61
  clap = { version = "4", features = ["derive"], optional = true }
62
+ console = "0.16.6"
62
63
  futures = { version = "0.3", optional = true }
63
64
  futures-channel.workspace = true
64
65
  globset = "0.4"
65
66
  # Hierarchical `.gitignore` matching for path-table scans (the fd/ripgrep
66
67
  # semantics), without adopting its walker: the scan keeps `walkdir`.
67
68
  ignore = "0.4"
69
+ indicatif = "0.18.6"
68
70
  notify = "7"
69
71
  # The REPL's interactive line editor (history, Ctrl+R, multi-line entry).
70
72
  # CLI-only: a library consumer never pulls crossterm.
@@ -536,6 +536,11 @@ Override with `DIRSQL_PROGRESS`:
536
536
  Values are case-insensitive and surrounding whitespace is ignored; anything
537
537
  unrecognized reads as `auto`, so a typo cannot stop a scan from running.
538
538
 
539
+ Each redraw clears the line and pads the new one out to the full width (the
540
+ terminal's, or 80 columns when there is none), so a capture of an `always` run
541
+ carries trailing spaces. It carries no escape sequences, and the summary that
542
+ survives the phase is a whole, newline-terminated line.
543
+
539
544
  The setting is read by the **core**, not the CLI, so it governs an index built
540
545
  from any SDK as well — a Python or TypeScript program that builds a `DirSQL`
541
546
  with a terminal attached gets the same two phases on stderr, and the same
@@ -0,0 +1,421 @@
1
+ //! Planning the resolution of `[[dirsql.extension]]` entries.
2
+ //!
3
+ //! Each launcher used to carry its own port of this: read the configs, decide
4
+ //! whether any entry names a package rather than a file, resolve the rest
5
+ //! against the config's directory. Only "where is package `X` installed on
6
+ //! this host" is genuinely host-specific (`importlib.util.find_spec` /
7
+ //! `require.resolve`), so that stays with the launcher and comes back here as
8
+ //! a set of directories.
9
+ //!
10
+ //! The suffix list is a parameter because it differs per host: Python counts
11
+ //! `.pyd` as a loadable, Node counts `.node`.
12
+
13
+ use std::fmt;
14
+ use std::path::{Path, PathBuf};
15
+
16
+ use crate::config::{ExtensionSpec, load_config_str};
17
+
18
+ /// One config file's contents, or `None` when it is missing or unreadable.
19
+ ///
20
+ /// `path` must be absolute: entry paths and the bare-name shadow probe resolve
21
+ /// against its parent directory.
22
+ #[derive(Debug, Clone, Copy)]
23
+ pub struct ConfigSource<'a> {
24
+ pub path: &'a Path,
25
+ pub contents: Option<&'a str>,
26
+ }
27
+
28
+ /// One planned extension entry.
29
+ #[derive(Debug, Clone, PartialEq, Eq)]
30
+ pub enum PlanEntry {
31
+ /// A concrete path, already absolute against its config's directory.
32
+ Literal {
33
+ path: PathBuf,
34
+ entrypoint: Option<String>,
35
+ },
36
+ /// A bare package name. Use `shadow` when it is a file on disk; otherwise
37
+ /// locate the package on the host and hand its directories, plus every
38
+ /// file found under them, to [`select_loadable`].
39
+ Package {
40
+ name: String,
41
+ shadow: PathBuf,
42
+ entrypoint: Option<String>,
43
+ },
44
+ }
45
+
46
+ /// Why a package's loadable file could not be picked.
47
+ #[derive(Debug, Clone, PartialEq, Eq)]
48
+ pub enum SelectError {
49
+ NotFound {
50
+ name: String,
51
+ suffixes: Vec<String>,
52
+ dirs: Vec<PathBuf>,
53
+ },
54
+ Ambiguous {
55
+ name: String,
56
+ matches: Vec<PathBuf>,
57
+ },
58
+ }
59
+
60
+ fn join_display(paths: &[PathBuf], sep: &str) -> String {
61
+ paths
62
+ .iter()
63
+ .map(|p| p.display().to_string())
64
+ .collect::<Vec<_>>()
65
+ .join(sep)
66
+ }
67
+
68
+ impl fmt::Display for SelectError {
69
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70
+ match self {
71
+ SelectError::NotFound {
72
+ name,
73
+ suffixes,
74
+ dirs,
75
+ } => {
76
+ let patterns = suffixes
77
+ .iter()
78
+ .map(|s| format!("*{s}"))
79
+ .collect::<Vec<_>>()
80
+ .join(" / ");
81
+ write!(
82
+ f,
83
+ "no loadable extension file ({patterns}) found in package '{name}' (searched {})",
84
+ join_display(dirs, ", ")
85
+ )
86
+ }
87
+ SelectError::Ambiguous { name, matches } => write!(
88
+ f,
89
+ "multiple loadable extension files found in package '{name}': {}; disambiguate with a literal path",
90
+ join_display(matches, ", ")
91
+ ),
92
+ }
93
+ }
94
+ }
95
+
96
+ impl std::error::Error for SelectError {}
97
+
98
+ /// True when `path` names a package rather than a file.
99
+ ///
100
+ /// Both separators count regardless of host, so a Windows-style path is a path
101
+ /// everywhere.
102
+ pub fn is_bare_name(path: &str, suffixes: &[&str]) -> bool {
103
+ if path.contains('/') || path.contains('\\') {
104
+ return false;
105
+ }
106
+ !suffixes.iter().any(|s| path.ends_with(s))
107
+ }
108
+
109
+ /// Plan the extension entries of several configs, in order.
110
+ ///
111
+ /// `None` means "do not intervene": no config names an extension by bare
112
+ /// package name, so the core's own config loading (and its error reporting)
113
+ /// handles every entry. Otherwise **every** config's entries are planned, each
114
+ /// against its own directory, concatenated in `configs` order — once the
115
+ /// caller suppresses the core's config-extension loading it must supply the
116
+ /// literal entries too.
117
+ ///
118
+ /// A config whose contents are absent or do not parse is skipped rather than
119
+ /// reported, leaving the error to the core.
120
+ pub fn plan_config_extensions(
121
+ configs: &[ConfigSource<'_>],
122
+ suffixes: &[&str],
123
+ ) -> Option<Vec<PlanEntry>> {
124
+ let loaded: Vec<(PathBuf, Vec<ExtensionSpec>)> = configs
125
+ .iter()
126
+ .filter_map(|source| {
127
+ let config = load_config_str(source.contents?).ok()?;
128
+ Some((source.path.parent()?.to_path_buf(), config.extensions))
129
+ })
130
+ .collect();
131
+
132
+ if !loaded
133
+ .iter()
134
+ .flat_map(|(_, extensions)| extensions)
135
+ .any(|extension| spec_is_bare_name(extension, suffixes))
136
+ {
137
+ return None;
138
+ }
139
+
140
+ Some(
141
+ loaded
142
+ .into_iter()
143
+ .flat_map(|(base, extensions)| {
144
+ extensions.into_iter().map(move |extension| {
145
+ let name = extension.path.to_string_lossy().into_owned();
146
+ if is_bare_name(&name, suffixes) {
147
+ PlanEntry::Package {
148
+ shadow: base.join(&name),
149
+ name,
150
+ entrypoint: extension.entrypoint,
151
+ }
152
+ } else {
153
+ // `join` already yields an absolute path verbatim.
154
+ PlanEntry::Literal {
155
+ path: base.join(&extension.path),
156
+ entrypoint: extension.entrypoint,
157
+ }
158
+ }
159
+ })
160
+ })
161
+ .collect(),
162
+ )
163
+ }
164
+
165
+ fn spec_is_bare_name(extension: &ExtensionSpec, suffixes: &[&str]) -> bool {
166
+ extension
167
+ .path
168
+ .to_str()
169
+ .is_some_and(|path| is_bare_name(path, suffixes))
170
+ }
171
+
172
+ /// Pick the single loadable file for package `name` out of a host's listing.
173
+ ///
174
+ /// `dirs` are the package's directories (used only for the not-found message);
175
+ /// `candidates` is every file found under them. Zero matches and more than one
176
+ /// are both errors — the user must disambiguate with a literal path.
177
+ pub fn select_loadable(
178
+ name: &str,
179
+ dirs: &[PathBuf],
180
+ candidates: &[PathBuf],
181
+ suffixes: &[&str],
182
+ ) -> Result<PathBuf, SelectError> {
183
+ let mut matches: Vec<PathBuf> = candidates
184
+ .iter()
185
+ .filter(|candidate| {
186
+ candidate
187
+ .to_str()
188
+ .is_some_and(|path| suffixes.iter().any(|suffix| path.ends_with(suffix)))
189
+ })
190
+ .cloned()
191
+ .collect();
192
+ matches.sort();
193
+ matches.dedup();
194
+
195
+ match matches.len() {
196
+ 0 => Err(SelectError::NotFound {
197
+ name: name.to_owned(),
198
+ suffixes: suffixes.iter().map(|s| (*s).to_owned()).collect(),
199
+ dirs: dirs.to_vec(),
200
+ }),
201
+ 1 => Ok(matches.remove(0)),
202
+ _ => Err(SelectError::Ambiguous {
203
+ name: name.to_owned(),
204
+ matches,
205
+ }),
206
+ }
207
+ }
208
+
209
+ #[cfg(test)]
210
+ mod tests {
211
+ use super::{
212
+ ConfigSource, PlanEntry, SelectError, is_bare_name, plan_config_extensions, select_loadable,
213
+ };
214
+ use std::path::{Path, PathBuf};
215
+
216
+ const PY: &[&str] = &[".so", ".dylib", ".dll", ".pyd"];
217
+ const NODE: &[&str] = &[".so", ".dylib", ".dll", ".node"];
218
+
219
+ fn source<'a>(path: &'a Path, contents: &'a str) -> ConfigSource<'a> {
220
+ ConfigSource {
221
+ path,
222
+ contents: Some(contents),
223
+ }
224
+ }
225
+
226
+ #[test]
227
+ fn a_value_with_no_separator_and_no_loadable_suffix_is_a_bare_name() {
228
+ assert!(is_bare_name("sqlite_vec", PY));
229
+ assert!(is_bare_name("vec0", NODE));
230
+ }
231
+
232
+ #[test]
233
+ fn a_value_carrying_either_separator_is_a_path() {
234
+ assert!(!is_bare_name("ext/vec0", PY));
235
+ assert!(!is_bare_name("ext\\vec0", PY));
236
+ assert!(!is_bare_name("/abs/vec0", PY));
237
+ }
238
+
239
+ #[test]
240
+ fn a_value_ending_in_a_loadable_suffix_is_a_path() {
241
+ for suffix in [".so", ".dylib", ".dll"] {
242
+ assert!(!is_bare_name(&format!("vec0{suffix}"), PY));
243
+ assert!(!is_bare_name(&format!("vec0{suffix}"), NODE));
244
+ }
245
+ }
246
+
247
+ #[test]
248
+ fn the_suffix_list_is_the_hosts_own() {
249
+ assert!(!is_bare_name("vec0.pyd", PY));
250
+ assert!(is_bare_name("vec0.pyd", NODE));
251
+ assert!(!is_bare_name("vec0.node", NODE));
252
+ assert!(is_bare_name("vec0.node", PY));
253
+ }
254
+
255
+ #[test]
256
+ fn configs_using_only_literal_paths_are_left_to_the_core() {
257
+ let path = Path::new("/project/.dirsql.toml");
258
+ let config = "[[dirsql.extension]]\npath = \"ext/vec0.so\"\n";
259
+ assert_eq!(plan_config_extensions(&[source(path, config)], PY), None);
260
+ }
261
+
262
+ #[test]
263
+ fn a_config_with_no_extension_array_is_left_to_the_core() {
264
+ let path = Path::new("/project/.dirsql.toml");
265
+ assert_eq!(
266
+ plan_config_extensions(&[source(path, "[dirsql]\nignore = [\"*.tmp\"]\n")], PY),
267
+ None
268
+ );
269
+ }
270
+
271
+ #[test]
272
+ fn an_absent_or_unparseable_config_is_skipped_rather_than_reported() {
273
+ let path = Path::new("/project/.dirsql.toml");
274
+ let missing = ConfigSource {
275
+ path,
276
+ contents: None,
277
+ };
278
+ assert_eq!(plan_config_extensions(&[missing], PY), None);
279
+ assert_eq!(
280
+ plan_config_extensions(&[source(path, "not valid = [")], PY),
281
+ None
282
+ );
283
+ }
284
+
285
+ #[test]
286
+ fn a_bare_name_plans_a_package_lookup_shadowed_by_the_configs_own_directory() {
287
+ let path = Path::new("/project/conf/.dirsql.toml");
288
+ let plan = plan_config_extensions(
289
+ &[source(
290
+ path,
291
+ "[[dirsql.extension]]\npath = \"sqlite_vec\"\n",
292
+ )],
293
+ PY,
294
+ )
295
+ .expect("a bare name makes the SDK intervene");
296
+
297
+ assert_eq!(
298
+ plan,
299
+ vec![PlanEntry::Package {
300
+ name: "sqlite_vec".into(),
301
+ shadow: PathBuf::from("/project/conf/sqlite_vec"),
302
+ entrypoint: None,
303
+ }]
304
+ );
305
+ }
306
+
307
+ #[test]
308
+ fn intervening_plans_the_literal_entries_too_and_keeps_config_order() {
309
+ let first = Path::new("/project/one/.dirsql.toml");
310
+ let second = Path::new("/project/two/.dirsql.toml");
311
+ let plan = plan_config_extensions(
312
+ &[
313
+ source(
314
+ first,
315
+ "[[dirsql.extension]]\npath = \"ext/vec0.so\"\nentrypoint = \"sqlite3_vec_init\"\n",
316
+ ),
317
+ source(second, "[[dirsql.extension]]\npath = \"sqlite_vec\"\n"),
318
+ ],
319
+ PY,
320
+ )
321
+ .expect("a bare name anywhere makes the SDK intervene for the whole set");
322
+
323
+ assert_eq!(
324
+ plan,
325
+ vec![
326
+ PlanEntry::Literal {
327
+ path: PathBuf::from("/project/one/ext/vec0.so"),
328
+ entrypoint: Some("sqlite3_vec_init".into()),
329
+ },
330
+ PlanEntry::Package {
331
+ name: "sqlite_vec".into(),
332
+ shadow: PathBuf::from("/project/two/sqlite_vec"),
333
+ entrypoint: None,
334
+ },
335
+ ]
336
+ );
337
+ }
338
+
339
+ #[test]
340
+ fn an_absolute_entry_path_is_left_alone() {
341
+ let path = Path::new("/project/.dirsql.toml");
342
+ let plan = plan_config_extensions(
343
+ &[source(
344
+ path,
345
+ "[[dirsql.extension]]\npath = \"/opt/vec0.so\"\n\n[[dirsql.extension]]\npath = \"sqlite_vec\"\n",
346
+ )],
347
+ PY,
348
+ )
349
+ .expect("intervening");
350
+
351
+ assert_eq!(
352
+ plan[0],
353
+ PlanEntry::Literal {
354
+ path: PathBuf::from("/opt/vec0.so"),
355
+ entrypoint: None,
356
+ }
357
+ );
358
+ }
359
+
360
+ #[test]
361
+ fn select_loadable_returns_the_single_matching_file() {
362
+ let dir = PathBuf::from("/site-packages/sqlite_vec");
363
+ let found = select_loadable(
364
+ "sqlite_vec",
365
+ std::slice::from_ref(&dir),
366
+ &[
367
+ dir.join("__init__.py"),
368
+ dir.join("vec0.so"),
369
+ dir.join("README.md"),
370
+ ],
371
+ PY,
372
+ );
373
+ assert_eq!(found, Ok(dir.join("vec0.so")));
374
+ }
375
+
376
+ #[test]
377
+ fn select_loadable_dedupes_and_sorts_before_counting() {
378
+ let dir = PathBuf::from("/pkg");
379
+ let found = select_loadable(
380
+ "vec",
381
+ std::slice::from_ref(&dir),
382
+ &[dir.join("vec0.so"), dir.join("vec0.so")],
383
+ PY,
384
+ );
385
+ assert_eq!(found, Ok(dir.join("vec0.so")));
386
+ }
387
+
388
+ #[test]
389
+ fn select_loadable_reports_every_searched_directory_when_nothing_matches() {
390
+ let dirs = vec![PathBuf::from("/a"), PathBuf::from("/b")];
391
+ let err = select_loadable("vec", &dirs, &[PathBuf::from("/a/vec.py")], PY)
392
+ .expect_err("no loadable file");
393
+ assert_eq!(
394
+ err.to_string(),
395
+ "no loadable extension file (*.so / *.dylib / *.dll / *.pyd) found in package 'vec' (searched /a, /b)"
396
+ );
397
+ }
398
+
399
+ #[test]
400
+ fn select_loadable_rejects_an_ambiguous_package_in_sorted_order() {
401
+ let dir = PathBuf::from("/pkg");
402
+ let err = select_loadable(
403
+ "vec",
404
+ std::slice::from_ref(&dir),
405
+ &[dir.join("z.so"), dir.join("a.so")],
406
+ PY,
407
+ )
408
+ .expect_err("two loadable files");
409
+ assert_eq!(
410
+ err,
411
+ SelectError::Ambiguous {
412
+ name: "vec".into(),
413
+ matches: vec![dir.join("a.so"), dir.join("z.so")],
414
+ }
415
+ );
416
+ assert_eq!(
417
+ err.to_string(),
418
+ "multiple loadable extension files found in package 'vec': /pkg/a.so, /pkg/z.so; disambiguate with a literal path"
419
+ );
420
+ }
421
+ }
@@ -16,6 +16,8 @@ pub mod db;
16
16
  #[doc(hidden)]
17
17
  pub mod differ;
18
18
  #[doc(hidden)]
19
+ pub mod extension_resolution;
20
+ #[doc(hidden)]
19
21
  pub mod functions;
20
22
  #[doc(hidden)]
21
23
  pub mod infer;