dirsql 0.3.24__tar.gz → 0.3.25__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 (167) hide show
  1. {dirsql-0.3.24 → dirsql-0.3.25}/Cargo.lock +1 -1
  2. {dirsql-0.3.24 → dirsql-0.3.25}/PKG-INFO +1 -1
  3. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/Cargo.toml +1 -1
  4. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/config.rs +0 -17
  5. dirsql-0.3.25/packages/rust/src/scanner.rs +83 -0
  6. dirsql-0.3.25/packages/rust/tests/config.rs +26 -0
  7. dirsql-0.3.25/packages/rust/tests/scanner.rs +97 -0
  8. dirsql-0.3.24/packages/rust/src/scanner.rs +0 -136
  9. {dirsql-0.3.24 → dirsql-0.3.25}/Cargo.toml +0 -0
  10. {dirsql-0.3.24 → dirsql-0.3.25}/README.md +0 -0
  11. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/__init__.py +0 -0
  12. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/_async.py +0 -0
  13. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/_async_test.py +0 -0
  14. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/_dirsql.pyi +0 -0
  15. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/__init__.py +0 -0
  16. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/binary_path.py +0 -0
  17. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/binary_path_test.py +0 -0
  18. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/interpret/__init__.py +0 -0
  19. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/interpret/dispatch_extract.py +0 -0
  20. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/interpret/dispatch_extract_test.py +0 -0
  21. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/interpret/load_app.py +0 -0
  22. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/interpret/load_app_test.py +0 -0
  23. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/interpret/run.py +0 -0
  24. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/interpret/run_test.py +0 -0
  25. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/interpret/write_message.py +0 -0
  26. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/interpret/write_message_test.py +0 -0
  27. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/is_windows.py +0 -0
  28. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/is_windows_test.py +0 -0
  29. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/main.py +0 -0
  30. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/cli/main_test.py +0 -0
  31. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/py.typed +0 -0
  32. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/resolve_config.py +0 -0
  33. {dirsql-0.3.24 → dirsql-0.3.25}/dirsql/resolve_config_test.py +0 -0
  34. {dirsql-0.3.24 → dirsql-0.3.25}/docs/.claude/CLAUDE.md +0 -0
  35. {dirsql-0.3.24 → dirsql-0.3.25}/docs/.vitepress/config.ts +0 -0
  36. {dirsql-0.3.24 → dirsql-0.3.25}/docs/.vitepress/theme/index.ts +0 -0
  37. {dirsql-0.3.24 → dirsql-0.3.25}/docs/.vitepress/theme/lang.ts +0 -0
  38. {dirsql-0.3.24 → dirsql-0.3.25}/docs/AGENTS.md +0 -0
  39. {dirsql-0.3.24 → dirsql-0.3.25}/docs/api/index.md +0 -0
  40. {dirsql-0.3.24 → dirsql-0.3.25}/docs/cli/config.md +0 -0
  41. {dirsql-0.3.24 → dirsql-0.3.25}/docs/cli/http-api.md +0 -0
  42. {dirsql-0.3.24 → dirsql-0.3.25}/docs/cli/index.md +0 -0
  43. {dirsql-0.3.24 → dirsql-0.3.25}/docs/cli/init.md +0 -0
  44. {dirsql-0.3.24 → dirsql-0.3.25}/docs/cli/server.md +0 -0
  45. {dirsql-0.3.24 → dirsql-0.3.25}/docs/getting-started.md +0 -0
  46. {dirsql-0.3.24 → dirsql-0.3.25}/docs/guide/async.md +0 -0
  47. {dirsql-0.3.24 → dirsql-0.3.25}/docs/guide/crdt.md +0 -0
  48. {dirsql-0.3.24 → dirsql-0.3.25}/docs/guide/persistence.md +0 -0
  49. {dirsql-0.3.24 → dirsql-0.3.25}/docs/guide/querying.md +0 -0
  50. {dirsql-0.3.24 → dirsql-0.3.25}/docs/guide/tables.md +0 -0
  51. {dirsql-0.3.24 → dirsql-0.3.25}/docs/guide/watching.md +0 -0
  52. {dirsql-0.3.24 → dirsql-0.3.25}/docs/index.md +0 -0
  53. {dirsql-0.3.24 → dirsql-0.3.25}/docs/migrations.md +0 -0
  54. {dirsql-0.3.24 → dirsql-0.3.25}/docs/package.json +0 -0
  55. {dirsql-0.3.24 → dirsql-0.3.25}/docs/playwright.config.ts +0 -0
  56. {dirsql-0.3.24 → dirsql-0.3.25}/docs/pnpm-lock.yaml +0 -0
  57. {dirsql-0.3.24 → dirsql-0.3.25}/docs/pnpm-workspace.yaml +0 -0
  58. {dirsql-0.3.24 → dirsql-0.3.25}/docs/tests/integration/home.spec.ts +0 -0
  59. {dirsql-0.3.24 → dirsql-0.3.25}/docs/tests/integration/language-flag.spec.ts +0 -0
  60. {dirsql-0.3.24 → dirsql-0.3.25}/docs/tests/unit/config.test.ts +0 -0
  61. {dirsql-0.3.24 → dirsql-0.3.25}/docs/tests/unit/lang.test.ts +0 -0
  62. {dirsql-0.3.24 → dirsql-0.3.25}/docs/vitest.config.ts +0 -0
  63. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/README.md +0 -0
  64. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/conftest.py +0 -0
  65. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/.claude/CLAUDE.md +0 -0
  66. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/.vitepress/config.ts +0 -0
  67. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  68. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  69. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/AGENTS.md +0 -0
  70. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/api/index.md +0 -0
  71. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/cli/config.md +0 -0
  72. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/cli/http-api.md +0 -0
  73. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/cli/index.md +0 -0
  74. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/cli/init.md +0 -0
  75. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/cli/server.md +0 -0
  76. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/getting-started.md +0 -0
  77. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/guide/async.md +0 -0
  78. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/guide/crdt.md +0 -0
  79. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/guide/persistence.md +0 -0
  80. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/guide/querying.md +0 -0
  81. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/guide/tables.md +0 -0
  82. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/guide/watching.md +0 -0
  83. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/index.md +0 -0
  84. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/migrations.md +0 -0
  85. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/package.json +0 -0
  86. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/playwright.config.ts +0 -0
  87. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/pnpm-lock.yaml +0 -0
  88. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/pnpm-workspace.yaml +0 -0
  89. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  90. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  91. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/tests/unit/config.test.ts +0 -0
  92. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  93. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/docs/vitest.config.ts +0 -0
  94. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/src/lib.rs +0 -0
  95. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/__init__.py +0 -0
  96. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/conftest.py +0 -0
  97. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/e2e/__init__.py +0 -0
  98. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/__fixtures__/data/a/meta.json +0 -0
  99. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/__fixtures__/data/b/meta.json +0 -0
  100. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/__fixtures__/dirsql.config.py +0 -0
  101. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/__fixtures__/interpret/data/a/meta.json +0 -0
  102. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/__fixtures__/interpret/data/b/meta.json +0 -0
  103. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/__fixtures__/interpret/dirsql.config.py +0 -0
  104. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/__fixtures__/interpret/dirsql.config_no_app.py +0 -0
  105. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/__fixtures__/interpret/dirsql.config_raises.py +0 -0
  106. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/__init__.py +0 -0
  107. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/interpret_subprocess.py +0 -0
  108. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_async_dirsql.py +0 -0
  109. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_binding.py +0 -0
  110. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_dirsql.py +0 -0
  111. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_docs_examples.py +0 -0
  112. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_docs_gaps.py +0 -0
  113. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_from_config.py +0 -0
  114. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_interpret.py +0 -0
  115. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_native_config.py +0 -0
  116. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_persist.py +0 -0
  117. {dirsql-0.3.24 → dirsql-0.3.25}/packages/python/tests/integration/test_serialization.py +0 -0
  118. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/Cargo.toml +0 -0
  119. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/README.md +0 -0
  120. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/benches/db_bench.rs +0 -0
  121. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/benches/differ_bench.rs +0 -0
  122. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/benches/matcher_bench.rs +0 -0
  123. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/benches/scanner_bench.rs +0 -0
  124. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/api/index.md +0 -0
  125. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/cli/config.md +0 -0
  126. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/cli/http-api.md +0 -0
  127. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/cli/index.md +0 -0
  128. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/cli/init.md +0 -0
  129. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/cli/server.md +0 -0
  130. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/getting-started.md +0 -0
  131. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/guide/async.md +0 -0
  132. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/guide/crdt.md +0 -0
  133. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/guide/persistence.md +0 -0
  134. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/guide/querying.md +0 -0
  135. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/guide/tables.md +0 -0
  136. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/guide/watching.md +0 -0
  137. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/index.md +0 -0
  138. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/docs/migrations.md +0 -0
  139. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/bin/dirsql.rs +0 -0
  140. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/cli/init.rs +0 -0
  141. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/cli/mod.rs +0 -0
  142. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/cli/native_config.rs +0 -0
  143. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/cli/router.rs +0 -0
  144. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/cli/serialize.rs +0 -0
  145. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/cli/server.rs +0 -0
  146. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/db.rs +0 -0
  147. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/differ.rs +0 -0
  148. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/lib.rs +0 -0
  149. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/matcher.rs +0 -0
  150. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/persist.rs +0 -0
  151. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/src/watcher.rs +0 -0
  152. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/async_sdk.rs +0 -0
  153. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/cli_e2e.rs +0 -0
  154. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/cli_integration.rs +0 -0
  155. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/code_review_findings.rs +0 -0
  156. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/docs_examples.rs +0 -0
  157. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/docs_gaps.rs +0 -0
  158. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/extensions.rs +0 -0
  159. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/from_config.rs +0 -0
  160. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/init_e2e.rs +0 -0
  161. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/init_integration.rs +0 -0
  162. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/persist.rs +0 -0
  163. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/readonly_query.rs +0 -0
  164. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/sdk.rs +0 -0
  165. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/serialization.rs +0 -0
  166. {dirsql-0.3.24 → dirsql-0.3.25}/packages/rust/tests/watcher.rs +0 -0
  167. {dirsql-0.3.24 → dirsql-0.3.25}/pyproject.toml +0 -0
@@ -499,7 +499,7 @@ dependencies = [
499
499
 
500
500
  [[package]]
501
501
  name = "dirsql-py-ext"
502
- version = "0.3.24"
502
+ version = "0.3.25"
503
503
  dependencies = [
504
504
  "dirsql",
505
505
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.24
3
+ Version: 0.3.25
4
4
  Requires-Dist: pytest>=8 ; extra == 'dev'
5
5
  Requires-Dist: pytest-describe>=2 ; extra == 'dev'
6
6
  Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
@@ -4,7 +4,7 @@ name = "dirsql-py-ext"
4
4
  # pypi/maturin handler can rewrite it via `write-version` before
5
5
  # `maturin build`. `pyproject.toml` declares `dynamic = ["version"]`
6
6
  # and maturin reads this field. Mirrors `packages/rust/Cargo.toml`.
7
- version = "0.3.24"
7
+ version = "0.3.25"
8
8
  edition.workspace = true
9
9
  publish = false
10
10
  readme = "README.md"
@@ -256,23 +256,6 @@ glob = "*.json"
256
256
  assert!(config.ignore.is_empty());
257
257
  }
258
258
 
259
- #[test]
260
- fn load_config_from_file() {
261
- let dir = tempfile::tempdir().unwrap();
262
- let path = dir.path().join(".dirsql.toml");
263
- std::fs::write(
264
- &path,
265
- r#"
266
- [[table]]
267
- ddl = "CREATE TABLE t (_path TEXT)"
268
- glob = "*.csv"
269
- "#,
270
- )
271
- .unwrap();
272
- let config = load_config(&path).unwrap();
273
- assert_eq!(config.tables.len(), 1);
274
- }
275
-
276
259
  #[test]
277
260
  fn load_config_missing_file_returns_io_error() {
278
261
  let err = load_config(Path::new("/nonexistent/.dirsql.toml")).unwrap_err();
@@ -0,0 +1,83 @@
1
+ use crate::matcher::TableMatcher;
2
+ use std::path::{Path, PathBuf};
3
+ use walkdir::WalkDir;
4
+
5
+ /// Top-level directory name reserved for `dirsql`'s own metadata (e.g. the
6
+ /// persistent cache database). Always excluded from the scan, regardless of
7
+ /// whether persistence is enabled.
8
+ pub const RESERVED_DIR: &str = ".dirsql";
9
+
10
+ /// Walk a directory tree and return all file paths paired with their matching table name.
11
+ /// Ignored paths and directories are skipped. Only files (not directories) are returned.
12
+ ///
13
+ /// The top-level `.dirsql/` directory is unconditionally excluded.
14
+ pub fn scan_directory(root: &Path, matcher: &TableMatcher) -> Vec<(PathBuf, String)> {
15
+ let mut results = Vec::new();
16
+
17
+ let walker = WalkDir::new(root).into_iter().filter_entry(|entry| {
18
+ // Skip the reserved `.dirsql/` directory at the top level.
19
+ !is_reserved_dir(entry.depth(), entry.file_type().is_dir(), entry.file_name())
20
+ });
21
+
22
+ for entry in walker.filter_map(|e| e.ok()) {
23
+ let path = entry.path();
24
+
25
+ // Match against relative path so globs like "comments/**/*.jsonl" work
26
+ // regardless of the absolute root directory.
27
+ let rel_path = path.strip_prefix(root).unwrap_or(path);
28
+
29
+ if matcher.is_ignored(rel_path) {
30
+ continue;
31
+ }
32
+
33
+ if !entry.file_type().is_file() {
34
+ continue;
35
+ }
36
+
37
+ if let Some(table_name) = matcher.match_file(rel_path) {
38
+ results.push((path.to_path_buf(), table_name.to_string()));
39
+ }
40
+ }
41
+
42
+ results
43
+ }
44
+
45
+ /// True for the reserved top-level `.dirsql/` directory (`depth == 1`), which
46
+ /// the scan unconditionally excludes. Factored out as a pure predicate over the
47
+ /// facts `WalkDir` exposes so it can be unit-tested without walking a real tree
48
+ /// -- the directory-walk behavior itself is covered by `tests/scanner.rs`.
49
+ fn is_reserved_dir(depth: usize, is_dir: bool, file_name: &std::ffi::OsStr) -> bool {
50
+ depth == 1 && is_dir && file_name == RESERVED_DIR
51
+ }
52
+
53
+ #[cfg(test)]
54
+ mod tests {
55
+ use super::*;
56
+ use std::ffi::OsStr;
57
+
58
+ // The directory-walk behavior of `scan_directory` (real temp trees, real
59
+ // files) is exercised by `tests/scanner.rs` -- those are integration tests
60
+ // and stay out of this inline unit module, which the `unit lint` isolation
61
+ // rule keeps free of effectful std. What remains here is the pure
62
+ // reserved-dir predicate the walker filters on.
63
+
64
+ #[test]
65
+ fn is_reserved_dir_matches_top_level_dirsql() {
66
+ assert!(is_reserved_dir(1, true, OsStr::new(RESERVED_DIR)));
67
+ }
68
+
69
+ #[test]
70
+ fn is_reserved_dir_rejects_nested_dirsql() {
71
+ // Only the *top-level* `.dirsql/` (depth 1) is reserved; a nested one
72
+ // (e.g. `sub/.dirsql/`) is an ordinary directory.
73
+ assert!(!is_reserved_dir(2, true, OsStr::new(RESERVED_DIR)));
74
+ }
75
+
76
+ #[test]
77
+ fn is_reserved_dir_rejects_files_and_other_names() {
78
+ // A file named `.dirsql` is not the reserved directory ...
79
+ assert!(!is_reserved_dir(1, false, OsStr::new(RESERVED_DIR)));
80
+ // ... and an ordinary top-level directory is not reserved.
81
+ assert!(!is_reserved_dir(1, true, OsStr::new("data")));
82
+ }
83
+ }
@@ -0,0 +1,26 @@
1
+ //! Integration test for `load_config` reading a real `.dirsql.toml` off disk.
2
+ //!
3
+ //! The pure TOML-parsing tests (`load_config_str`) stay inline in `config.rs`;
4
+ //! this exercises the thin file-reading wrapper, which needs a real file
5
+ //! (effectful std), so per the `unit lint` isolation rule it belongs in the
6
+ //! integration tier rather than the inline unit module.
7
+
8
+ use dirsql::config::load_config;
9
+ use tempfile::TempDir;
10
+
11
+ #[test]
12
+ fn load_config_from_file() {
13
+ let dir = TempDir::new().unwrap();
14
+ let path = dir.path().join(".dirsql.toml");
15
+ std::fs::write(
16
+ &path,
17
+ r#"
18
+ [[table]]
19
+ ddl = "CREATE TABLE t (_path TEXT)"
20
+ glob = "*.csv"
21
+ "#,
22
+ )
23
+ .unwrap();
24
+ let config = load_config(&path).unwrap();
25
+ assert_eq!(config.tables.len(), 1);
26
+ }
@@ -0,0 +1,97 @@
1
+ //! Integration tests for `scan_directory`.
2
+ //!
3
+ //! These build real temp directory trees with real files and assert what the
4
+ //! scanner returns end-to-end through its public API. They were moved out of
5
+ //! `scanner.rs`'s inline `#[cfg(test)]` module so that module stays pure (the
6
+ //! `testing-conventions` `unit lint` isolation rule forbids effectful std --
7
+ //! `std::fs`, temp dirs -- in unit tests). The pure reserved-dir predicate
8
+ //! test remains inline next to the function it covers.
9
+
10
+ use std::fs;
11
+
12
+ use dirsql::matcher::TableMatcher;
13
+ use dirsql::scanner::scan_directory;
14
+ use tempfile::TempDir;
15
+
16
+ #[test]
17
+ fn scan_finds_matching_files() {
18
+ let dir = TempDir::new().unwrap();
19
+ fs::write(dir.path().join("data.csv"), "a,b\n1,2").unwrap();
20
+ fs::write(dir.path().join("readme.md"), "# hi").unwrap();
21
+
22
+ let matcher = TableMatcher::new(&[("**/*.csv", "csv_table")], &[]).unwrap();
23
+ let results = scan_directory(dir.path(), &matcher);
24
+
25
+ assert_eq!(results.len(), 1);
26
+ assert!(results[0].0.ends_with("data.csv"));
27
+ assert_eq!(results[0].1, "csv_table");
28
+ }
29
+
30
+ #[test]
31
+ fn scan_skips_ignored_files() {
32
+ let dir = TempDir::new().unwrap();
33
+ fs::write(dir.path().join("data.csv"), "a,b").unwrap();
34
+ fs::write(dir.path().join("data.tmp"), "junk").unwrap();
35
+
36
+ let matcher =
37
+ TableMatcher::new(&[("**/*.csv", "t"), ("**/*.tmp", "t2")], &["**/*.tmp"]).unwrap();
38
+ let results = scan_directory(dir.path(), &matcher);
39
+
40
+ assert_eq!(results.len(), 1);
41
+ assert!(results[0].0.ends_with("data.csv"));
42
+ }
43
+
44
+ #[test]
45
+ fn scan_recurses_into_subdirectories() {
46
+ let dir = TempDir::new().unwrap();
47
+ let sub = dir.path().join("nested").join("deep");
48
+ fs::create_dir_all(&sub).unwrap();
49
+ fs::write(sub.join("events.jsonl"), "{}").unwrap();
50
+
51
+ let matcher = TableMatcher::new(&[("**/*.jsonl", "events")], &[]).unwrap();
52
+ let results = scan_directory(dir.path(), &matcher);
53
+
54
+ assert_eq!(results.len(), 1);
55
+ assert!(results[0].0.ends_with("events.jsonl"));
56
+ assert_eq!(results[0].1, "events");
57
+ }
58
+
59
+ #[test]
60
+ fn scan_returns_empty_for_no_matches() {
61
+ let dir = TempDir::new().unwrap();
62
+ fs::write(dir.path().join("readme.md"), "# hi").unwrap();
63
+
64
+ let matcher = TableMatcher::new(&[("**/*.csv", "t")], &[]).unwrap();
65
+ let results = scan_directory(dir.path(), &matcher);
66
+
67
+ assert!(results.is_empty());
68
+ }
69
+
70
+ #[test]
71
+ fn scan_skips_directories() {
72
+ let dir = TempDir::new().unwrap();
73
+ // Create a directory that matches the glob -- it should not appear in results
74
+ fs::create_dir(dir.path().join("data.csv")).unwrap();
75
+
76
+ let matcher = TableMatcher::new(&[("**/*.csv", "t")], &[]).unwrap();
77
+ let results = scan_directory(dir.path(), &matcher);
78
+
79
+ assert!(results.is_empty());
80
+ }
81
+
82
+ #[test]
83
+ fn scan_excludes_top_level_dirsql_directory() {
84
+ let dir = TempDir::new().unwrap();
85
+ fs::write(dir.path().join("real.csv"), "a,b\n1,2").unwrap();
86
+
87
+ // Files inside the reserved `.dirsql/` directory (e.g. the cache db)
88
+ // must never be picked up by the scanner.
89
+ fs::create_dir(dir.path().join(".dirsql")).unwrap();
90
+ fs::write(dir.path().join(".dirsql").join("cache.csv"), "a,b\n1,2").unwrap();
91
+
92
+ let matcher = TableMatcher::new(&[("**/*.csv", "t")], &[]).unwrap();
93
+ let results = scan_directory(dir.path(), &matcher);
94
+
95
+ assert_eq!(results.len(), 1);
96
+ assert!(results[0].0.ends_with("real.csv"));
97
+ }
@@ -1,136 +0,0 @@
1
- use crate::matcher::TableMatcher;
2
- use std::path::{Path, PathBuf};
3
- use walkdir::WalkDir;
4
-
5
- /// Top-level directory name reserved for `dirsql`'s own metadata (e.g. the
6
- /// persistent cache database). Always excluded from the scan, regardless of
7
- /// whether persistence is enabled.
8
- pub const RESERVED_DIR: &str = ".dirsql";
9
-
10
- /// Walk a directory tree and return all file paths paired with their matching table name.
11
- /// Ignored paths and directories are skipped. Only files (not directories) are returned.
12
- ///
13
- /// The top-level `.dirsql/` directory is unconditionally excluded.
14
- pub fn scan_directory(root: &Path, matcher: &TableMatcher) -> Vec<(PathBuf, String)> {
15
- let mut results = Vec::new();
16
-
17
- let walker = WalkDir::new(root).into_iter().filter_entry(|entry| {
18
- // Skip the reserved `.dirsql/` directory at the top level.
19
- if entry.depth() == 1 && entry.file_type().is_dir() && entry.file_name() == RESERVED_DIR {
20
- return false;
21
- }
22
- true
23
- });
24
-
25
- for entry in walker.filter_map(|e| e.ok()) {
26
- let path = entry.path();
27
-
28
- // Match against relative path so globs like "comments/**/*.jsonl" work
29
- // regardless of the absolute root directory.
30
- let rel_path = path.strip_prefix(root).unwrap_or(path);
31
-
32
- if matcher.is_ignored(rel_path) {
33
- continue;
34
- }
35
-
36
- if !entry.file_type().is_file() {
37
- continue;
38
- }
39
-
40
- if let Some(table_name) = matcher.match_file(rel_path) {
41
- results.push((path.to_path_buf(), table_name.to_string()));
42
- }
43
- }
44
-
45
- results
46
- }
47
-
48
- #[cfg(test)]
49
- mod tests {
50
- use super::*;
51
- use std::fs;
52
- use tempfile::TempDir;
53
-
54
- #[test]
55
- fn scan_finds_matching_files() {
56
- let dir = TempDir::new().unwrap();
57
- fs::write(dir.path().join("data.csv"), "a,b\n1,2").unwrap();
58
- fs::write(dir.path().join("readme.md"), "# hi").unwrap();
59
-
60
- let matcher = TableMatcher::new(&[("**/*.csv", "csv_table")], &[]).unwrap();
61
- let results = scan_directory(dir.path(), &matcher);
62
-
63
- assert_eq!(results.len(), 1);
64
- assert!(results[0].0.ends_with("data.csv"));
65
- assert_eq!(results[0].1, "csv_table");
66
- }
67
-
68
- #[test]
69
- fn scan_skips_ignored_files() {
70
- let dir = TempDir::new().unwrap();
71
- fs::write(dir.path().join("data.csv"), "a,b").unwrap();
72
- fs::write(dir.path().join("data.tmp"), "junk").unwrap();
73
-
74
- let matcher =
75
- TableMatcher::new(&[("**/*.csv", "t"), ("**/*.tmp", "t2")], &["**/*.tmp"]).unwrap();
76
- let results = scan_directory(dir.path(), &matcher);
77
-
78
- assert_eq!(results.len(), 1);
79
- assert!(results[0].0.ends_with("data.csv"));
80
- }
81
-
82
- #[test]
83
- fn scan_recurses_into_subdirectories() {
84
- let dir = TempDir::new().unwrap();
85
- let sub = dir.path().join("nested").join("deep");
86
- fs::create_dir_all(&sub).unwrap();
87
- fs::write(sub.join("events.jsonl"), "{}").unwrap();
88
-
89
- let matcher = TableMatcher::new(&[("**/*.jsonl", "events")], &[]).unwrap();
90
- let results = scan_directory(dir.path(), &matcher);
91
-
92
- assert_eq!(results.len(), 1);
93
- assert!(results[0].0.ends_with("events.jsonl"));
94
- assert_eq!(results[0].1, "events");
95
- }
96
-
97
- #[test]
98
- fn scan_returns_empty_for_no_matches() {
99
- let dir = TempDir::new().unwrap();
100
- fs::write(dir.path().join("readme.md"), "# hi").unwrap();
101
-
102
- let matcher = TableMatcher::new(&[("**/*.csv", "t")], &[]).unwrap();
103
- let results = scan_directory(dir.path(), &matcher);
104
-
105
- assert!(results.is_empty());
106
- }
107
-
108
- #[test]
109
- fn scan_skips_directories() {
110
- let dir = TempDir::new().unwrap();
111
- // Create a directory that matches the glob -- it should not appear in results
112
- fs::create_dir(dir.path().join("data.csv")).unwrap();
113
-
114
- let matcher = TableMatcher::new(&[("**/*.csv", "t")], &[]).unwrap();
115
- let results = scan_directory(dir.path(), &matcher);
116
-
117
- assert!(results.is_empty());
118
- }
119
-
120
- #[test]
121
- fn scan_excludes_top_level_dirsql_directory() {
122
- let dir = TempDir::new().unwrap();
123
- fs::write(dir.path().join("real.csv"), "a,b\n1,2").unwrap();
124
-
125
- // Files inside the reserved `.dirsql/` directory (e.g. the cache db)
126
- // must never be picked up by the scanner.
127
- fs::create_dir(dir.path().join(".dirsql")).unwrap();
128
- fs::write(dir.path().join(".dirsql").join("cache.csv"), "a,b\n1,2").unwrap();
129
-
130
- let matcher = TableMatcher::new(&[("**/*.csv", "t")], &[]).unwrap();
131
- let results = scan_directory(dir.path(), &matcher);
132
-
133
- assert_eq!(results.len(), 1);
134
- assert!(results[0].0.ends_with("real.csv"));
135
- }
136
- }
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes