dirsql 0.3.32__tar.gz → 0.3.33__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 (130) hide show
  1. {dirsql-0.3.32 → dirsql-0.3.33}/Cargo.lock +1 -1
  2. {dirsql-0.3.32 → dirsql-0.3.33}/PKG-INFO +1 -1
  3. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/Cargo.toml +1 -1
  4. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/lib.rs +75 -41
  5. {dirsql-0.3.32 → dirsql-0.3.33}/Cargo.toml +0 -0
  6. {dirsql-0.3.32 → dirsql-0.3.33}/README.md +0 -0
  7. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/__init__.py +0 -0
  8. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/_async.py +0 -0
  9. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/_dirsql.pyi +0 -0
  10. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/cli/__init__.py +0 -0
  11. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/cli/binary_path.py +0 -0
  12. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/cli/interpret/__init__.py +0 -0
  13. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/cli/interpret/dispatch_extract.py +0 -0
  14. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/cli/interpret/load_app.py +0 -0
  15. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/cli/interpret/run.py +0 -0
  16. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/cli/interpret/write_message.py +0 -0
  17. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/cli/is_windows.py +0 -0
  18. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/cli/main.py +0 -0
  19. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/py.typed +0 -0
  20. {dirsql-0.3.32 → dirsql-0.3.33}/dirsql/resolve_config.py +0 -0
  21. {dirsql-0.3.32 → dirsql-0.3.33}/docs/.claude/CLAUDE.md +0 -0
  22. {dirsql-0.3.32 → dirsql-0.3.33}/docs/.vitepress/config.ts +0 -0
  23. {dirsql-0.3.32 → dirsql-0.3.33}/docs/.vitepress/theme/index.ts +0 -0
  24. {dirsql-0.3.32 → dirsql-0.3.33}/docs/.vitepress/theme/lang.ts +0 -0
  25. {dirsql-0.3.32 → dirsql-0.3.33}/docs/AGENTS.md +0 -0
  26. {dirsql-0.3.32 → dirsql-0.3.33}/docs/api/index.md +0 -0
  27. {dirsql-0.3.32 → dirsql-0.3.33}/docs/cli/config.md +0 -0
  28. {dirsql-0.3.32 → dirsql-0.3.33}/docs/cli/http-api.md +0 -0
  29. {dirsql-0.3.32 → dirsql-0.3.33}/docs/cli/index.md +0 -0
  30. {dirsql-0.3.32 → dirsql-0.3.33}/docs/cli/init.md +0 -0
  31. {dirsql-0.3.32 → dirsql-0.3.33}/docs/cli/server.md +0 -0
  32. {dirsql-0.3.32 → dirsql-0.3.33}/docs/getting-started.md +0 -0
  33. {dirsql-0.3.32 → dirsql-0.3.33}/docs/guide/async.md +0 -0
  34. {dirsql-0.3.32 → dirsql-0.3.33}/docs/guide/crdt.md +0 -0
  35. {dirsql-0.3.32 → dirsql-0.3.33}/docs/guide/persistence.md +0 -0
  36. {dirsql-0.3.32 → dirsql-0.3.33}/docs/guide/querying.md +0 -0
  37. {dirsql-0.3.32 → dirsql-0.3.33}/docs/guide/tables.md +0 -0
  38. {dirsql-0.3.32 → dirsql-0.3.33}/docs/guide/watching.md +0 -0
  39. {dirsql-0.3.32 → dirsql-0.3.33}/docs/index.md +0 -0
  40. {dirsql-0.3.32 → dirsql-0.3.33}/docs/migrations.md +0 -0
  41. {dirsql-0.3.32 → dirsql-0.3.33}/docs/package.json +0 -0
  42. {dirsql-0.3.32 → dirsql-0.3.33}/docs/playwright.config.ts +0 -0
  43. {dirsql-0.3.32 → dirsql-0.3.33}/docs/pnpm-lock.yaml +0 -0
  44. {dirsql-0.3.32 → dirsql-0.3.33}/docs/pnpm-workspace.yaml +0 -0
  45. {dirsql-0.3.32 → dirsql-0.3.33}/docs/tests/integration/home.spec.ts +0 -0
  46. {dirsql-0.3.32 → dirsql-0.3.33}/docs/tests/integration/language-flag.spec.ts +0 -0
  47. {dirsql-0.3.32 → dirsql-0.3.33}/docs/tests/unit/config.test.ts +0 -0
  48. {dirsql-0.3.32 → dirsql-0.3.33}/docs/tests/unit/lang.test.ts +0 -0
  49. {dirsql-0.3.32 → dirsql-0.3.33}/docs/vitest.config.ts +0 -0
  50. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/README.md +0 -0
  51. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/conftest.py +0 -0
  52. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/.claude/CLAUDE.md +0 -0
  53. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/.vitepress/config.ts +0 -0
  54. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  55. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  56. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/AGENTS.md +0 -0
  57. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/api/index.md +0 -0
  58. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/cli/config.md +0 -0
  59. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/cli/http-api.md +0 -0
  60. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/cli/index.md +0 -0
  61. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/cli/init.md +0 -0
  62. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/cli/server.md +0 -0
  63. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/getting-started.md +0 -0
  64. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/guide/async.md +0 -0
  65. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/guide/crdt.md +0 -0
  66. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/guide/persistence.md +0 -0
  67. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/guide/querying.md +0 -0
  68. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/guide/tables.md +0 -0
  69. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/guide/watching.md +0 -0
  70. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/index.md +0 -0
  71. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/migrations.md +0 -0
  72. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/package.json +0 -0
  73. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/playwright.config.ts +0 -0
  74. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/pnpm-lock.yaml +0 -0
  75. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/pnpm-workspace.yaml +0 -0
  76. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  77. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  78. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/tests/unit/config.test.ts +0 -0
  79. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  80. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/docs/vitest.config.ts +0 -0
  81. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/src/lib.rs +0 -0
  82. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/__init__.py +0 -0
  83. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/conftest.py +0 -0
  84. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/e2e/__init__.py +0 -0
  85. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/__fixtures__/data/a/meta.json +0 -0
  86. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/__fixtures__/data/b/meta.json +0 -0
  87. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/__fixtures__/dirsql.config.py +0 -0
  88. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/__fixtures__/interpret/data/a/meta.json +0 -0
  89. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/__fixtures__/interpret/data/b/meta.json +0 -0
  90. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/__fixtures__/interpret/dirsql.config.py +0 -0
  91. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/__fixtures__/interpret/dirsql.config_no_app.py +0 -0
  92. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/__fixtures__/interpret/dirsql.config_raises.py +0 -0
  93. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/__init__.py +0 -0
  94. {dirsql-0.3.32 → dirsql-0.3.33}/packages/python/tests/integration/interpret_subprocess.py +0 -0
  95. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/Cargo.toml +0 -0
  96. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/README.md +0 -0
  97. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/benches/db_bench.rs +0 -0
  98. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/benches/differ_bench.rs +0 -0
  99. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/benches/matcher_bench.rs +0 -0
  100. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/benches/scanner_bench.rs +0 -0
  101. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/api/index.md +0 -0
  102. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/cli/config.md +0 -0
  103. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/cli/http-api.md +0 -0
  104. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/cli/index.md +0 -0
  105. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/cli/init.md +0 -0
  106. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/cli/server.md +0 -0
  107. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/getting-started.md +0 -0
  108. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/guide/async.md +0 -0
  109. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/guide/crdt.md +0 -0
  110. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/guide/persistence.md +0 -0
  111. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/guide/querying.md +0 -0
  112. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/guide/tables.md +0 -0
  113. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/guide/watching.md +0 -0
  114. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/index.md +0 -0
  115. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/docs/migrations.md +0 -0
  116. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/bin/dirsql.rs +0 -0
  117. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/cli/init.rs +0 -0
  118. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/cli/mod.rs +0 -0
  119. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/cli/native_config.rs +0 -0
  120. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/cli/router.rs +0 -0
  121. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/cli/serialize.rs +0 -0
  122. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/cli/server.rs +0 -0
  123. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/config.rs +0 -0
  124. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/db.rs +0 -0
  125. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/differ.rs +0 -0
  126. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/matcher.rs +0 -0
  127. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/persist.rs +0 -0
  128. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/scanner.rs +0 -0
  129. {dirsql-0.3.32 → dirsql-0.3.33}/packages/rust/src/watcher.rs +0 -0
  130. {dirsql-0.3.32 → dirsql-0.3.33}/pyproject.toml +0 -0
