dirsql 0.4.59__tar.gz → 0.4.60__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 (255) hide show
  1. {dirsql-0.4.59 → dirsql-0.4.60}/Cargo.lock +2 -2
  2. {dirsql-0.4.59 → dirsql-0.4.60}/PKG-INFO +1 -1
  3. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/Cargo.toml +1 -1
  4. dirsql-0.4.60/packages/python/e2e-attestations/claude-1207-win-argv-quote.json +7 -0
  5. dirsql-0.4.60/packages/python/e2e-attestations/claude-1208-win-nonverbatim-paths.json +7 -0
  6. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/Cargo.toml +1 -1
  7. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/command.rs +254 -2
  8. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/extension_resolution.rs +5 -1
  9. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/lib.rs +25 -4
  10. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/parsed_vtab.rs +2 -2
  11. {dirsql-0.4.59 → dirsql-0.4.60}/Cargo.toml +0 -0
  12. {dirsql-0.4.59 → dirsql-0.4.60}/README.md +0 -0
  13. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/__init__.py +0 -0
  14. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/_async.py +0 -0
  15. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/_dirsql.pyi +0 -0
  16. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/cli/__init__.py +0 -0
  17. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/cli/discover_plugins/__init__.py +0 -0
  18. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
  19. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
  20. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
  21. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
  22. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
  23. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/cli/main.py +0 -0
  24. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/cli/resolve_config_extensions.py +0 -0
  25. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/plan_entry_path.py +0 -0
  26. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/py.typed +0 -0
  27. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/read_config.py +0 -0
  28. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/resolve_configs_extension_specs.py +0 -0
  29. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/resolve_extension.py +0 -0
  30. {dirsql-0.4.59 → dirsql-0.4.60}/dirsql/resolve_package.py +0 -0
  31. {dirsql-0.4.59 → dirsql-0.4.60}/docs/.claude/CLAUDE.md +0 -0
  32. {dirsql-0.4.59 → dirsql-0.4.60}/docs/.vitepress/config.ts +0 -0
  33. {dirsql-0.4.59 → dirsql-0.4.60}/docs/.vitepress/theme/index.ts +0 -0
  34. {dirsql-0.4.59 → dirsql-0.4.60}/docs/.vitepress/theme/lang.ts +0 -0
  35. {dirsql-0.4.59 → dirsql-0.4.60}/docs/AGENTS.md +0 -0
  36. {dirsql-0.4.59 → dirsql-0.4.60}/docs/explanation.md +0 -0
  37. {dirsql-0.4.59 → dirsql-0.4.60}/docs/getting-started.md +0 -0
  38. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/columns-from-paths.md +0 -0
  39. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/define-tables.md +0 -0
  40. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/embed.md +0 -0
  41. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/extract-from-contents.md +0 -0
  42. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/load-extension.md +0 -0
  43. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/parse-files-into-columns.md +0 -0
  44. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/persist.md +0 -0
  45. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/query-json.md +0 -0
  46. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/query-without-config.md +0 -0
  47. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/react-to-changes.md +0 -0
  48. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/search-by-meaning.md +0 -0
  49. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/search-indexes.md +0 -0
  50. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/skip-files.md +0 -0
  51. {dirsql-0.4.59 → dirsql-0.4.60}/docs/howto/write-a-plugin.md +0 -0
  52. {dirsql-0.4.59 → dirsql-0.4.60}/docs/index.md +0 -0
  53. {dirsql-0.4.59 → dirsql-0.4.60}/docs/package.json +0 -0
  54. {dirsql-0.4.59 → dirsql-0.4.60}/docs/plugins.md +0 -0
  55. {dirsql-0.4.59 → dirsql-0.4.60}/docs/pnpm-lock.yaml +0 -0
  56. {dirsql-0.4.59 → dirsql-0.4.60}/docs/pnpm-workspace.yaml +0 -0
  57. {dirsql-0.4.59 → dirsql-0.4.60}/docs/reference/cli.md +0 -0
  58. {dirsql-0.4.59 → dirsql-0.4.60}/docs/reference/columns.md +0 -0
  59. {dirsql-0.4.59 → dirsql-0.4.60}/docs/reference/config.md +0 -0
  60. {dirsql-0.4.59 → dirsql-0.4.60}/docs/reference/hooks.md +0 -0
  61. {dirsql-0.4.59 → dirsql-0.4.60}/docs/reference/http-api.md +0 -0
  62. {dirsql-0.4.59 → dirsql-0.4.60}/docs/reference/path-tables.md +0 -0
  63. {dirsql-0.4.59 → dirsql-0.4.60}/docs/reference/sdk.md +0 -0
  64. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/CHANGELOG.md +0 -0
  65. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/MIGRATIONS.md +0 -0
  66. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/README.md +0 -0
  67. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  68. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
  69. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  70. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
  71. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  72. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
  73. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
  74. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
  75. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  76. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
  77. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
  78. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
  79. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
  80. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
  81. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
  82. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
  83. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
  84. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
  85. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
  86. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-04-config-paths-from-argv-module.md +0 -0
  87. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-05-config-extension-loader-split.md +0 -0
  88. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-05-resolve-config-extensions-split.md +0 -0
  89. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-05-resolve-extension-split.md +0 -0
  90. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md +0 -0
  91. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-17-extension-resolution-via-core.md +0 -0
  92. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-18-export-scan-failure.md +0 -0
  93. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-18-flatten-row-event-in-core.md +0 -0
  94. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-18-programmatic-extension-plan.md +0 -0
  95. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  96. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  97. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/changelog.d/README.md +0 -0
  98. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/conftest.py +0 -0
  99. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/.claude/CLAUDE.md +0 -0
  100. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/.vitepress/config.ts +0 -0
  101. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  102. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  103. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/AGENTS.md +0 -0
  104. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/explanation.md +0 -0
  105. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/getting-started.md +0 -0
  106. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/columns-from-paths.md +0 -0
  107. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/define-tables.md +0 -0
  108. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/embed.md +0 -0
  109. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/extract-from-contents.md +0 -0
  110. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/load-extension.md +0 -0
  111. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
  112. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/persist.md +0 -0
  113. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/query-json.md +0 -0
  114. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/query-without-config.md +0 -0
  115. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/react-to-changes.md +0 -0
  116. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/search-by-meaning.md +0 -0
  117. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/search-indexes.md +0 -0
  118. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/skip-files.md +0 -0
  119. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/howto/write-a-plugin.md +0 -0
  120. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/index.md +0 -0
  121. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/package.json +0 -0
  122. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/plugins.md +0 -0
  123. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/pnpm-lock.yaml +0 -0
  124. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/pnpm-workspace.yaml +0 -0
  125. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/reference/cli.md +0 -0
  126. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/reference/columns.md +0 -0
  127. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/reference/config.md +0 -0
  128. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/reference/hooks.md +0 -0
  129. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/reference/http-api.md +0 -0
  130. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/reference/path-tables.md +0 -0
  131. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/docs/reference/sdk.md +0 -0
  132. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
  133. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1056-py-ofpf9.json +0 -0
  134. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1066-p1-resolve-extension.json +0 -0
  135. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1078-argv-config-paths-core.json +0 -0
  136. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1079-p2-resolve-config-extensions.json +0 -0
  137. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1080-share-vtab-scaffolding.json +0 -0
  138. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1081-indicatif-progress.json +0 -0
  139. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1096-watch-moved-dir.json +0 -0
  140. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1109-watch-moved-out.json +0 -0
  141. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1119-ext-plan.json +0 -0
  142. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1120-py-ext.json +0 -0
  143. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1122-ext-cleanup.json +0 -0
  144. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1130-codec-crates.json +0 -0
  145. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1131-flatten-row-event.json +0 -0
  146. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1139-sse-flatten.json +0 -0
  147. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1140-scan-failure-export.json +0 -0
  148. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1148-clippy-all-targets.json +0 -0
  149. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1164-sigint-fatal.json +0 -0
  150. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1170-projection-order.json +0 -0
  151. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1172-on-file-path.json +0 -0
  152. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1173-py-projection-order.json +0 -0
  153. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1184-win-rel-paths.json +0 -0
  154. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1185-win-abs-table-names.json +0 -0
  155. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-1186-win-test-harness.json +0 -0
  156. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
  157. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
  158. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
  159. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
  160. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
  161. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
  162. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
  163. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
  164. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
  165. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-tackle-1034-cachesplit.json +0 -0
  166. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-tackle-1042-py12.json +0 -0
  167. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
  168. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6-worker-calls.json +0 -0
  169. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +0 -0
  170. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +0 -0
  171. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  172. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  173. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  174. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
  175. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
  176. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
  177. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  178. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
  179. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
  180. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
  181. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  182. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  183. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/migrations.d/README.md +0 -0
  184. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/src/lib.rs +0 -0
  185. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/testing-conventions.toml +0 -0
  186. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/tests/__init__.py +0 -0
  187. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/tests/conftest.py +0 -0
  188. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/tests/e2e/__init__.py +0 -0
  189. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
  190. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
  191. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/tests/e2e/speed_of_light.py +0 -0
  192. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/tests/integration/__init__.py +0 -0
  193. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/tests/integration/binding/__init__.py +0 -0
  194. {dirsql-0.4.59 → dirsql-0.4.60}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  195. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/CHANGELOG.md +0 -0
  196. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/MIGRATIONS.md +0 -0
  197. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/README.md +0 -0
  198. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/benches/db_bench.rs +0 -0
  199. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/benches/differ_bench.rs +0 -0
  200. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/benches/matcher_bench.rs +0 -0
  201. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/benches/scanner_bench.rs +0 -0
  202. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/explanation.md +0 -0
  203. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/getting-started.md +0 -0
  204. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  205. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/define-tables.md +0 -0
  206. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/embed.md +0 -0
  207. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  208. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/load-extension.md +0 -0
  209. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
  210. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/persist.md +0 -0
  211. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/query-json.md +0 -0
  212. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/query-without-config.md +0 -0
  213. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/react-to-changes.md +0 -0
  214. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  215. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/search-indexes.md +0 -0
  216. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/skip-files.md +0 -0
  217. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/howto/write-a-plugin.md +0 -0
  218. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/index.md +0 -0
  219. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/plugins.md +0 -0
  220. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/reference/cli.md +0 -0
  221. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/reference/columns.md +0 -0
  222. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/reference/config.md +0 -0
  223. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/reference/hooks.md +0 -0
  224. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/reference/http-api.md +0 -0
  225. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/reference/path-tables.md +0 -0
  226. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/docs/reference/sdk.md +0 -0
  227. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/bin/dirsql.rs +0 -0
  228. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/cli/execute.rs +0 -0
  229. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/cli/init.rs +0 -0
  230. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/cli/mod.rs +0 -0
  231. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/cli/repl.rs +0 -0
  232. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/cli/router.rs +0 -0
  233. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/cli/run.rs +0 -0
  234. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/cli/serialize.rs +0 -0
  235. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/cli/server.rs +0 -0
  236. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/cli/table.rs +0 -0
  237. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/config.rs +0 -0
  238. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/db.rs +0 -0
  239. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/default_config.toml +0 -0
  240. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/differ.rs +0 -0
  241. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/functions.rs +0 -0
  242. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/infer.rs +0 -0
  243. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/launcher.rs +0 -0
  244. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/matcher.rs +0 -0
  245. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/parsed_cache.rs +0 -0
  246. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/path_table.rs +0 -0
  247. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/persist.rs +0 -0
  248. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/progress.rs +0 -0
  249. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/row_event_flat.rs +0 -0
  250. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/scanner.rs +0 -0
  251. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/sql_literal.rs +0 -0
  252. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/vtab.rs +0 -0
  253. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/vtab_scaffold.rs +0 -0
  254. {dirsql-0.4.59 → dirsql-0.4.60}/packages/rust/src/watcher.rs +0 -0
  255. {dirsql-0.4.59 → dirsql-0.4.60}/pyproject.toml +0 -0
@@ -552,7 +552,7 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
552
552
 
553
553
  [[package]]
554
554
  name = "dirsql"
555
- version = "0.4.59"
555
+ version = "0.4.60"
556
556
  dependencies = [
557
557
  "assert_cmd",
558
558
  "axum",
@@ -601,7 +601,7 @@ dependencies = [
601
601
 
602
602
  [[package]]
603
603
  name = "dirsql-py-ext"
604
- version = "0.4.59"
604
+ version = "0.4.60"
605
605
  dependencies = [
606
606
  "dirsql",
607
607
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.4.59
3
+ Version: 0.4.60
4
4
  Requires-Dist: bin-shim>=0.1
5
5
  Summary: Ephemeral SQL index over a local directory
6
6
  Keywords: sql,filesystem,directory,sqlite,index
@@ -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.59"
7
+ version = "0.4.60"
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
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "just test-e2e",
3
+ "ran_at": 1790939617,
4
+ "exit_code": 0,
5
+ "commit": "009c015ffa36453da6d92ddec17b5b655b51372f",
6
+ "branch": "claude/1207-win-argv-quote"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "just test-e2e",
3
+ "ran_at": 1790939958,
4
+ "exit_code": 0,
5
+ "commit": "64a48bb1eff07865a52fc25e1ad50722aced9e45",
6
+ "branch": "claude/1208-win-nonverbatim-paths"
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.59"
9
+ version = "0.4.60"
10
10
  edition.workspace = true
11
11
  # Literal `license` (not `license.workspace = true`) because
12
12
  # putitoutthere's preflight check reads `[package].license` directly
@@ -49,6 +49,63 @@ impl Placeholder {
49
49
  value: value.into(),
50
50
  }
51
51
  }
52
+
53
+ /// A placeholder whose value is a filesystem path, handed to the child in
54
+ /// its plain (non-verbatim) form; see [`non_verbatim`].
55
+ pub fn path(name: impl Into<String>, path: &str) -> Self {
56
+ Self::new(name, non_verbatim(path))
57
+ }
58
+ }
59
+
60
+ /// The plain form of a Windows verbatim path: `\\?\C:\x` becomes `C:\x` and
61
+ /// `\\?\UNC\server\share\x` becomes `\\server\share\x`. Anything else, and any
62
+ /// verbatim path whose plain spelling would name a different file, is returned
63
+ /// unchanged.
64
+ pub(crate) fn non_verbatim(path: &str) -> String {
65
+ plain_form(path).unwrap_or_else(|| path.to_string())
66
+ }
67
+
68
+ fn plain_form(path: &str) -> Option<String> {
69
+ let rest = path.strip_prefix(r"\\?\")?;
70
+ let (plain, tail) = if let Some(unc) = rest.strip_prefix(r"UNC\") {
71
+ if unc.split('\\').count() < 2 {
72
+ return None;
73
+ }
74
+ (format!(r"\\{unc}"), unc)
75
+ } else {
76
+ let drive = rest.as_bytes();
77
+ let is_drive = drive.len() >= 3
78
+ && drive[0].is_ascii_alphabetic()
79
+ && drive[1] == b':'
80
+ && drive[2] == b'\\';
81
+ if !is_drive {
82
+ return None;
83
+ }
84
+ (rest.to_string(), &rest[3..])
85
+ };
86
+ // Win32 rejects or rewrites these, so the plain spelling would not name
87
+ // the same file; MAX_PATH counts UTF-16 units including the terminator.
88
+ let same_file = (tail.is_empty() || tail.split('\\').all(is_plain_component))
89
+ && plain.encode_utf16().count() < 260;
90
+ same_file.then_some(plain)
91
+ }
92
+
93
+ fn is_plain_component(name: &str) -> bool {
94
+ let invalid_char = |c: char| c < ' ' || r#"/<>:"|?*"#.contains(c);
95
+ let stem = name
96
+ .split_once('.')
97
+ .map_or(name, |(stem, _)| stem)
98
+ .trim_end_matches(' ');
99
+ let reserved = matches!(
100
+ stem.to_ascii_uppercase().as_bytes(),
101
+ b"CON"
102
+ | b"PRN"
103
+ | b"AUX"
104
+ | b"NUL"
105
+ | [b'C', b'O', b'M', b'1'..=b'9']
106
+ | [b'L', b'P', b'T', b'1'..=b'9']
107
+ );
108
+ !name.is_empty() && !name.ends_with(['.', ' ']) && !name.contains(invalid_char) && !reserved
52
109
  }
53
110
 
54
111
  /// A successful command run.
@@ -119,8 +176,8 @@ pub fn run_command(
119
176
  let argv = build_argv(command, placeholders)?;
120
177
  // `build_argv` guarantees a non-empty argv.
121
178
  let mut cmd = Command::new(&argv[0]);
122
- cmd.args(&argv[1..])
123
- .current_dir(cwd)
179
+ push_args(&mut cmd, &argv[1..]);
180
+ cmd.current_dir(cwd)
124
181
  .stdout(Stdio::piped())
125
182
  .stderr(Stdio::piped())
126
183
  .stdin(if stdin_payload.is_some() {
@@ -193,6 +250,20 @@ pub fn run_command(
193
250
  }
194
251
  }
195
252
 
253
+ // One fn with cfg blocks rather than two cfg'd fns: cargo-mutants mutates
254
+ // the uncompiled twin too, and no test on a Linux gate can kill that mutant.
255
+ fn push_args(cmd: &mut Command, args: &[String]) {
256
+ #[cfg(windows)]
257
+ {
258
+ use std::os::windows::process::CommandExt;
259
+ for arg in args {
260
+ cmd.raw_arg(quote_windows_arg(arg));
261
+ }
262
+ }
263
+ #[cfg(not(windows))]
264
+ cmd.args(args);
265
+ }
266
+
196
267
  fn spawn_error(program: &str, source: std::io::Error, cwd: &Path) -> CommandError {
197
268
  // A missing cwd fails the child's chdir with `NotFound` too.
198
269
  if !searched_path(program, source.kind()) || !cwd.is_dir() {
@@ -244,6 +315,35 @@ pub(crate) fn build_argv(
244
315
  Ok(argv)
245
316
  }
246
317
 
318
+ /// Quote `arg` for a Windows command line by the MS C-runtime rules, always
319
+ /// wrapped in double quotes: an MSYS/Cygwin child globs, brace-expands and
320
+ /// unescapes any unquoted word, while an MSVCRT child parses the quoted form
321
+ /// back to `arg` exactly.
322
+ #[cfg_attr(not(windows), allow(dead_code))]
323
+ pub(crate) fn quote_windows_arg(arg: &str) -> String {
324
+ let mut out = String::with_capacity(arg.len() + 2);
325
+ out.push('"');
326
+ let mut backslashes = 0;
327
+ for ch in arg.chars() {
328
+ match ch {
329
+ '\\' => backslashes += 1,
330
+ '"' => {
331
+ out.extend(std::iter::repeat_n('\\', backslashes * 2 + 1));
332
+ out.push('"');
333
+ backslashes = 0;
334
+ }
335
+ _ => {
336
+ out.extend(std::iter::repeat_n('\\', backslashes));
337
+ out.push(ch);
338
+ backslashes = 0;
339
+ }
340
+ }
341
+ }
342
+ out.extend(std::iter::repeat_n('\\', backslashes * 2));
343
+ out.push('"');
344
+ out
345
+ }
346
+
247
347
  /// Replace every `{name}` in `token` with its placeholder value in a single
248
348
  /// left-to-right pass. Injected values are never re-scanned, so an untrusted
249
349
  /// value containing `{…}` is inert. Unknown `{…}` sequences are left literal.
@@ -315,6 +415,117 @@ mod tests {
315
415
  build_argv(command, placeholders).expect("valid command")
316
416
  }
317
417
 
418
+ fn unchanged(path: &str) {
419
+ assert_eq!(non_verbatim(path), path);
420
+ }
421
+
422
+ #[test]
423
+ fn non_verbatim_strips_a_verbatim_drive_path() {
424
+ assert_eq!(
425
+ non_verbatim(r"\\?\C:\Users\runner\in_cwd.txt"),
426
+ r"C:\Users\runner\in_cwd.txt"
427
+ );
428
+ assert_eq!(non_verbatim(r"\\?\d:\x"), r"d:\x");
429
+ assert_eq!(non_verbatim(r"\\?\C:\my file.txt"), r"C:\my file.txt");
430
+ }
431
+
432
+ #[test]
433
+ fn non_verbatim_strips_a_verbatim_drive_root() {
434
+ assert_eq!(non_verbatim(r"\\?\C:\"), r"C:\");
435
+ }
436
+
437
+ #[test]
438
+ fn non_verbatim_rewrites_a_verbatim_unc_path() {
439
+ assert_eq!(
440
+ non_verbatim(r"\\?\UNC\server\share\dir\f.txt"),
441
+ r"\\server\share\dir\f.txt"
442
+ );
443
+ assert_eq!(non_verbatim(r"\\?\UNC\server\share"), r"\\server\share");
444
+ }
445
+
446
+ #[test]
447
+ fn non_verbatim_leaves_plain_paths_unchanged() {
448
+ unchanged(r"C:\Users\runner\f.txt");
449
+ unchanged(r"\\server\share\f.txt");
450
+ unchanged("/tmp/root/f.txt");
451
+ unchanged("relative/f.txt");
452
+ unchanged("");
453
+ }
454
+
455
+ #[test]
456
+ fn non_verbatim_leaves_verbatim_forms_without_a_drive_or_unc_unchanged() {
457
+ unchanged(r"\\?\GLOBALROOT\Device\HarddiskVolume1\f.txt");
458
+ unchanged(r"\\?\Volume{0b1c2d3e-0000-0000-0000-100000000000}\f.txt");
459
+ unchanged(r"\\?\C:");
460
+ unchanged(r"\\?\C:x");
461
+ unchanged(r"\\?\1:\x");
462
+ unchanged(r"\\?\");
463
+ }
464
+
465
+ #[test]
466
+ fn non_verbatim_leaves_an_incomplete_unc_unchanged() {
467
+ unchanged(r"\\?\UNC\");
468
+ unchanged(r"\\?\UNC\server");
469
+ unchanged(r"\\?\UNC\server\");
470
+ }
471
+
472
+ #[test]
473
+ fn non_verbatim_leaves_components_win32_would_reinterpret_unchanged() {
474
+ unchanged(r"\\?\C:\a\..\b");
475
+ unchanged(r"\\?\C:\a\.\b");
476
+ unchanged(r"\\?\C:\a\\b");
477
+ unchanged(r"\\?\C:\a\");
478
+ unchanged(r"\\?\C:\trailing.");
479
+ unchanged(r"\\?\C:\trailing ");
480
+ unchanged(r"\\?\C:\a/b");
481
+ unchanged(r"\\?\C:\a?b");
482
+ unchanged(r"\\?\C:\a*b");
483
+ unchanged(r"\\?\C:\a:b");
484
+ unchanged(r#"\\?\C:\a"b"#);
485
+ unchanged(r"\\?\C:\a<b");
486
+ unchanged(r"\\?\C:\a>b");
487
+ unchanged(r"\\?\C:\a|b");
488
+ unchanged("\\\\?\\C:\\a\u{1}b");
489
+ unchanged(r"\\?\UNC\server\share\..\x");
490
+ }
491
+
492
+ #[test]
493
+ fn non_verbatim_leaves_reserved_device_names_unchanged() {
494
+ for name in [
495
+ "CON", "con", "PRN", "AUX", "NUL", "nul.txt", "NUL .txt", "COM1", "COM9", "LPT1",
496
+ "lpt9.log",
497
+ ] {
498
+ unchanged(&format!(r"\\?\C:\dir\{name}"));
499
+ }
500
+ }
501
+
502
+ #[test]
503
+ fn non_verbatim_strips_names_that_only_resemble_device_names() {
504
+ for name in [
505
+ "CONSOLE", "NULL.txt", "COM0", "COM10", "LPT", "xCON", "AUXa",
506
+ ] {
507
+ assert_eq!(
508
+ non_verbatim(&format!(r"\\?\C:\dir\{name}")),
509
+ format!(r"C:\dir\{name}")
510
+ );
511
+ }
512
+ }
513
+
514
+ #[test]
515
+ fn non_verbatim_strips_up_to_max_path_and_no_further() {
516
+ let fits = format!(r"C:\{}", "a".repeat(256));
517
+ assert_eq!(fits.encode_utf16().count(), 259);
518
+ assert_eq!(non_verbatim(&format!(r"\\?\{fits}")), fits);
519
+ unchanged(&format!(r"\\?\C:\{}", "a".repeat(257)));
520
+ }
521
+
522
+ #[test]
523
+ fn path_placeholder_carries_the_non_verbatim_value() {
524
+ let placeholder = Placeholder::path("path", r"\\?\C:\a\f.txt");
525
+ assert_eq!(placeholder.name, "path");
526
+ assert_eq!(placeholder.value, r"C:\a\f.txt");
527
+ }
528
+
318
529
  #[test]
319
530
  fn splits_on_whitespace_without_placeholders() {
320
531
  assert_eq!(argv("echo hello world", &[]), ["echo", "hello", "world"]);
@@ -402,6 +613,47 @@ mod tests {
402
613
  assert_eq!(argv("run", &[Placeholder::new("path", "/tmp/x")]), ["run"]);
403
614
  }
404
615
 
616
+ #[test]
617
+ fn windows_quoting_wraps_a_plain_arg_in_quotes() {
618
+ assert_eq!(quote_windows_arg("hello"), r#""hello""#);
619
+ }
620
+
621
+ #[test]
622
+ fn windows_quoting_keeps_an_empty_arg_as_an_empty_quoted_word() {
623
+ assert_eq!(quote_windows_arg(""), r#""""#);
624
+ }
625
+
626
+ #[test]
627
+ fn windows_quoting_quotes_glob_and_brace_characters() {
628
+ assert_eq!(quote_windows_arg("[{}]"), r#""[{}]""#);
629
+ assert_eq!(quote_windows_arg("a?b*c"), r#""a?b*c""#);
630
+ }
631
+
632
+ #[test]
633
+ fn windows_quoting_leaves_backslashes_not_before_a_quote_alone() {
634
+ assert_eq!(
635
+ quote_windows_arg(r"\\?\C:\Users\x.txt"),
636
+ r#""\\?\C:\Users\x.txt""#
637
+ );
638
+ }
639
+
640
+ #[test]
641
+ fn windows_quoting_doubles_trailing_backslashes_before_the_closing_quote() {
642
+ assert_eq!(quote_windows_arg(r"C:\dir\"), r#""C:\dir\\""#);
643
+ assert_eq!(quote_windows_arg(r"a\\"), r#""a\\\\""#);
644
+ }
645
+
646
+ #[test]
647
+ fn windows_quoting_escapes_embedded_quotes() {
648
+ assert_eq!(quote_windows_arg(r#"[{"n":1}]"#), r#""[{\"n\":1}]""#);
649
+ }
650
+
651
+ #[test]
652
+ fn windows_quoting_doubles_backslashes_before_an_embedded_quote() {
653
+ assert_eq!(quote_windows_arg(r#"a\"b"#), r#""a\\\"b""#);
654
+ assert_eq!(quote_windows_arg(r#"a\\"b"#), r#""a\\\\\"b""#);
655
+ }
656
+
405
657
  #[test]
406
658
  fn extract_payload_takes_the_last_non_empty_line() {
407
659
  assert_eq!(
@@ -512,7 +512,11 @@ mod tests {
512
512
  );
513
513
  assert_eq!(
514
514
  err.to_string(),
515
- "multiple loadable extension files found in package 'vec': /pkg/a.so, /pkg/z.so; disambiguate with a literal path"
515
+ format!(
516
+ "multiple loadable extension files found in package 'vec': {}, {}; disambiguate with a literal path",
517
+ dir.join("a.so").display(),
518
+ dir.join("z.so").display()
519
+ )
516
520
  );
517
521
  }
518
522
  }
@@ -1938,8 +1938,8 @@ fn run_on_file(
1938
1938
  root: &Path,
1939
1939
  ) -> std::result::Result<Vec<Row>, BoxError> {
1940
1940
  let placeholders = [
1941
- Placeholder::new("path", abs_path),
1942
- Placeholder::new("root", root.to_string_lossy().into_owned()),
1941
+ Placeholder::path("path", abs_path),
1942
+ Placeholder::path("root", &root.to_string_lossy()),
1943
1943
  ];
1944
1944
 
1945
1945
  // Errors travel to the caller rather than being logged and flattened to an
@@ -2936,7 +2936,12 @@ mod internal_tests {
2936
2936
  /// so `notify` never sees `.`.
2937
2937
  #[test]
2938
2938
  fn relative_root_canonicalizes_watch_root_only() {
2939
- let fake = FakeFs::default().with_canonical_root(".", "/ws/canonical");
2939
+ let canonical = if cfg!(windows) {
2940
+ r"C:\ws\canonical"
2941
+ } else {
2942
+ "/ws/canonical"
2943
+ };
2944
+ let fake = FakeFs::default().with_canonical_root(".", canonical);
2940
2945
  let db = DirSQL::with_ignore_and_fs(
2941
2946
  ".",
2942
2947
  vec![Table::new(
@@ -2956,7 +2961,7 @@ mod internal_tests {
2956
2961
  "watch_root must be absolute, got {:?}",
2957
2962
  db.inner.watch_root
2958
2963
  );
2959
- assert_eq!(db.inner.watch_root, PathBuf::from("/ws/canonical"));
2964
+ assert_eq!(db.inner.watch_root, PathBuf::from(canonical));
2960
2965
  }
2961
2966
 
2962
2967
  /// With an absolute root, `process_file_event` strips the `watch_root`
@@ -4376,6 +4381,22 @@ mod internal_tests {
4376
4381
  );
4377
4382
  }
4378
4383
 
4384
+ #[test]
4385
+ fn run_on_file_hands_the_hook_non_verbatim_path_and_root() {
4386
+ let dir = TempDir::new().unwrap();
4387
+ run_on_file(
4388
+ r#"sh -c 'printf "%s|%s" "$1" "$2" > seen; echo "[]"' sh {path} {root}"#,
4389
+ r"\\?\C:\r\f.txt",
4390
+ dir.path(),
4391
+ Path::new(r"\\?\D:\r"),
4392
+ )
4393
+ .expect("a well-formed payload parses");
4394
+ assert_eq!(
4395
+ std::fs::read_to_string(dir.path().join("seen")).unwrap(),
4396
+ r"C:\r\f.txt|D:\r"
4397
+ );
4398
+ }
4399
+
4379
4400
  /// A command that cannot be spawned is an error the caller records, not an
4380
4401
  /// empty row set: a skip and a file that legitimately produced no rows must
4381
4402
  /// stay distinguishable.
@@ -292,8 +292,8 @@ fn no_rows_message(pattern: &str) -> String {
292
292
  fn run_parser(command: &str, root: &Path, rel_path: &Path) -> std::result::Result<String, String> {
293
293
  let abs_path = root.join(rel_path);
294
294
  let placeholders = [
295
- Placeholder::new("path", abs_path.to_string_lossy().into_owned()),
296
- Placeholder::new("root", root.to_string_lossy().into_owned()),
295
+ Placeholder::path("path", &abs_path.to_string_lossy()),
296
+ Placeholder::path("root", &root.to_string_lossy()),
297
297
  ];
298
298
 
299
299
  run_command(command, &placeholders, root, None)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes