dirsql 0.4.70__tar.gz → 0.4.71__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 (309) hide show
  1. {dirsql-0.4.70 → dirsql-0.4.71}/Cargo.lock +2 -2
  2. {dirsql-0.4.70 → dirsql-0.4.71}/PKG-INFO +1 -1
  3. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/Cargo.toml +1 -1
  4. dirsql-0.4.71/packages/python/e2e-attestations/claude-1267-posix-classes.json +7 -0
  5. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/Cargo.toml +1 -1
  6. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/lib.rs +1 -0
  7. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/matcher.rs +11 -1
  8. dirsql-0.4.71/packages/rust/src/posix_class.rs +313 -0
  9. {dirsql-0.4.70 → dirsql-0.4.71}/Cargo.toml +0 -0
  10. {dirsql-0.4.70 → dirsql-0.4.71}/README.md +0 -0
  11. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/__init__.py +0 -0
  12. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/_async.py +0 -0
  13. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/_dirsql.pyi +0 -0
  14. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/cli/__init__.py +0 -0
  15. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/cli/discover_plugins/__init__.py +0 -0
  16. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
  17. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
  18. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
  19. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
  20. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
  21. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/cli/main.py +0 -0
  22. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/cli/resolve_config_extensions.py +0 -0
  23. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/plan_entry_path.py +0 -0
  24. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/py.typed +0 -0
  25. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/read_config.py +0 -0
  26. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/resolve_configs_extension_specs.py +0 -0
  27. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/resolve_extension.py +0 -0
  28. {dirsql-0.4.70 → dirsql-0.4.71}/dirsql/resolve_package.py +0 -0
  29. {dirsql-0.4.70 → dirsql-0.4.71}/docs/.claude/CLAUDE.md +0 -0
  30. {dirsql-0.4.70 → dirsql-0.4.71}/docs/.vitepress/config.ts +0 -0
  31. {dirsql-0.4.70 → dirsql-0.4.71}/docs/.vitepress/theme/index.ts +0 -0
  32. {dirsql-0.4.70 → dirsql-0.4.71}/docs/.vitepress/theme/lang.ts +0 -0
  33. {dirsql-0.4.70 → dirsql-0.4.71}/docs/AGENTS.md +0 -0
  34. {dirsql-0.4.70 → dirsql-0.4.71}/docs/context/.gitattributes +0 -0
  35. {dirsql-0.4.70 → dirsql-0.4.71}/docs/context/recipes.md +0 -0
  36. {dirsql-0.4.70 → dirsql-0.4.71}/docs/context/troubleshooting.md +0 -0
  37. {dirsql-0.4.70 → dirsql-0.4.71}/docs/context/usage.md +0 -0
  38. {dirsql-0.4.70 → dirsql-0.4.71}/docs/explanation.md +0 -0
  39. {dirsql-0.4.70 → dirsql-0.4.71}/docs/getting-started.md +0 -0
  40. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/columns-from-paths.md +0 -0
  41. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/define-tables.md +0 -0
  42. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/embed.md +0 -0
  43. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/extract-from-contents.md +0 -0
  44. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/load-extension.md +0 -0
  45. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/parse-files-into-columns.md +0 -0
  46. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/persist.md +0 -0
  47. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/query-json.md +0 -0
  48. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/query-without-config.md +0 -0
  49. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/react-to-changes.md +0 -0
  50. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/search-by-meaning.md +0 -0
  51. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/search-indexes.md +0 -0
  52. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/skip-files.md +0 -0
  53. {dirsql-0.4.70 → dirsql-0.4.71}/docs/howto/write-a-plugin.md +0 -0
  54. {dirsql-0.4.70 → dirsql-0.4.71}/docs/index.md +0 -0
  55. {dirsql-0.4.70 → dirsql-0.4.71}/docs/package.json +0 -0
  56. {dirsql-0.4.70 → dirsql-0.4.71}/docs/plugins.md +0 -0
  57. {dirsql-0.4.70 → dirsql-0.4.71}/docs/pnpm-lock.yaml +0 -0
  58. {dirsql-0.4.70 → dirsql-0.4.71}/docs/pnpm-workspace.yaml +0 -0
  59. {dirsql-0.4.70 → dirsql-0.4.71}/docs/reference/cli.md +0 -0
  60. {dirsql-0.4.70 → dirsql-0.4.71}/docs/reference/columns.md +0 -0
  61. {dirsql-0.4.70 → dirsql-0.4.71}/docs/reference/config.md +0 -0
  62. {dirsql-0.4.70 → dirsql-0.4.71}/docs/reference/hooks.md +0 -0
  63. {dirsql-0.4.70 → dirsql-0.4.71}/docs/reference/http-api.md +0 -0
  64. {dirsql-0.4.70 → dirsql-0.4.71}/docs/reference/path-tables.md +0 -0
  65. {dirsql-0.4.70 → dirsql-0.4.71}/docs/reference/sdk.md +0 -0
  66. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/CHANGELOG.md +0 -0
  67. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/MIGRATIONS.md +0 -0
  68. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/README.md +0 -0
  69. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  70. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
  71. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  72. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
  73. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  74. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
  75. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
  76. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
  77. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  78. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
  79. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
  80. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
  81. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
  82. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
  83. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
  84. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
  85. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
  86. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
  87. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
  88. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-04-config-paths-from-argv-module.md +0 -0
  89. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-05-config-extension-loader-split.md +0 -0
  90. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-05-resolve-config-extensions-split.md +0 -0
  91. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-05-resolve-extension-split.md +0 -0
  92. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md +0 -0
  93. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-17-extension-resolution-via-core.md +0 -0
  94. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-18-export-scan-failure.md +0 -0
  95. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-18-flatten-row-event-in-core.md +0 -0
  96. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-18-programmatic-extension-plan.md +0 -0
  97. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  98. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  99. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-02-readme-docs-host.md +0 -0
  100. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
  101. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-03-path-table-directory-one-level.md +0 -0
  102. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
  103. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-05-cli-context.md +0 -0
  104. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-05-gitignore-requires-repo.md +0 -0
  105. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-05-path-table-node-modules-named.md +0 -0
  106. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-05-path-table-symlinks.md +0 -0
  107. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/2026-10-05-path-table-trailing-slash.md +0 -0
  108. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/changelog.d/README.md +0 -0
  109. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/conftest.py +0 -0
  110. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/.claude/CLAUDE.md +0 -0
  111. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/.vitepress/config.ts +0 -0
  112. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  113. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  114. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/AGENTS.md +0 -0
  115. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/context/.gitattributes +0 -0
  116. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/context/recipes.md +0 -0
  117. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/context/troubleshooting.md +0 -0
  118. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/context/usage.md +0 -0
  119. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/explanation.md +0 -0
  120. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/getting-started.md +0 -0
  121. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/columns-from-paths.md +0 -0
  122. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/define-tables.md +0 -0
  123. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/embed.md +0 -0
  124. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/extract-from-contents.md +0 -0
  125. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/load-extension.md +0 -0
  126. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
  127. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/persist.md +0 -0
  128. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/query-json.md +0 -0
  129. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/query-without-config.md +0 -0
  130. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/react-to-changes.md +0 -0
  131. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/search-by-meaning.md +0 -0
  132. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/search-indexes.md +0 -0
  133. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/skip-files.md +0 -0
  134. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/howto/write-a-plugin.md +0 -0
  135. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/index.md +0 -0
  136. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/package.json +0 -0
  137. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/plugins.md +0 -0
  138. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/pnpm-lock.yaml +0 -0
  139. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/pnpm-workspace.yaml +0 -0
  140. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/reference/cli.md +0 -0
  141. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/reference/columns.md +0 -0
  142. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/reference/config.md +0 -0
  143. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/reference/hooks.md +0 -0
  144. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/reference/http-api.md +0 -0
  145. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/reference/path-tables.md +0 -0
  146. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/docs/reference/sdk.md +0 -0
  147. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
  148. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1056-py-ofpf9.json +0 -0
  149. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1066-p1-resolve-extension.json +0 -0
  150. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1078-argv-config-paths-core.json +0 -0
  151. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1079-p2-resolve-config-extensions.json +0 -0
  152. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1080-share-vtab-scaffolding.json +0 -0
  153. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1081-indicatif-progress.json +0 -0
  154. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1096-watch-moved-dir.json +0 -0
  155. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1109-watch-moved-out.json +0 -0
  156. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1119-ext-plan.json +0 -0
  157. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1120-py-ext.json +0 -0
  158. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1122-ext-cleanup.json +0 -0
  159. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1130-codec-crates.json +0 -0
  160. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1131-flatten-row-event.json +0 -0
  161. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1139-sse-flatten.json +0 -0
  162. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1140-scan-failure-export.json +0 -0
  163. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1148-clippy-all-targets.json +0 -0
  164. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1164-sigint-fatal.json +0 -0
  165. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1170-projection-order.json +0 -0
  166. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1172-on-file-path.json +0 -0
  167. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1173-py-projection-order.json +0 -0
  168. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1184-win-rel-paths.json +0 -0
  169. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1185-win-abs-table-names.json +0 -0
  170. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1186-win-test-harness.json +0 -0
  171. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1196-join-index.json +0 -0
  172. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1199-glob-prefix.json +0 -0
  173. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1201-single-walk.json +0 -0
  174. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1204-embed-batch.json +0 -0
  175. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1207-win-argv-quote.json +0 -0
  176. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1208-win-nonverbatim-paths.json +0 -0
  177. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1215-on-file-batch.json +0 -0
  178. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1216-on-file-cleanup.json +0 -0
  179. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1223-config-glob-rule.json +0 -0
  180. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1223-dir-one-level.json +0 -0
  181. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1226-stale-docs.json +0 -0
  182. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1238-parallel-walk.json +0 -0
  183. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1241-content-prefetch.json +0 -0
  184. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1243-ingest-perf.json +0 -0
  185. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1249-dotfiles.json +0 -0
  186. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1256-symlinks.json +0 -0
  187. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1257-trailing-slash.json +0 -0
  188. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1264-context.json +0 -0
  189. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1269-utf8-chars.json +0 -0
  190. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1271-node-modules-named.json +0 -0
  191. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1272-ancestor-gitignore.json +0 -0
  192. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1273-walk-prune.json +0 -0
  193. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-1285-ingest-fast.json +0 -0
  194. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
  195. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
  196. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
  197. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
  198. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
  199. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
  200. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
  201. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
  202. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
  203. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-1034-cachesplit.json +0 -0
  204. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-1042-py12.json +0 -0
  205. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
  206. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6-worker-calls.json +0 -0
  207. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +0 -0
  208. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +0 -0
  209. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  210. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  211. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  212. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
  213. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
  214. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
  215. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  216. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
  217. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
  218. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
  219. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  220. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  221. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-02-readme-docs-host.md +0 -0
  222. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
  223. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-03-path-table-directory-one-level.md +0 -0
  224. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
  225. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-05-cli-context.md +0 -0
  226. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-05-gitignore-requires-repo.md +0 -0
  227. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-05-path-table-node-modules-named.md +0 -0
  228. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-05-path-table-symlinks.md +0 -0
  229. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/2026-10-05-path-table-trailing-slash.md +0 -0
  230. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/migrations.d/README.md +0 -0
  231. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/src/lib.rs +0 -0
  232. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/testing-conventions.toml +0 -0
  233. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/tests/__init__.py +0 -0
  234. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/tests/conftest.py +0 -0
  235. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/tests/e2e/__init__.py +0 -0
  236. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
  237. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
  238. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/tests/e2e/speed_of_light.py +0 -0
  239. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/tests/integration/__init__.py +0 -0
  240. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/tests/integration/binding/__init__.py +0 -0
  241. {dirsql-0.4.70 → dirsql-0.4.71}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  242. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/CHANGELOG.md +0 -0
  243. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/MIGRATIONS.md +0 -0
  244. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/README.md +0 -0
  245. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/benches/db_bench.rs +0 -0
  246. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/benches/differ_bench.rs +0 -0
  247. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/benches/matcher_bench.rs +0 -0
  248. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/benches/scanner_bench.rs +0 -0
  249. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/context/.gitattributes +0 -0
  250. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/context/recipes.md +0 -0
  251. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/context/troubleshooting.md +0 -0
  252. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/context/usage.md +0 -0
  253. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/explanation.md +0 -0
  254. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/getting-started.md +0 -0
  255. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  256. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/define-tables.md +0 -0
  257. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/embed.md +0 -0
  258. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  259. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/load-extension.md +0 -0
  260. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
  261. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/persist.md +0 -0
  262. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/query-json.md +0 -0
  263. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/query-without-config.md +0 -0
  264. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/react-to-changes.md +0 -0
  265. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  266. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/search-indexes.md +0 -0
  267. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/skip-files.md +0 -0
  268. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/howto/write-a-plugin.md +0 -0
  269. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/index.md +0 -0
  270. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/plugins.md +0 -0
  271. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/reference/cli.md +0 -0
  272. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/reference/columns.md +0 -0
  273. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/reference/config.md +0 -0
  274. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/reference/hooks.md +0 -0
  275. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/reference/http-api.md +0 -0
  276. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/reference/path-tables.md +0 -0
  277. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/docs/reference/sdk.md +0 -0
  278. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/bin/dirsql.rs +0 -0
  279. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/context.rs +0 -0
  280. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/execute.rs +0 -0
  281. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/init.rs +0 -0
  282. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/mod.rs +0 -0
  283. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/repl.rs +0 -0
  284. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/router.rs +0 -0
  285. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/run.rs +0 -0
  286. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/serialize.rs +0 -0
  287. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/server.rs +0 -0
  288. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/cli/table.rs +0 -0
  289. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/command.rs +0 -0
  290. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/config.rs +0 -0
  291. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/db.rs +0 -0
  292. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/default_config.toml +0 -0
  293. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/differ.rs +0 -0
  294. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/extension_resolution.rs +0 -0
  295. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/functions.rs +0 -0
  296. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/infer.rs +0 -0
  297. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/launcher.rs +0 -0
  298. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/on_file.rs +0 -0
  299. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/parsed_vtab.rs +0 -0
  300. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/path_table.rs +0 -0
  301. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/persist.rs +0 -0
  302. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/progress.rs +0 -0
  303. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/row_event_flat.rs +0 -0
  304. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/scanner.rs +0 -0
  305. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/sql_literal.rs +0 -0
  306. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/vtab.rs +0 -0
  307. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/vtab_scaffold.rs +0 -0
  308. {dirsql-0.4.70 → dirsql-0.4.71}/packages/rust/src/watcher.rs +0 -0
  309. {dirsql-0.4.70 → dirsql-0.4.71}/pyproject.toml +0 -0
@@ -552,7 +552,7 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
552
552
 
553
553
  [[package]]
554
554
  name = "dirsql"
555
- version = "0.4.70"
555
+ version = "0.4.71"
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.70"
604
+ version = "0.4.71"
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.70
3
+ Version: 0.4.71
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.70"
7
+ version = "0.4.71"
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": 1791224868,
4
+ "exit_code": 0,
5
+ "commit": "ca8ccbb55b7f51f9373b8f774665b925ba10ac21",
6
+ "branch": "claude/1267-posix-classes"
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.70"
9
+ version = "0.4.71"
10
10
  edition.workspace = true
11
11
  # Literal `license` (not `license.workspace = true`) because
12
12
  # putitoutthere's preflight check reads `[package].license` directly
@@ -33,6 +33,7 @@ pub mod parsed_vtab;
33
33
  pub mod path_table;
34
34
  #[doc(hidden)]
35
35
  pub mod persist;
36
+ mod posix_class;
36
37
  #[doc(hidden)]
37
38
  pub mod progress;
38
39
  #[doc(hidden)]
@@ -1,3 +1,4 @@
1
+ use crate::posix_class::expand_posix_classes;
1
2
  use globset::GlobBuilder;
2
3
  use regex::{Regex, RegexBuilder};
3
4
  use std::path::Path;
@@ -20,7 +21,9 @@ pub(crate) struct Pattern(Regex);
20
21
  impl Pattern {
21
22
  /// `*` and `?` stop at `/`; `**` crosses it.
22
23
  pub(crate) fn new(glob: &str) -> Result<Self, GlobError> {
23
- let glob = GlobBuilder::new(glob).literal_separator(true).build()?;
24
+ let glob = GlobBuilder::new(&expand_posix_classes(glob))
25
+ .literal_separator(true)
26
+ .build()?;
24
27
  let regex = RegexBuilder::new(&unicode_regex(glob.regex()))
25
28
  .dot_matches_new_line(true)
26
29
  .build()?;
@@ -586,4 +589,11 @@ mod tests {
586
589
  .is_empty()
587
590
  );
588
591
  }
592
+
593
+ #[test]
594
+ fn a_posix_class_in_a_table_glob_matches_its_characters() {
595
+ let matcher = TableMatcher::new(&[("notes/[[:digit:]].md", "n")], &[]).unwrap();
596
+ assert_eq!(names(&matcher, "notes/7.md"), vec!["n"]);
597
+ assert!(matcher.match_all(Path::new("notes/x.md")).is_empty());
598
+ }
589
599
  }
@@ -0,0 +1,313 @@
1
+ //! POSIX character classes (`[[:digit:]]`) for globset, which has no
2
+ //! `[:name:]` syntax: each bracket expression holding one is rewritten into an
3
+ //! equivalent bracket of plain ranges before the glob is compiled.
4
+
5
+ /// Rewrite every bracket expression in `pattern` that holds a `[:name:]`
6
+ /// class into plain ranges. Brackets without one, unclosed brackets and
7
+ /// backslash-escaped characters are left exactly as written.
8
+ pub(crate) fn expand_posix_classes(pattern: &str) -> String {
9
+ let chars: Vec<char> = pattern.chars().collect();
10
+ let mut out = String::with_capacity(pattern.len());
11
+ let mut i = 0;
12
+ while i < chars.len() {
13
+ match chars[i] {
14
+ '\\' => {
15
+ let end = (i + 2).min(chars.len());
16
+ out.extend(&chars[i..end]);
17
+ i = end;
18
+ }
19
+ '[' => match bracket(&chars, i) {
20
+ Some((end, Some(rewritten))) => {
21
+ out.push_str(&rewritten);
22
+ i = end;
23
+ }
24
+ Some((end, None)) => {
25
+ out.extend(&chars[i..end]);
26
+ i = end;
27
+ }
28
+ None => {
29
+ out.push('[');
30
+ i += 1;
31
+ }
32
+ },
33
+ c => {
34
+ out.push(c);
35
+ i += 1;
36
+ }
37
+ }
38
+ }
39
+ out
40
+ }
41
+
42
+ /// Parse the bracket expression opening at `start`. `None` when it never
43
+ /// closes; otherwise the index past its `]`, plus its rewrite when it holds a
44
+ /// class.
45
+ fn bracket(chars: &[char], start: usize) -> Option<(usize, Option<String>)> {
46
+ let mut j = start + 1;
47
+ let negated = matches!(chars.get(j), Some('!' | '^'));
48
+ if negated {
49
+ j += 1;
50
+ }
51
+ let body_start = j;
52
+ let mut ranges: Vec<(char, char)> = Vec::new();
53
+ let mut has_class = false;
54
+ // bash matches nothing at all when a range ends in a class, negated or not.
55
+ let mut range_into_class = false;
56
+ loop {
57
+ let c = *chars.get(j)?;
58
+ if c == ']' && j > body_start {
59
+ j += 1;
60
+ break;
61
+ }
62
+ if let Some((name, next)) = class_at(chars, j) {
63
+ ranges.extend_from_slice(class_ranges(&name));
64
+ has_class = true;
65
+ j = next;
66
+ continue;
67
+ }
68
+ match (chars.get(j + 1), chars.get(j + 2)) {
69
+ (Some('-'), Some(&end)) if end != ']' => {
70
+ if let Some((_, next)) = class_at(chars, j + 2) {
71
+ has_class = true;
72
+ range_into_class = true;
73
+ j = next;
74
+ continue;
75
+ }
76
+ if c <= end {
77
+ ranges.push((c, end));
78
+ }
79
+ j += 3;
80
+ }
81
+ _ => {
82
+ ranges.push((c, c));
83
+ j += 1;
84
+ }
85
+ }
86
+ }
87
+ if !has_class {
88
+ return Some((j, None));
89
+ }
90
+ let rewritten = if range_into_class {
91
+ "[\0]".to_string()
92
+ } else {
93
+ render(negated, ranges)
94
+ };
95
+ Some((j, Some(rewritten)))
96
+ }
97
+
98
+ /// The class `[:name:]` starting at `j`, with the index past its `:]`. A `]`
99
+ /// before the `:]` means the `[` is an ordinary character, as in bash.
100
+ fn class_at(chars: &[char], j: usize) -> Option<(String, usize)> {
101
+ if chars.get(j) != Some(&'[') || chars.get(j + 1) != Some(&':') {
102
+ return None;
103
+ }
104
+ let mut k = j + 2;
105
+ loop {
106
+ match (chars.get(k)?, chars.get(k + 1)) {
107
+ (':', Some(']')) => return Some((chars[j + 2..k].iter().collect(), k + 2)),
108
+ (']', _) => return None,
109
+ _ => k += 1,
110
+ }
111
+ }
112
+ }
113
+
114
+ /// The ASCII members of a class; an unknown name has none, as in bash.
115
+ fn class_ranges(name: &str) -> &'static [(char, char)] {
116
+ match name {
117
+ "alnum" => &[('0', '9'), ('A', 'Z'), ('a', 'z')],
118
+ "alpha" => &[('A', 'Z'), ('a', 'z')],
119
+ "ascii" => &[('\0', '\x7f')],
120
+ "blank" => &[(' ', ' '), ('\t', '\t')],
121
+ "cntrl" => &[('\0', '\x1f'), ('\x7f', '\x7f')],
122
+ "digit" => &[('0', '9')],
123
+ "graph" => &[('!', '~')],
124
+ "lower" => &[('a', 'z')],
125
+ "print" => &[(' ', '~')],
126
+ "punct" => &[('!', '/'), (':', '@'), ('[', '`'), ('{', '~')],
127
+ "space" => &[('\t', '\r'), (' ', ' ')],
128
+ "upper" => &[('A', 'Z')],
129
+ "word" => &[('0', '9'), ('A', 'Z'), ('a', 'z'), ('_', '_')],
130
+ "xdigit" => &[('0', '9'), ('A', 'F'), ('a', 'f')],
131
+ _ => &[],
132
+ }
133
+ }
134
+
135
+ /// Characters globset reads as syntax at some position inside a bracket, so
136
+ /// they are pulled out of ranges and placed where they are literal.
137
+ const POSITIONAL: [char; 4] = [']', '-', '!', '^'];
138
+
139
+ /// A globset bracket matching exactly `ranges` (or everything else when
140
+ /// `negated`). A bracket never matches the separator, so `/` is dropped from a
141
+ /// positive set and added to a negated one. NUL, which no path holds, stands
142
+ /// in when nothing else may open the bracket.
143
+ fn render(negated: bool, ranges: Vec<(char, char)>) -> String {
144
+ let mut set: Vec<(u32, u32)> = ranges
145
+ .into_iter()
146
+ .map(|(a, b)| (a as u32, b as u32))
147
+ .collect();
148
+ set = without(set, '/');
149
+ if negated {
150
+ set.push(('/' as u32, '/' as u32));
151
+ }
152
+ let present = |c: char| set.iter().any(|&(a, b)| a <= c as u32 && c as u32 <= b);
153
+ let specials: Vec<char> = POSITIONAL.into_iter().filter(|&c| present(c)).collect();
154
+ let mut plain = POSITIONAL.into_iter().fold(set.clone(), without);
155
+ plain.sort_unstable();
156
+ if plain.is_empty() && !specials.contains(&']') {
157
+ plain.push((0, 0));
158
+ }
159
+
160
+ let mut out = String::from("[");
161
+ if negated {
162
+ out.push('!');
163
+ }
164
+ if specials.contains(&']') {
165
+ out.push(']');
166
+ }
167
+ for (a, b) in plain {
168
+ out.extend(char::from_u32(a));
169
+ if a != b {
170
+ out.push('-');
171
+ out.extend(char::from_u32(b));
172
+ }
173
+ }
174
+ for c in ['!', '^', '-'] {
175
+ if specials.contains(&c) {
176
+ out.push(c);
177
+ }
178
+ }
179
+ out.push(']');
180
+ out
181
+ }
182
+
183
+ /// `set` with `c` removed, splitting any range that spans it. `c` is never
184
+ /// NUL, so `c - 1` cannot underflow.
185
+ fn without(set: Vec<(u32, u32)>, c: char) -> Vec<(u32, u32)> {
186
+ let c = c as u32;
187
+ set.into_iter()
188
+ .flat_map(|(a, b)| [(a, b.min(c - 1)), (a.max(c + 1), b)])
189
+ .filter(|&(a, b)| a <= b)
190
+ .collect()
191
+ }
192
+
193
+ #[cfg(test)]
194
+ mod tests {
195
+ use super::*;
196
+
197
+ #[test]
198
+ fn a_class_becomes_its_ranges() {
199
+ assert_eq!(expand_posix_classes("[[:digit:]].md"), "[0-9].md");
200
+ assert_eq!(expand_posix_classes("[[:upper:]]*"), "[A-Z]*");
201
+ }
202
+
203
+ #[test]
204
+ fn every_named_class_expands() {
205
+ let cases = [
206
+ ("alnum", "[0-9A-Za-z]"),
207
+ ("alpha", "[A-Za-z]"),
208
+ ("ascii", "[]\0- \"-,.0-\\_-\x7f!^-]"),
209
+ ("blank", "[\t ]"),
210
+ ("cntrl", "[\0-\x1f\x7f]"),
211
+ ("digit", "[0-9]"),
212
+ ("graph", "[]\"-,.0-\\_-~!^-]"),
213
+ ("lower", "[a-z]"),
214
+ ("print", "[] \"-,.0-\\_-~!^-]"),
215
+ ("punct", "[]\"-,.:-@[-\\_-`{-~!^-]"),
216
+ ("space", "[\t-\r ]"),
217
+ ("upper", "[A-Z]"),
218
+ ("word", "[0-9A-Z_a-z]"),
219
+ ("xdigit", "[0-9A-Fa-f]"),
220
+ ];
221
+ for (name, want) in cases {
222
+ assert_eq!(
223
+ expand_posix_classes(&format!("[[:{name}:]]")),
224
+ want,
225
+ "{name}"
226
+ );
227
+ }
228
+ }
229
+
230
+ #[test]
231
+ fn a_negated_class_also_excludes_the_separator() {
232
+ assert_eq!(expand_posix_classes("[![:digit:]]"), "[!/0-9]");
233
+ assert_eq!(expand_posix_classes("[^[:digit:]]"), "[!/0-9]");
234
+ }
235
+
236
+ #[test]
237
+ fn classes_and_characters_combine() {
238
+ assert_eq!(expand_posix_classes("[a[:digit:]]"), "[0-9a]");
239
+ assert_eq!(expand_posix_classes("[[:digit:][:upper:]]"), "[0-9A-Z]");
240
+ assert_eq!(expand_posix_classes("[[:digit:]x-z]"), "[0-9x-z]");
241
+ }
242
+
243
+ #[test]
244
+ fn a_dash_after_a_class_is_literal() {
245
+ assert_eq!(expand_posix_classes("[[:digit:]-z]"), "[0-9z-]");
246
+ assert_eq!(expand_posix_classes("[[:digit:]-]"), "[0-9-]");
247
+ assert_eq!(expand_posix_classes("[[:digit:]x-]"), "[0-9x-]");
248
+ }
249
+
250
+ #[test]
251
+ fn a_leading_close_bracket_is_a_member() {
252
+ assert_eq!(expand_posix_classes("[][:digit:]]"), "[]0-9]");
253
+ }
254
+
255
+ #[test]
256
+ fn an_unknown_class_contributes_nothing() {
257
+ assert_eq!(expand_posix_classes("[[:foo:]a]"), "[a]");
258
+ assert_eq!(expand_posix_classes("[[:DIGIT:]]"), "[\0]");
259
+ assert_eq!(expand_posix_classes("[![:foo:]]"), "[!/]");
260
+ }
261
+
262
+ #[test]
263
+ fn a_lone_bang_is_kept_off_the_negation_position() {
264
+ assert_eq!(expand_posix_classes("[[:foo:]!]"), "[\0!]");
265
+ assert_eq!(expand_posix_classes("[[:foo:]^]"), "[\0^]");
266
+ }
267
+
268
+ #[test]
269
+ fn a_range_ending_in_a_class_matches_nothing() {
270
+ assert_eq!(expand_posix_classes("[x-[:digit:]]"), "[\0]");
271
+ assert_eq!(expand_posix_classes("[!x-[:digit:]]"), "[\0]");
272
+ }
273
+
274
+ #[test]
275
+ fn a_reversed_range_beside_a_class_is_empty() {
276
+ assert_eq!(expand_posix_classes("[z-a[:digit:]]"), "[0-9]");
277
+ }
278
+
279
+ #[test]
280
+ fn a_bracket_without_a_class_is_untouched() {
281
+ for pattern in [
282
+ "[a-c]*",
283
+ "[!x].md",
284
+ "[]]",
285
+ "[[:a]",
286
+ "{a,b}/[xy]",
287
+ "[a:digit:]]",
288
+ "[[:a][:digit:]]",
289
+ ] {
290
+ assert_eq!(expand_posix_classes(pattern), pattern);
291
+ }
292
+ }
293
+
294
+ #[test]
295
+ fn an_unclosed_bracket_is_untouched() {
296
+ assert_eq!(expand_posix_classes("[[:digit:]"), "[[:digit:]");
297
+ assert_eq!(expand_posix_classes("a["), "a[");
298
+ }
299
+
300
+ #[test]
301
+ fn an_escaped_bracket_is_untouched() {
302
+ assert_eq!(expand_posix_classes("\\[[:digit:]]"), "\\[[:digit:]]");
303
+ assert_eq!(expand_posix_classes("a\\"), "a\\");
304
+ }
305
+
306
+ #[test]
307
+ fn text_around_brackets_is_kept() {
308
+ assert_eq!(
309
+ expand_posix_classes("**/v[[:digit:]]/*.md"),
310
+ "**/v[0-9]/*.md"
311
+ );
312
+ }
313
+ }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes