dirsql 0.4.14__tar.gz → 0.4.15__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.4.14 → dirsql-0.4.15}/Cargo.lock +1 -1
- {dirsql-0.4.14 → dirsql-0.4.15}/PKG-INFO +2 -1
- dirsql-0.4.15/dirsql/cli/main.py +66 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/Cargo.toml +5 -2
- dirsql-0.4.15/packages/python/changelog.d/2026-08-04-cli-in-process.md +3 -0
- dirsql-0.4.15/packages/python/e2e-attestations/claude-737-cli-run-shim.json +7 -0
- dirsql-0.4.15/packages/python/e2e-attestations/claude-738-pypi-in-process.json +7 -0
- dirsql-0.4.15/packages/python/e2e-attestations/claude-739-npm-in-process.json +7 -0
- dirsql-0.4.15/packages/python/migrations.d/2026-08-04-cli-in-process.md +63 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/src/lib.rs +21 -0
- dirsql-0.4.15/packages/rust/changelog.d/2026-08-04-cli-run-shim.md +3 -0
- dirsql-0.4.15/packages/rust/src/bin/dirsql.rs +46 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/cli/mod.rs +2 -0
- dirsql-0.4.14/packages/rust/src/bin/dirsql.rs → dirsql-0.4.15/packages/rust/src/cli/run.rs +131 -43
- {dirsql-0.4.14 → dirsql-0.4.15}/pyproject.toml +7 -6
- dirsql-0.4.14/dirsql/cli/binary_path.py +0 -23
- dirsql-0.4.14/dirsql/cli/is_windows.py +0 -9
- dirsql-0.4.14/dirsql/cli/main.py +0 -42
- {dirsql-0.4.14 → dirsql-0.4.15}/Cargo.toml +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/README.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/__init__.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/_async.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/_dirsql.pyi +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/cli/__init__.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/cli/discover_plugins/__init__.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/cli/resolve_config_extensions.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/py.typed +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/resolve_config_extensions.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/dirsql/resolve_extension.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/AGENTS.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/explanation.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/getting-started.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/embed.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/persist.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/index.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/migrations.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/package.json +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/plugins.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/reference/cli.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/reference/columns.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/reference/config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/reference/hooks.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/reference/http-api.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/docs/reference/sdk.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/CHANGELOG.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/MIGRATIONS.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/README.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/changelog.d/README.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/conftest.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/AGENTS.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/explanation.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/getting-started.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/embed.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/persist.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/index.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/migrations.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/package.json +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/plugins.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/reference/cli.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/reference/columns.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/reference/config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/reference/hooks.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/reference/http-api.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/docs/reference/sdk.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/e2e-attestations/chore-766-narrow-pedantic.json +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/e2e-attestations/claude-772-extension-without-config.json +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/migrations.d/README.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/tests/conftest.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/tests/e2e/__init__.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/tests/integration/binding/__init__.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/python/tests/integration/hermetic/__init__.py +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/CHANGELOG.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/Cargo.toml +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/MIGRATIONS.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/README.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/benches/differ_bench.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/benches/scanner_bench.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-batch-ingest-transaction.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-chained-pre-post-query-hooks.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-fan-out-file-table-matching.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-persist-wal.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-repeatable-builder-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-repeatable-cli-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-13-subcommand-local-config-flags.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-19-path-table-query-fallback.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-19-path-table-vtab.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-20-path-table-glob-semantics.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-20-schema-inference.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-21-on-file-flag.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-23-init-escalation-example.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-23-query-default-cli.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-07-29-on-file-failures-accumulate.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-08-02-drop-regex.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-08-02-per-file-scan-failures.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-08-02-release-profile.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-08-03-gitignore-by-default.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-08-03-nested-default-ignores.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-08-04-cast-lints.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/2026-08-04-configless-extension.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/changelog.d/README.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/explanation.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/getting-started.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/embed.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/persist.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/index.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/migrations.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/plugins.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/reference/cli.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/reference/columns.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/reference/config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/reference/hooks.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/reference/http-api.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/docs/reference/sdk.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-13-fan-out-file-table-matching.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-13-persist-wal.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-13-subcommand-local-config-flags.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-20-path-table-glob-semantics.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-23-init-escalation-example.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-23-query-default-cli.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-08-02-per-file-scan-failures.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-08-03-gitignore-by-default.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/2026-08-03-nested-default-ignores.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/migrations.d/README.md +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/cli/execute.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/cli/init.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/cli/router.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/cli/serialize.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/cli/server.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/command.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/config.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/db.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/default_config.toml +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/differ.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/infer.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/lib.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/matcher.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/parsed_vtab.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/path_table.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/persist.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/scanner.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/vtab.rs +0 -0
- {dirsql-0.4.14 → dirsql-0.4.15}/packages/rust/src/watcher.rs +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dirsql
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.15
|
|
4
4
|
Requires-Dist: tomli>=2 ; python_full_version < '3.11'
|
|
5
|
+
Requires-Dist: bin-shim>=0.1
|
|
5
6
|
Summary: Ephemeral SQL index over a local directory
|
|
6
7
|
Keywords: sql,filesystem,directory,sqlite,index
|
|
7
8
|
Author: Kevin Scott
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Console-script entry point. Runs the CLI in-process through the compiled
|
|
2
|
+
extension module — the same `_dirsql` the SDK imports — so the wheel ships one
|
|
3
|
+
copy of the core instead of a `.so` plus a bundled binary (#738).
|
|
4
|
+
|
|
5
|
+
All argv is forwarded transparently to the core, which owns subcommand
|
|
6
|
+
dispatch; the launcher only prepends what the core cannot work out for itself
|
|
7
|
+
(plugin config fragments, resolved extension paths).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import signal
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
from bin_shim import main as run_in_process
|
|
16
|
+
|
|
17
|
+
from .discover_plugins.with_discovered_plugins import with_discovered_plugins
|
|
18
|
+
from .resolve_config_extensions import with_resolved_extensions
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _absorb_interrupt(*_args: object) -> None:
|
|
22
|
+
"""Let the core's own shutdown decide the exit code on SIGINT.
|
|
23
|
+
|
|
24
|
+
signal-hook (which tokio uses) *chains*: it runs tokio's handler — which
|
|
25
|
+
drives `dirsql server`'s graceful shutdown, after which `run_cli` returns
|
|
26
|
+
0 — and then whatever handler was installed before it. CPython's default
|
|
27
|
+
is `default_int_handler`, which raises `KeyboardInterrupt`; that lands
|
|
28
|
+
after `run_cli` has already returned 0 and turns a clean shutdown into a
|
|
29
|
+
130. This handler occupies that slot without raising, so the core's exit
|
|
30
|
+
code is the one that survives, exactly as it does when the CLI is its own
|
|
31
|
+
process.
|
|
32
|
+
|
|
33
|
+
A signal arriving when the core is NOT handling signals still terminates:
|
|
34
|
+
`run_cli` is only reached with this installed, and it returns promptly for
|
|
35
|
+
every non-server command.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def with_core_owned_signals(handler=signal.signal):
|
|
40
|
+
"""Install `_absorb_interrupt` for SIGINT and return the prior handler."""
|
|
41
|
+
return handler(signal.SIGINT, _absorb_interrupt)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main(argv: list[str] | None = None) -> int:
|
|
45
|
+
if argv is None:
|
|
46
|
+
argv = sys.argv[1:]
|
|
47
|
+
|
|
48
|
+
# Discover installed plugins (CLI only) and inject their config fragments as
|
|
49
|
+
# `-c` flags before resolving extensions; then resolve any package-name
|
|
50
|
+
# extensions in a TOML config here (the core can't) as `--extension`
|
|
51
|
+
# flags. Both are no-ops when nothing applies.
|
|
52
|
+
try:
|
|
53
|
+
argv = with_discovered_plugins(argv)
|
|
54
|
+
argv = with_resolved_extensions(argv)
|
|
55
|
+
except Exception as exc:
|
|
56
|
+
print(f"dirsql: {exc}", file=sys.stderr)
|
|
57
|
+
return 1
|
|
58
|
+
|
|
59
|
+
previous = with_core_owned_signals()
|
|
60
|
+
try:
|
|
61
|
+
return run_in_process(argv=argv, module="dirsql._dirsql")
|
|
62
|
+
except Exception as exc:
|
|
63
|
+
print(f"dirsql: {exc}", file=sys.stderr)
|
|
64
|
+
return 1
|
|
65
|
+
finally:
|
|
66
|
+
signal.signal(signal.SIGINT, previous)
|
|
@@ -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.
|
|
7
|
+
version = "0.4.15"
|
|
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
|
|
@@ -22,7 +22,10 @@ name = "dirsql_py_ext"
|
|
|
22
22
|
crate-type = ["cdylib", "rlib"]
|
|
23
23
|
|
|
24
24
|
[dependencies]
|
|
25
|
-
dirsql
|
|
25
|
+
# `cli` is on so the extension module carries `dirsql::cli::run_cli`: the
|
|
26
|
+
# console script calls it in-process instead of exec'ing a bundled binary
|
|
27
|
+
# (#738), which is what lets `dirsql/_binary/` leave the wheel.
|
|
28
|
+
dirsql = { workspace = true, features = ["cli"] }
|
|
26
29
|
pyo3 = { version = "0.28", features = ["extension-module", "generate-import-lib", "abi3-py310"], optional = true }
|
|
27
30
|
|
|
28
31
|
[lints.rust]
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
**Changed** — the `dirsql` console script now runs **in-process** through the compiled extension module instead of `exec`ing a bundled binary. `_dirsql` is built with the core's `cli` feature and exports `run_cli`, which the launcher calls via [bin-shim](https://pypi.org/project/bin-shim/). Plugin discovery and config-extension resolution are unchanged and still run first. The Windows/POSIX split is gone — `os.execv` on POSIX and `subprocess.run` on Windows are replaced by one in-process call on every platform, so the two no longer drift.
|
|
2
|
+
|
|
3
|
+
**Removed** — **BREAKING (packaging)**: the wheel no longer ships `dirsql/_binary/`. It held a standalone `dirsql` executable duplicating the core already present in the extension module; `pyproject.toml`'s `[tool.maturin].include` row and the release pipeline's pypi `bundle_cli` staging are both gone. `pip install dirsql && dirsql ...` and `uvx dirsql` behave exactly as before — verified across `--version`, a query, a SQL error (1), an unknown flag (2), and `dirsql server` + Ctrl-C (graceful shutdown, **0**). Anyone locating that binary by path must instead call `dirsql._dirsql.run_cli(argv)`, or install the Rust binary with `cargo install dirsql --features cli`. See [MIGRATIONS.md](../../MIGRATIONS.md). #738.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# The wheel's CLI runs in-process; `dirsql/_binary/` is gone
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
The `dirsql` console script no longer `exec`s a bundled binary. The extension
|
|
6
|
+
module (`dirsql._dirsql`) is built with the core's `cli` feature and exports
|
|
7
|
+
`run_cli`; the launcher calls it in this process via bin-shim. The wheel
|
|
8
|
+
therefore ships one copy of the core — the extension module — instead of that
|
|
9
|
+
module *plus* a standalone executable.
|
|
10
|
+
|
|
11
|
+
## Required changes
|
|
12
|
+
|
|
13
|
+
**For nearly everyone: none.** `pip install dirsql`, `uvx dirsql`, and the
|
|
14
|
+
`dirsql` console script behave as before: same argv handling, same output,
|
|
15
|
+
same exit codes.
|
|
16
|
+
|
|
17
|
+
Action is needed only if you located the bundled binary by path — e.g.
|
|
18
|
+
`importlib.resources.files("dirsql") / "_binary" / "dirsql"` — and executed it
|
|
19
|
+
yourself:
|
|
20
|
+
|
|
21
|
+
- Call `dirsql._dirsql.run_cli(argv)` instead. It returns an exit code rather
|
|
22
|
+
than terminating the process (`argv` excludes the program name).
|
|
23
|
+
- If you need a standalone executable, `cargo install dirsql --features cli`
|
|
24
|
+
builds the same code.
|
|
25
|
+
|
|
26
|
+
## Deprecations removed
|
|
27
|
+
|
|
28
|
+
`dirsql/_binary/` is no longer part of the wheel. The
|
|
29
|
+
`{ path = "dirsql/_binary/*", format = "wheel" }` maturin include row and the
|
|
30
|
+
pypi `[package.bundle_cli]` release block are both removed.
|
|
31
|
+
|
|
32
|
+
`dirsql.cli.binary_path` and `dirsql.cli.is_windows` are deleted — both existed
|
|
33
|
+
only to locate and exec that binary.
|
|
34
|
+
|
|
35
|
+
## Behavior changes without code changes
|
|
36
|
+
|
|
37
|
+
- **The CLI shares the console script's process.** For `dirsql server` the
|
|
38
|
+
Python interpreter stays resident for the server's lifetime rather than
|
|
39
|
+
being replaced by `os.execv`.
|
|
40
|
+
- **Windows and POSIX take the same path.** The `is_windows()` branch
|
|
41
|
+
(`subprocess.run` vs `os.execv`) is gone, so the platforms cannot drift.
|
|
42
|
+
- **`dirsql server` + Ctrl-C still exits 0.** This needed care and is worth
|
|
43
|
+
recording. signal-hook (which tokio uses) chains to the handler installed
|
|
44
|
+
before it; CPython's `default_int_handler` raises `KeyboardInterrupt`, which
|
|
45
|
+
lands *after* `run_cli` has already returned 0 from its graceful shutdown
|
|
46
|
+
and which bin-shim then converts to 130. Measured: the naive wiring exits
|
|
47
|
+
**130** where the old `execv` path exits **0**. The launcher therefore
|
|
48
|
+
installs a non-raising SIGINT handler for the duration of the run so the
|
|
49
|
+
core's own exit code survives, and restores the previous handler afterward.
|
|
50
|
+
This keeps the wheel's behavior identical to before *and* matches the npm
|
|
51
|
+
launcher (#739), which also exits 0.
|
|
52
|
+
|
|
53
|
+
## Verification
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
$ dirsql --version # 0 dirsql 0.2.7
|
|
57
|
+
$ dirsql query "SELECT COUNT(*) AS n FROM './'" # 0 [{"n":2}]
|
|
58
|
+
$ dirsql query "SELECT frobnicate()" # 1 SQLite error: no such function
|
|
59
|
+
$ dirsql --nope # 2 error: unexpected argument
|
|
60
|
+
$ dirsql server --port 7242 & kill -INT $! # 0 graceful shutdown
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Python e2e suite: 27 passed. Unit suite: 108 passed at 100% coverage.
|
|
@@ -446,6 +446,26 @@ mod python {
|
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
448
|
|
|
449
|
+
/// Run the `dirsql` CLI in this process and return its exit code.
|
|
450
|
+
///
|
|
451
|
+
/// The console script calls this instead of exec'ing a bundled binary, so
|
|
452
|
+
/// the wheel ships one copy of the core rather than two (#738). `argv`
|
|
453
|
+
/// excludes the program name; the core prepends it for clap.
|
|
454
|
+
///
|
|
455
|
+
/// Returns rather than exiting — the caller (bin-shim) owns the process
|
|
456
|
+
/// exit. Codes are ordinary status codes, never 130/143 (#737).
|
|
457
|
+
///
|
|
458
|
+
/// The GIL is released for the duration: the CLI can run a long-lived
|
|
459
|
+
/// server, and holding the GIL would wedge every other Python thread in
|
|
460
|
+
/// the host for its lifetime.
|
|
461
|
+
#[pyfunction]
|
|
462
|
+
fn run_cli(py: Python<'_>, argv: Vec<String>) -> i32 {
|
|
463
|
+
let mut full = Vec::with_capacity(argv.len() + 1);
|
|
464
|
+
full.push("dirsql".to_string());
|
|
465
|
+
full.extend(argv);
|
|
466
|
+
py.detach(|| dirsql::cli::run_cli(full))
|
|
467
|
+
}
|
|
468
|
+
|
|
449
469
|
#[pymodule]
|
|
450
470
|
#[pyo3(name = "_dirsql")]
|
|
451
471
|
fn py_dirsql_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
@@ -454,6 +474,7 @@ mod python {
|
|
|
454
474
|
m.add_class::<PyDirSQL>()?;
|
|
455
475
|
m.add_class::<PyRowEvent>()?;
|
|
456
476
|
m.add_class::<PyScanFailure>()?;
|
|
477
|
+
m.add_function(wrap_pyfunction!(run_cli, m)?)?;
|
|
457
478
|
Ok(())
|
|
458
479
|
}
|
|
459
480
|
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
**Added** — `dirsql::cli::run_cli(argv) -> i32`, the CLI's single entry point, now public on the crate (behind `--features cli`). It parses an explicit `argv`, dispatches the same commands the binary always has, and **returns** its exit code rather than terminating the process: clap's `parse()` calls `std::process::exit` on `--help`, `--version` and usage errors, so `run_cli` parses fallibly and renders the result itself — same text, same streams, same codes, minus the exit. That makes the CLI callable from an embedding host (the pip/npm launchers, via their bindings) instead of only from a process of its own. Exit codes stay ordinary status codes and are never 130/143: a `SIGINT` to `dirsql server` still shuts down gracefully and returns 0, exactly as before.
|
|
2
|
+
|
|
3
|
+
**Changed** — the 956-line `src/bin/dirsql.rs` (argument parsing, command dispatch, config/state loading) moved into `src/cli/run.rs`; the binary is now a shim that calls `run_cli` and exits with what it returns, so `cargo install dirsql --features cli` and every other entry path run the same code. Behavior is unchanged and was verified so: a capture harness recorded stdout, stderr and exit code for 23 invocations — every clap help/version/usage-error path, each subcommand, and the config/SQL/extension error paths — against binaries built before and after the move, and the two captures are byte-identical.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//! The `dirsql` binary: a shim over [`dirsql::cli::run_cli`].
|
|
2
|
+
//!
|
|
3
|
+
//! Every packaging path runs the same code — `cargo install dirsql --features
|
|
4
|
+
//! cli` compiles this shim, and the pip/npm launchers reach `run_cli` through
|
|
5
|
+
//! their bindings. All argument parsing, dispatch and exit-code selection live
|
|
6
|
+
//! in the library so none of it can drift per entry point.
|
|
7
|
+
//!
|
|
8
|
+
//! Only compiled with `--features cli`.
|
|
9
|
+
|
|
10
|
+
use std::process::ExitCode;
|
|
11
|
+
|
|
12
|
+
fn main() -> ExitCode {
|
|
13
|
+
// `run_cli` returns rather than exiting, so the process exit lives here.
|
|
14
|
+
ExitCode::from(exit_status(dirsql::cli::run_cli(
|
|
15
|
+
std::env::args().collect(),
|
|
16
|
+
)))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// Narrow `run_cli`'s `i32` to the `u8` a process exit status actually
|
|
20
|
+
/// carries. `run_cli` is `i32` for the binding boundary but only ever yields
|
|
21
|
+
/// 0..=255; anything outside that range would be truncated by the OS anyway,
|
|
22
|
+
/// so it is clamped to a generic failure rather than wrapped into a
|
|
23
|
+
/// misleading code (a hypothetical 256 must not surface as success).
|
|
24
|
+
fn exit_status(code: i32) -> u8 {
|
|
25
|
+
u8::try_from(code).unwrap_or(1)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[cfg(test)]
|
|
29
|
+
mod tests {
|
|
30
|
+
use super::exit_status;
|
|
31
|
+
|
|
32
|
+
#[test]
|
|
33
|
+
fn passes_through_the_codes_run_cli_produces() {
|
|
34
|
+
assert_eq!(exit_status(0), 0);
|
|
35
|
+
assert_eq!(exit_status(1), 1);
|
|
36
|
+
assert_eq!(exit_status(2), 2);
|
|
37
|
+
assert_eq!(exit_status(255), 255);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[test]
|
|
41
|
+
fn clamps_out_of_range_codes_to_failure() {
|
|
42
|
+
// Truncating would turn these into 0 — a failure reported as success.
|
|
43
|
+
assert_eq!(exit_status(256), 1);
|
|
44
|
+
assert_eq!(exit_status(-1), 1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -34,9 +34,11 @@ use crate::command::DEFAULT_COMMAND_TIMEOUT;
|
|
|
34
34
|
pub mod execute;
|
|
35
35
|
pub mod init;
|
|
36
36
|
pub mod router;
|
|
37
|
+
pub mod run;
|
|
37
38
|
pub mod serialize;
|
|
38
39
|
pub mod server;
|
|
39
40
|
|
|
41
|
+
pub use run::run_cli;
|
|
40
42
|
pub use server::{serve, serve_with_state};
|
|
41
43
|
|
|
42
44
|
/// Re-export of the crate-level baked-in default config
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
//! `dirsql` CLI
|
|
1
|
+
//! The `dirsql` CLI: argument parsing and command dispatch behind
|
|
2
|
+
//! [`run_cli`]. Query is the default; the server is a subcommand:
|
|
2
3
|
//! - No subcommand + SQL (`dirsql "<sql>"`): one-shot query, the default
|
|
3
4
|
//! behavior. Identical to `dirsql query "<sql>"`.
|
|
4
5
|
//! - `query`: explicit synonym for the default one-shot query; see
|
|
@@ -8,16 +9,18 @@
|
|
|
8
9
|
//! - No subcommand and no SQL: a usage error pointing at `dirsql server`.
|
|
9
10
|
//!
|
|
10
11
|
//! Only compiled with `--features cli`.
|
|
12
|
+
//!
|
|
13
|
+
//! The `dirsql` binary is a shim over [`run_cli`], so `cargo install dirsql
|
|
14
|
+
//! --features cli` and every other entry path run the same code.
|
|
11
15
|
|
|
12
16
|
use std::path::PathBuf;
|
|
13
|
-
use std::process::ExitCode;
|
|
14
17
|
|
|
15
|
-
use
|
|
16
|
-
use dirsql::cli::{
|
|
18
|
+
use super::{
|
|
17
19
|
AppState, PostQuery, PreQuery, ServerConfig, execute::execute_query, init::InitOptions,
|
|
18
20
|
serve_with_state,
|
|
19
21
|
};
|
|
20
|
-
use
|
|
22
|
+
use crate::{DirSQL, Extension, Row, Table};
|
|
23
|
+
use clap::{Args, Parser, Subcommand};
|
|
21
24
|
|
|
22
25
|
#[derive(Debug, Parser)]
|
|
23
26
|
#[command(
|
|
@@ -126,7 +129,7 @@ impl ConfigArgs {
|
|
|
126
129
|
/// Apply the index-shaping flags to a builder: `--persist [PATH]` (absent
|
|
127
130
|
/// → no change; bare `--persist` → the default location; `--persist
|
|
128
131
|
/// <path>` → that path) and `--no-ignore`.
|
|
129
|
-
fn apply_index_flags(&self, mut builder:
|
|
132
|
+
fn apply_index_flags(&self, mut builder: crate::DirSQLBuilder) -> crate::DirSQLBuilder {
|
|
130
133
|
if let Some(path) = &self.persist {
|
|
131
134
|
builder = builder.persist(path.as_ref());
|
|
132
135
|
}
|
|
@@ -235,16 +238,51 @@ struct InitArgs {
|
|
|
235
238
|
force: bool,
|
|
236
239
|
}
|
|
237
240
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
/// Run the CLI over an explicit `argv` and return its exit code.
|
|
242
|
+
///
|
|
243
|
+
/// The single entry point every packaging path shares: the `dirsql` binary is
|
|
244
|
+
/// a shim over this, and the language launchers reach it through their
|
|
245
|
+
/// bindings. Per the bin-shim contract it **always returns** — it never
|
|
246
|
+
/// terminates the host process, so an embedder stays in control of its own
|
|
247
|
+
/// exit. `argv[0]` is the program name, as `std::env::args()` yields it.
|
|
248
|
+
///
|
|
249
|
+
/// Exit codes are ordinary status codes only (never 130/143): a `SIGINT` to
|
|
250
|
+
/// `dirsql server` shuts down gracefully and returns 0, which is what the
|
|
251
|
+
/// binary has always done. Signals are handled *within* the run by
|
|
252
|
+
/// [`wait_for_shutdown`]; nothing is re-raised on the way out.
|
|
253
|
+
///
|
|
254
|
+
/// Clap's own `parse()` exits the process on `--help`, `--version` and usage
|
|
255
|
+
/// errors, so this parses fallibly and renders the result itself — same text,
|
|
256
|
+
/// same streams, same codes, minus the exit.
|
|
257
|
+
pub fn run_cli(argv: Vec<String>) -> i32 {
|
|
258
|
+
let mut cli = match Cli::try_parse_from(argv) {
|
|
259
|
+
Ok(cli) => cli,
|
|
260
|
+
Err(err) => {
|
|
261
|
+
let code = err.exit_code();
|
|
262
|
+
// Writes help/version to stdout and usage errors to stderr,
|
|
263
|
+
// exactly as clap's own `parse()` does before exiting.
|
|
264
|
+
let _ = err.print();
|
|
265
|
+
return code;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
let runtime = match tokio::runtime::Runtime::new() {
|
|
270
|
+
Ok(runtime) => runtime,
|
|
271
|
+
Err(err) => {
|
|
272
|
+
eprintln!("dirsql: failed to start the async runtime: {err}");
|
|
273
|
+
return 1;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
241
276
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
277
|
+
let code = runtime.block_on(async {
|
|
278
|
+
match cli.command.take() {
|
|
279
|
+
Some(Command::Init(args)) => run_init(args),
|
|
280
|
+
Some(Command::Query(args)) => run_query(args).await,
|
|
281
|
+
Some(Command::Server(args)) => run_server(args).await,
|
|
282
|
+
None => run_default(cli).await,
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
i32::from(code)
|
|
248
286
|
}
|
|
249
287
|
|
|
250
288
|
/// The default (no-subcommand) behavior: run the positional SQL as a one-shot
|
|
@@ -252,7 +290,7 @@ async fn main() -> ExitCode {
|
|
|
252
290
|
/// to run and no mode selected, so print a usage error pointing at
|
|
253
291
|
/// `dirsql server` — silently starting the server here would re-invert the
|
|
254
292
|
/// default this design deliberately fixed (#662).
|
|
255
|
-
async fn run_default(cli: Cli) ->
|
|
293
|
+
async fn run_default(cli: Cli) -> u8 {
|
|
256
294
|
match cli.sql {
|
|
257
295
|
Some(sql) => {
|
|
258
296
|
run_query(QueryArgs {
|
|
@@ -267,7 +305,7 @@ async fn run_default(cli: Cli) -> ExitCode {
|
|
|
267
305
|
"dirsql: no query given. Run a query with `dirsql \"SELECT * FROM './'\"`, \
|
|
268
306
|
or start the HTTP server with `dirsql server`. See `dirsql --help`."
|
|
269
307
|
);
|
|
270
|
-
|
|
308
|
+
2
|
|
271
309
|
}
|
|
272
310
|
}
|
|
273
311
|
}
|
|
@@ -275,15 +313,15 @@ async fn run_default(cli: Cli) -> ExitCode {
|
|
|
275
313
|
/// One-shot `dirsql query`: build the index exactly as server mode would
|
|
276
314
|
/// (same `load_state` / hook loading), run the SQL through the shared
|
|
277
315
|
/// [`execute_query`] pipeline, print the result JSON on stdout, and exit.
|
|
278
|
-
/// Any [`QueryFailure`](
|
|
316
|
+
/// Any [`QueryFailure`](super::execute::QueryFailure) prints its
|
|
279
317
|
/// message — the same string the HTTP `{"error": …}` body carries — to
|
|
280
318
|
/// stderr with a non-zero exit.
|
|
281
|
-
async fn run_query(args: QueryArgs) ->
|
|
319
|
+
async fn run_query(args: QueryArgs) -> u8 {
|
|
282
320
|
let parser = match resolve_on_file(&args.on_file) {
|
|
283
321
|
Ok(parser) => parser,
|
|
284
322
|
Err(message) => {
|
|
285
323
|
eprintln!("dirsql query: {message}");
|
|
286
|
-
return
|
|
324
|
+
return 1;
|
|
287
325
|
}
|
|
288
326
|
};
|
|
289
327
|
let state = load_state(&args.common, parser);
|
|
@@ -308,15 +346,11 @@ async fn run_query(args: QueryArgs) -> ExitCode {
|
|
|
308
346
|
// failure -- but the index behind them is missing files, and a
|
|
309
347
|
// caller piping into `jq` under `set -e` has no other way to find
|
|
310
348
|
// that out.
|
|
311
|
-
if skipped {
|
|
312
|
-
ExitCode::from(PARTIAL_SCAN_EXIT)
|
|
313
|
-
} else {
|
|
314
|
-
ExitCode::SUCCESS
|
|
315
|
-
}
|
|
349
|
+
if skipped { PARTIAL_SCAN_EXIT } else { 0 }
|
|
316
350
|
}
|
|
317
351
|
Err(failure) => {
|
|
318
352
|
eprintln!("dirsql query: {}", failure.message());
|
|
319
|
-
|
|
353
|
+
1
|
|
320
354
|
}
|
|
321
355
|
}
|
|
322
356
|
}
|
|
@@ -360,14 +394,14 @@ fn query_body(sql: &str) -> String {
|
|
|
360
394
|
serde_json::json!({ "sql": sql }).to_string()
|
|
361
395
|
}
|
|
362
396
|
|
|
363
|
-
fn run_init(args: InitArgs) ->
|
|
397
|
+
fn run_init(args: InitArgs) -> u8 {
|
|
364
398
|
let root = match args.root {
|
|
365
399
|
Some(p) => p,
|
|
366
400
|
None => match std::env::current_dir() {
|
|
367
401
|
Ok(p) => p,
|
|
368
402
|
Err(err) => {
|
|
369
403
|
eprintln!("dirsql init: failed to read current directory: {err}");
|
|
370
|
-
return
|
|
404
|
+
return 1;
|
|
371
405
|
}
|
|
372
406
|
},
|
|
373
407
|
};
|
|
@@ -378,16 +412,16 @@ fn run_init(args: InitArgs) -> ExitCode {
|
|
|
378
412
|
force: args.force,
|
|
379
413
|
};
|
|
380
414
|
|
|
381
|
-
match
|
|
382
|
-
Ok(()) =>
|
|
415
|
+
match super::init::run(opts) {
|
|
416
|
+
Ok(()) => 0,
|
|
383
417
|
Err(err) => {
|
|
384
418
|
eprintln!("dirsql init: {err}");
|
|
385
|
-
|
|
419
|
+
1
|
|
386
420
|
}
|
|
387
421
|
}
|
|
388
422
|
}
|
|
389
423
|
|
|
390
|
-
async fn run_server(args: ServerArgs) ->
|
|
424
|
+
async fn run_server(args: ServerArgs) -> u8 {
|
|
391
425
|
// The server has no `--on-file`: clap rejects it as an unknown flag before
|
|
392
426
|
// reaching here. Path-tables served over HTTP keep their stat columns.
|
|
393
427
|
let state = load_state(&args.common, None);
|
|
@@ -404,7 +438,7 @@ async fn run_server(args: ServerArgs) -> ExitCode {
|
|
|
404
438
|
Ok(handle) => handle,
|
|
405
439
|
Err(err) => {
|
|
406
440
|
eprintln!("dirsql: failed to bind: {err}");
|
|
407
|
-
return
|
|
441
|
+
return 1;
|
|
408
442
|
}
|
|
409
443
|
};
|
|
410
444
|
|
|
@@ -417,9 +451,9 @@ async fn run_server(args: ServerArgs) -> ExitCode {
|
|
|
417
451
|
|
|
418
452
|
if let Err(err) = handle.shutdown().await {
|
|
419
453
|
eprintln!("dirsql: shutdown error: {err}");
|
|
420
|
-
return
|
|
454
|
+
return 1;
|
|
421
455
|
}
|
|
422
|
-
|
|
456
|
+
0
|
|
423
457
|
}
|
|
424
458
|
|
|
425
459
|
fn load_state(cfg: &ConfigArgs, path_table_parser: Option<String>) -> AppState {
|
|
@@ -517,7 +551,7 @@ fn load_pre_queries(cfg: &ConfigArgs) -> Vec<PreQuery> {
|
|
|
517
551
|
let Ok(resolved) = config_path.canonicalize() else {
|
|
518
552
|
continue;
|
|
519
553
|
};
|
|
520
|
-
let Ok(config) =
|
|
554
|
+
let Ok(config) = crate::config::load_config(&resolved) else {
|
|
521
555
|
continue;
|
|
522
556
|
};
|
|
523
557
|
let Some(command) = config.pre_query else {
|
|
@@ -548,7 +582,7 @@ fn load_post_queries(cfg: &ConfigArgs) -> Vec<PostQuery> {
|
|
|
548
582
|
let Ok(resolved) = config_path.canonicalize() else {
|
|
549
583
|
continue;
|
|
550
584
|
};
|
|
551
|
-
let Ok(config) =
|
|
585
|
+
let Ok(config) = crate::config::load_config(&resolved) else {
|
|
552
586
|
continue;
|
|
553
587
|
};
|
|
554
588
|
let Some(command) = config.post_query else {
|
|
@@ -580,16 +614,20 @@ fn load_configless_state(cfg: &ConfigArgs, path_table_parser: Option<String>) ->
|
|
|
580
614
|
}
|
|
581
615
|
};
|
|
582
616
|
|
|
583
|
-
let mut builder = cfg.apply_index_flags(DirSQL::builder().root(root));
|
|
584
617
|
// `--extension` applies here too (#772). Without this the flag was
|
|
585
618
|
// silently ignored whenever no `-c` was given: `dirsql query "SELECT
|
|
586
619
|
// vec_version()" --extension <path>` reported `no such function` rather
|
|
587
620
|
// than loading anything, and `--extension /nonexistent.so` exited 0. A
|
|
588
621
|
// path-table query over an extension-provided function is a legitimate
|
|
589
622
|
// configless use, and a bad path must still fail loudly.
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
623
|
+
//
|
|
624
|
+
// Applied unconditionally: `extensions([])` assigns the builder's own
|
|
625
|
+
// default, and `apply_index_flags` never touches the field, so an
|
|
626
|
+
// `is_empty()` guard would only add a branch no unit test can reach --
|
|
627
|
+
// `load_configless_state` needs a real cwd and index (#233).
|
|
628
|
+
let mut builder = cfg
|
|
629
|
+
.apply_index_flags(DirSQL::builder().root(root))
|
|
630
|
+
.extensions(parse_extension_specs(&cfg.extension));
|
|
593
631
|
if let Some(command) = path_table_parser {
|
|
594
632
|
builder = builder.path_table_parser(command);
|
|
595
633
|
}
|
|
@@ -599,12 +637,12 @@ fn load_configless_state(cfg: &ConfigArgs, path_table_parser: Option<String>) ->
|
|
|
599
637
|
}
|
|
600
638
|
}
|
|
601
639
|
|
|
602
|
-
/// The shipped starter `records` table, parsed from the [`
|
|
640
|
+
/// The shipped starter `records` table, parsed from the [`crate::DEFAULT_CONFIG_TOML`]
|
|
603
641
|
/// asset `dirsql init` writes. Used only by the explicit `--include-default`
|
|
604
642
|
/// compose path (#604), which seeds it as a programmatic table *before* the
|
|
605
643
|
/// `-c` configs. There is no implicit no-`-c` fallback (#636).
|
|
606
644
|
fn default_records_table() -> Table {
|
|
607
|
-
let config =
|
|
645
|
+
let config = crate::config::load_config_str(crate::DEFAULT_CONFIG_TOML)
|
|
608
646
|
.expect("DEFAULT_CONFIG_TOML must be valid dirsql config TOML");
|
|
609
647
|
let table_config = &config.tables[0];
|
|
610
648
|
Table::new(
|
|
@@ -987,4 +1025,54 @@ mod tests {
|
|
|
987
1025
|
assert_eq!(exts[0].path, PathBuf::from("/a.so"));
|
|
988
1026
|
assert_eq!(exts[0].entrypoint.as_deref(), Some("init::extra"));
|
|
989
1027
|
}
|
|
1028
|
+
|
|
1029
|
+
#[test]
|
|
1030
|
+
fn run_cli_returns_zero_for_version_instead_of_exiting() {
|
|
1031
|
+
// The contract this slice exists to establish: clap's own `parse()`
|
|
1032
|
+
// calls `std::process::exit`, which would take an embedding host down
|
|
1033
|
+
// with it. If someone reinstates it, this test kills the test runner
|
|
1034
|
+
// rather than failing politely -- which is the point.
|
|
1035
|
+
assert_eq!(run_cli(vec!["dirsql".into(), "--version".into()]), 0);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
#[test]
|
|
1039
|
+
fn run_cli_returns_zero_for_help() {
|
|
1040
|
+
assert_eq!(run_cli(vec!["dirsql".into(), "--help".into()]), 0);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
#[test]
|
|
1044
|
+
fn run_cli_returns_clap_usage_code_for_an_unknown_flag() {
|
|
1045
|
+
assert_eq!(run_cli(vec!["dirsql".into(), "--nope".into()]), 2);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
#[test]
|
|
1049
|
+
fn run_cli_returns_a_usage_code_for_a_config_flag_before_a_subcommand() {
|
|
1050
|
+
// `args_conflicts_with_subcommands` makes misplacement a hard error
|
|
1051
|
+
// rather than a silently dropped flag (#609); it must surface as a
|
|
1052
|
+
// returned code like any other usage error.
|
|
1053
|
+
let code = run_cli(vec![
|
|
1054
|
+
"dirsql".into(),
|
|
1055
|
+
"-c".into(),
|
|
1056
|
+
"cfg.toml".into(),
|
|
1057
|
+
"query".into(),
|
|
1058
|
+
"SELECT 1".into(),
|
|
1059
|
+
]);
|
|
1060
|
+
assert_eq!(code, 2);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
#[test]
|
|
1064
|
+
fn report_scan_failures_is_false_when_the_index_is_degraded() {
|
|
1065
|
+
// The `Ready` arm needs a real scanned directory, so it lives in the
|
|
1066
|
+
// integration tier; the degraded arm short-circuits before any I/O.
|
|
1067
|
+
let state = AppState::Unavailable("config failed to load".to_string());
|
|
1068
|
+
assert!(!report_scan_failures(&state));
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
#[test]
|
|
1072
|
+
fn default_records_table_is_built_from_the_shipped_starter_config() {
|
|
1073
|
+
// `--include-default` seeds this table, so a starter config that
|
|
1074
|
+
// stopped parsing would break that flag rather than `dirsql init`.
|
|
1075
|
+
let table = default_records_table();
|
|
1076
|
+
assert!(table.ddl.contains("records"));
|
|
1077
|
+
}
|
|
990
1078
|
}
|
|
@@ -23,6 +23,9 @@ dependencies = [
|
|
|
23
23
|
# to the backport it was derived from. Drop this the day the floor reaches
|
|
24
24
|
# 3.11 again.
|
|
25
25
|
'tomli>=2; python_version < "3.11"',
|
|
26
|
+
# The console script runs the CLI in-process through this (#738); without
|
|
27
|
+
# it `dirsql.cli.main` cannot import.
|
|
28
|
+
"bin-shim>=0.1",
|
|
26
29
|
]
|
|
27
30
|
|
|
28
31
|
# Dev/test dependencies live in a PEP 735 dependency-group (not a
|
|
@@ -66,13 +69,11 @@ exclude = [
|
|
|
66
69
|
# `python-source = "python"`) once upstream's verifier handles
|
|
67
70
|
# python-source stripping.
|
|
68
71
|
module-name = "dirsql._dirsql"
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
# wheel
|
|
73
|
-
# source control.
|
|
72
|
+
# No `dirsql/_binary/*` row any more (#738): the wheel ships no standalone
|
|
73
|
+
# binary. The extension module built here carries the CLI (`run_cli`, via
|
|
74
|
+
# the core's `cli` feature) and the console script calls it in-process, so
|
|
75
|
+
# the wheel holds one copy of the core instead of a `.so` plus a binary.
|
|
74
76
|
include = [
|
|
75
|
-
{ path = "dirsql/_binary/*", format = "wheel" },
|
|
76
77
|
{ path = "docs/**/*", format = "sdist" },
|
|
77
78
|
]
|
|
78
79
|
manifest-path = "packages/python/Cargo.toml"
|