@@ -499,7 +499,7 @@ dependencies = [
499
499
 
500
500
  [[package]]
501
501
  name = "dirsql-py-ext"
502
- version = "0.3.32"
502
+ version = "0.3.33"
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.32
3
+ Version: 0.3.33
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.32"
7
+ version = "0.3.33"
8
8
  edition.workspace = true
9
9
  publish = false
10
10
  readme = "README.md"
@@ -939,7 +939,7 @@ impl DirSQL {
939
939
  // construction point reached by both `build()` and `build_async()`,
940
940
  // so the live watcher never sees a relative path (#250). `root` itself
941
941
  // is left untouched.
942
- let watch_root = PathBuf::from(canonical_root(&root));
942
+ let watch_root = PathBuf::from(fs.canonical_root(&root));
943
943
 
944
944
  Ok(Self {
945
945
  inner: Arc::new(DirSqlInner {
@@ -1373,6 +1373,9 @@ trait FileSystem: Send + Sync {
1373
1373
  fn stat(&self, path: &Path) -> std::io::Result<FileStat>;
1374
1374
  /// BLAKE3-hash a file's contents. Mirrors [`hash_file`].
1375
1375
  fn hash(&self, path: &Path) -> std::io::Result<[u8; 32]>;
1376
+ /// Canonicalize the watch root (literal fallback). Mirrors
1377
+ /// [`canonical_root`](persist::canonical_root).
1378
+ fn canonical_root(&self, root: &Path) -> String;
1376
1379
  }
1377
1380
 
1378
1381
  /// Production [`FileSystem`]: delegates to the real `std::fs` / [`hash_file`]
@@ -1387,6 +1390,10 @@ impl FileSystem for RealFs {
1387
1390
  fn hash(&self, path: &Path) -> std::io::Result<[u8; 32]> {
1388
1391
  hash_file(path)
1389
1392
  }
1393
+
1394
+ fn canonical_root(&self, root: &Path) -> String {
1395
+ canonical_root(root)
1396
+ }
1390
1397
  }
1391
1398
 
1392
1399
  /// Decide which files are trusted, which need re-parsing, and which were
@@ -1894,6 +1901,7 @@ mod internal_tests {
1894
1901
  struct FakeFs {
1895
1902
  stats: StdHashMap<PathBuf, FileStat>,
1896
1903
  hashes: StdHashMap<PathBuf, [u8; 32]>,
1904
+ canonical_roots: StdHashMap<PathBuf, String>,
1897
1905
  }
1898
1906
 
1899
1907
  impl FakeFs {
@@ -1906,6 +1914,18 @@ mod internal_tests {
1906
1914
  fn set_hash(&mut self, path: impl Into<PathBuf>, hash: [u8; 32]) {
1907
1915
  self.hashes.insert(path.into(), hash);
1908
1916
  }
1917
+
1918
+ /// Builder: register a canned canonicalization for `root`, so the
1919
+ /// `watch_root` computation in `finish_build_with_fs` becomes
1920
+ /// deterministic without touching the real filesystem or process CWD.
1921
+ fn with_canonical_root(
1922
+ mut self,
1923
+ root: impl Into<PathBuf>,
1924
+ canonical: impl Into<String>,
1925
+ ) -> Self {
1926
+ self.canonical_roots.insert(root.into(), canonical.into());
1927
+ self
1928
+ }
1909
1929
  }
1910
1930
 
1911
1931
  impl FileSystem for FakeFs {
@@ -1920,6 +1940,13 @@ mod internal_tests {
1920
1940
  std::io::Error::new(std::io::ErrorKind::NotFound, "fake: no such file")
1921
1941
  })
1922
1942
  }
1943
+
1944
+ fn canonical_root(&self, root: &Path) -> String {
1945
+ self.canonical_roots
1946
+ .get(root)
1947
+ .cloned()
1948
+ .unwrap_or_else(|| root.to_string_lossy().into_owned())
1949
+ }
1923
1950
  }
1924
1951
 
1925
1952
  /// A canned [`FileStat`] for unit tests that don't care about specific
@@ -2058,47 +2085,39 @@ mod internal_tests {
2058
2085
  // -----------------------------------------------------------------------
2059
2086
  // #250: canonical `watch_root` and the strip-prefix fallbacks.
2060
2087
  //
2061
- // `std::env::set_current_dir` is process-global, so the relative-root test
2062
- // serializes through this lock and restores the cwd on the way out.
2088
+ // The canonicalization runs through the `FileSystem` seam, so these tests
2089
+ // inject a `FakeFs` with a canned `canonical_root` mapping instead of
2090
+ // mutating the process CWD or staging real files (#233).
2063
2091
  // -----------------------------------------------------------------------
2064
2092
 
2065
- fn cwd_lock() -> &'static Mutex<()> {
2066
- static LOCK: std::sync::OnceLock<Mutex<()>> = std::sync::OnceLock::new();
2067
- LOCK.get_or_init(|| Mutex::new(()))
2068
- }
2069
-
2070
2093
  /// Building with a **relative** root canonicalizes `watch_root` to an
2071
2094
  /// absolute path while leaving `root` (and therefore `config()` / `_path`)
2072
2095
  /// exactly as the caller supplied it. This is the core of the #250 fix:
2073
2096
  /// `start_watching` watches `watch_root`, so `notify` never sees `.`.
2074
2097
  #[test]
2075
2098
  fn relative_root_canonicalizes_watch_root_only() {
2076
- let dir = TempDir::new().unwrap();
2077
- let canonical = std::fs::canonicalize(dir.path()).unwrap();
2078
-
2079
- let _guard = cwd_lock().lock().unwrap_or_else(|p| p.into_inner());
2080
- let original = std::env::current_dir().unwrap();
2081
- std::env::set_current_dir(&canonical).unwrap();
2082
-
2083
- let db = DirSQL::new(
2099
+ // FakeFs canonicalizes the relative root `.` to a fixed absolute
2100
+ // string, so the watch_root computation is deterministic with no CWD
2101
+ // juggling.
2102
+ let fake = FakeFs::default().with_canonical_root(".", "/ws/canonical");
2103
+ let db = DirSQL::with_ignore_and_fs(
2084
2104
  ".",
2085
2105
  vec![Table::new("CREATE TABLE t (x TEXT)", "*.txt", |_| vec![])],
2106
+ Vec::<String>::new(),
2107
+ Arc::new(fake),
2086
2108
  )
2087
2109
  .unwrap();
2088
2110
 
2089
- // Restore cwd before asserting so a failure can't strand the process.
2090
- std::env::set_current_dir(&original).unwrap();
2091
-
2092
2111
  // `root` is preserved verbatim; `config()` echoes it.
2093
2112
  assert_eq!(db.inner.root, PathBuf::from("."));
2094
2113
  assert_eq!(db.config().root, PathBuf::from("."));
2095
- // `watch_root` is absolute and points at the canonical temp dir.
2114
+ // `watch_root` is absolute and points at the canonical dir.
2096
2115
  assert!(
2097
2116
  db.inner.watch_root.is_absolute(),
2098
2117
  "watch_root must be absolute, got {:?}",
2099
2118
  db.inner.watch_root
2100
2119
  );
2101
- assert_eq!(db.inner.watch_root, canonical);
2120
+ assert_eq!(db.inner.watch_root, PathBuf::from("/ws/canonical"));
2102
2121
  }
2103
2122
 
2104
2123
  /// With an absolute root the canonical `watch_root` equals the (already
@@ -2107,10 +2126,14 @@ mod internal_tests {
2107
2126
  /// (watch_root) arm.
2108
2127
  #[test]
2109
2128
  fn process_file_event_strips_watch_root_prefix() {
2110
- let dir = TempDir::new().unwrap();
2111
- let canonical = std::fs::canonicalize(dir.path()).unwrap();
2112
- let db = DirSQL::new(
2113
- &canonical,
2129
+ let root = PathBuf::from("/ws");
2130
+ let abs = root.join("nested").join("a.txt");
2131
+ // FakeFs canonicalizes the root to itself (already-canonical case) and
2132
+ // stats the event path so the upsert's existence check passes — no real
2133
+ // file is staged.
2134
+ let fake = FakeFs::with_stat(abs.clone(), fake_stat()).with_canonical_root(&root, "/ws");
2135
+ let db = DirSQL::with_ignore_and_fs(
2136
+ &root,
2114
2137
  vec![Table::new(
2115
2138
  "CREATE TABLE items (name TEXT, _path TEXT)",
2116
2139
  "**/*.txt",
@@ -2121,13 +2144,11 @@ mod internal_tests {
2121
2144
  )])]
2122
2145
  },
2123
2146
  )],
2147
+ Vec::<String>::new(),
2148
+ Arc::new(fake),
2124
2149
  )
2125
2150
  .unwrap();
2126
2151
 
2127
- let abs = canonical.join("nested").join("a.txt");
2128
- std::fs::create_dir_all(canonical.join("nested")).unwrap();
2129
- std::fs::write(&abs, b"").unwrap();
2130
-
2131
2152
  let events = db.process_file_event(FileEvent::Created(abs));
2132
2153
  assert_eq!(events.len(), 1, "expected one insert: {events:?}");
2133
2154
  match &events[0] {
@@ -2148,10 +2169,11 @@ mod internal_tests {
2148
2169
  /// is not a prefix of the event path, leaving `root` as the real dir.
2149
2170
  #[test]
2150
2171
  fn process_file_event_falls_back_to_root_prefix() {
2151
- let dir = TempDir::new().unwrap();
2152
- let canonical = std::fs::canonicalize(dir.path()).unwrap();
2153
- let mut db = DirSQL::new(
2154
- &canonical,
2172
+ let root = PathBuf::from("/ws");
2173
+ let abs = root.join("b.txt");
2174
+ let fake = FakeFs::with_stat(abs.clone(), fake_stat()).with_canonical_root(&root, "/ws");
2175
+ let mut db = DirSQL::with_ignore_and_fs(
2176
+ &root,
2155
2177
  vec![Table::new(
2156
2178
  "CREATE TABLE items (name TEXT, _path TEXT)",
2157
2179
  "**/*.txt",
@@ -2162,15 +2184,15 @@ mod internal_tests {
2162
2184
  )])]
2163
2185
  },
2164
2186
  )],
2187
+ Vec::<String>::new(),
2188
+ Arc::new(fake),
2165
2189
  )
2166
2190
  .unwrap();
2167
2191
 
2168
2192
  // Repoint watch_root to a non-prefix sibling so the first strip misses
2169
2193
  // and the `.or_else(root)` arm runs. `root` stays the real dir.
2170
- Arc::get_mut(&mut db.inner).unwrap().watch_root = canonical.join("does-not-prefix");
2194
+ Arc::get_mut(&mut db.inner).unwrap().watch_root = root.join("does-not-prefix");
2171
2195
 
2172
- let abs = canonical.join("b.txt");
2173
- std::fs::write(&abs, b"").unwrap();
2174
2196
  let events = db.process_file_event(FileEvent::Created(abs));
2175
2197
  assert_eq!(events.len(), 1, "expected one insert: {events:?}");
2176
2198
  match &events[0] {
@@ -2192,10 +2214,10 @@ mod internal_tests {
2192
2214
  /// on a row.
2193
2215
  #[test]
2194
2216
  fn process_file_event_keeps_absolute_path_when_no_prefix_matches() {
2195
- let dir = TempDir::new().unwrap();
2196
- let canonical = std::fs::canonicalize(dir.path()).unwrap();
2197
- let db = DirSQL::new(
2198
- &canonical,
2217
+ let root = PathBuf::from("/ws");
2218
+ let fake = FakeFs::default().with_canonical_root(&root, "/ws");
2219
+ let db = DirSQL::with_ignore_and_fs(
2220
+ &root,
2199
2221
  vec![Table::new(
2200
2222
  "CREATE TABLE items (name TEXT)",
2201
2223
  "*.txt",
@@ -2206,12 +2228,15 @@ mod internal_tests {
2206
2228
  )])]
2207
2229
  },
2208
2230
  )],
2231
+ Vec::<String>::new(),
2232
+ Arc::new(fake),
2209
2233
  )
2210
2234
  .unwrap();
2211
2235
 
2212
2236
  // A path outside both roots: neither strip matches, so the absolute
2213
2237
  // path is used as the relative path. It does not match `*.txt` at the
2214
- // root, so no events are produced.
2238
+ // root, so no events are produced. (The non-matching glob returns before
2239
+ // any stat, so the FakeFs needs no entry for this path.)
2215
2240
  let outside = PathBuf::from("/some/elsewhere/c.md");
2216
2241
  let events = db.process_file_event(FileEvent::Created(outside));
2217
2242
  assert!(
@@ -2337,6 +2362,15 @@ mod internal_tests {
2337
2362
  fs.hash(&missing).is_err(),
2338
2363
  "hash of a missing path must error"
2339
2364
  );
2365
+ // `canonical_root` of a nonexistent path can't canonicalize, so it
2366
+ // takes the literal fallback (the path's lossy string) — exercising the
2367
+ // RealFs delegation to `persist::canonical_root` without a direct
2368
+ // `std::fs` call in the test.
2369
+ assert_eq!(
2370
+ fs.canonical_root(&missing),
2371
+ missing.to_string_lossy(),
2372
+ "canonical_root must fall back to the literal path when it can't canonicalize"
2373
+ );
2340
2374
  }
2341
2375
 
2342
2376
  // -----------------------------------------------------------------------
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