dirsql 0.3.63__tar.gz → 0.3.65__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 (137) hide show
  1. {dirsql-0.3.63 → dirsql-0.3.65}/Cargo.lock +1 -1
  2. {dirsql-0.3.63 → dirsql-0.3.65}/PKG-INFO +1 -1
  3. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/Cargo.toml +1 -1
  4. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/e2e-attestation.json +2 -2
  5. dirsql-0.3.65/packages/python/tests/smoke/__init__.py +0 -0
  6. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/cli/serialize.rs +12 -8
  7. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/db.rs +6 -90
  8. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/lib.rs +21 -16
  9. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/persist.rs +8 -1
  10. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/watcher.rs +53 -33
  11. dirsql-0.3.63/dirsql/cli/__init__.py +0 -6
  12. dirsql-0.3.63/dirsql/cli/interpret/__init__.py +0 -14
  13. {dirsql-0.3.63 → dirsql-0.3.65}/Cargo.toml +0 -0
  14. {dirsql-0.3.63 → dirsql-0.3.65}/README.md +0 -0
  15. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/__init__.py +0 -0
  16. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/_async.py +0 -0
  17. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/_dirsql.pyi +0 -0
  18. {dirsql-0.3.63/packages/python/tests/binding → dirsql-0.3.65/dirsql/cli}/__init__.py +0 -0
  19. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/cli/binary_path.py +0 -0
  20. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/cli/is_windows.py +0 -0
  21. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/cli/main.py +0 -0
  22. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/cli/resolve_config_extensions.py +0 -0
  23. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/py.typed +0 -0
  24. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/resolve_config_extensions.py +0 -0
  25. {dirsql-0.3.63 → dirsql-0.3.65}/dirsql/resolve_extension.py +0 -0
  26. {dirsql-0.3.63 → dirsql-0.3.65}/docs/.claude/CLAUDE.md +0 -0
  27. {dirsql-0.3.63 → dirsql-0.3.65}/docs/.vitepress/config.ts +0 -0
  28. {dirsql-0.3.63 → dirsql-0.3.65}/docs/.vitepress/theme/index.ts +0 -0
  29. {dirsql-0.3.63 → dirsql-0.3.65}/docs/.vitepress/theme/lang.ts +0 -0
  30. {dirsql-0.3.63 → dirsql-0.3.65}/docs/AGENTS.md +0 -0
  31. {dirsql-0.3.63 → dirsql-0.3.65}/docs/explanation.md +0 -0
  32. {dirsql-0.3.63 → dirsql-0.3.65}/docs/getting-started.md +0 -0
  33. {dirsql-0.3.63 → dirsql-0.3.65}/docs/howto/columns-from-paths.md +0 -0
  34. {dirsql-0.3.63 → dirsql-0.3.65}/docs/howto/define-tables.md +0 -0
  35. {dirsql-0.3.63 → dirsql-0.3.65}/docs/howto/embed.md +0 -0
  36. {dirsql-0.3.63 → dirsql-0.3.65}/docs/howto/extract-from-contents.md +0 -0
  37. {dirsql-0.3.63 → dirsql-0.3.65}/docs/howto/load-extension.md +0 -0
  38. {dirsql-0.3.63 → dirsql-0.3.65}/docs/howto/persist.md +0 -0
  39. {dirsql-0.3.63 → dirsql-0.3.65}/docs/howto/react-to-changes.md +0 -0
  40. {dirsql-0.3.63 → dirsql-0.3.65}/docs/howto/search-by-meaning.md +0 -0
  41. {dirsql-0.3.63 → dirsql-0.3.65}/docs/howto/skip-files.md +0 -0
  42. {dirsql-0.3.63 → dirsql-0.3.65}/docs/index.md +0 -0
  43. {dirsql-0.3.63 → dirsql-0.3.65}/docs/migrations.md +0 -0
  44. {dirsql-0.3.63 → dirsql-0.3.65}/docs/package.json +0 -0
  45. {dirsql-0.3.63 → dirsql-0.3.65}/docs/playwright.config.ts +0 -0
  46. {dirsql-0.3.63 → dirsql-0.3.65}/docs/pnpm-lock.yaml +0 -0
  47. {dirsql-0.3.63 → dirsql-0.3.65}/docs/pnpm-workspace.yaml +0 -0
  48. {dirsql-0.3.63 → dirsql-0.3.65}/docs/reference/cli.md +0 -0
  49. {dirsql-0.3.63 → dirsql-0.3.65}/docs/reference/columns.md +0 -0
  50. {dirsql-0.3.63 → dirsql-0.3.65}/docs/reference/config.md +0 -0
  51. {dirsql-0.3.63 → dirsql-0.3.65}/docs/reference/hooks.md +0 -0
  52. {dirsql-0.3.63 → dirsql-0.3.65}/docs/reference/http-api.md +0 -0
  53. {dirsql-0.3.63 → dirsql-0.3.65}/docs/reference/sdk.md +0 -0
  54. {dirsql-0.3.63 → dirsql-0.3.65}/docs/tests/integration/home.spec.ts +0 -0
  55. {dirsql-0.3.63 → dirsql-0.3.65}/docs/tests/integration/language-flag.spec.ts +0 -0
  56. {dirsql-0.3.63 → dirsql-0.3.65}/docs/tests/integration/sidebar.spec.ts +0 -0
  57. {dirsql-0.3.63 → dirsql-0.3.65}/docs/tests/unit/config.test.ts +0 -0
  58. {dirsql-0.3.63 → dirsql-0.3.65}/docs/tests/unit/lang.test.ts +0 -0
  59. {dirsql-0.3.63 → dirsql-0.3.65}/docs/vitest.config.ts +0 -0
  60. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/README.md +0 -0
  61. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/conftest.py +0 -0
  62. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/.claude/CLAUDE.md +0 -0
  63. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/.vitepress/config.ts +0 -0
  64. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  65. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  66. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/AGENTS.md +0 -0
  67. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/explanation.md +0 -0
  68. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/getting-started.md +0 -0
  69. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/howto/columns-from-paths.md +0 -0
  70. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/howto/define-tables.md +0 -0
  71. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/howto/embed.md +0 -0
  72. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/howto/extract-from-contents.md +0 -0
  73. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/howto/load-extension.md +0 -0
  74. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/howto/persist.md +0 -0
  75. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/howto/react-to-changes.md +0 -0
  76. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/howto/search-by-meaning.md +0 -0
  77. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/howto/skip-files.md +0 -0
  78. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/index.md +0 -0
  79. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/migrations.md +0 -0
  80. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/package.json +0 -0
  81. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/playwright.config.ts +0 -0
  82. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/pnpm-lock.yaml +0 -0
  83. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/pnpm-workspace.yaml +0 -0
  84. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/reference/cli.md +0 -0
  85. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/reference/columns.md +0 -0
  86. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/reference/config.md +0 -0
  87. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/reference/hooks.md +0 -0
  88. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/reference/http-api.md +0 -0
  89. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/reference/sdk.md +0 -0
  90. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  91. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  92. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
  93. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/tests/unit/config.test.ts +0 -0
  94. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  95. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/docs/vitest.config.ts +0 -0
  96. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/src/lib.rs +0 -0
  97. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/tests/__init__.py +0 -0
  98. {dirsql-0.3.63/packages/python/tests/e2e → dirsql-0.3.65/packages/python/tests/binding}/__init__.py +0 -0
  99. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/tests/conftest.py +0 -0
  100. {dirsql-0.3.63/packages/python/tests/smoke → dirsql-0.3.65/packages/python/tests/e2e}/__init__.py +0 -0
  101. {dirsql-0.3.63 → dirsql-0.3.65}/packages/python/tests/integration/__init__.py +0 -0
  102. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/Cargo.toml +0 -0
  103. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/README.md +0 -0
  104. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/benches/db_bench.rs +0 -0
  105. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/benches/differ_bench.rs +0 -0
  106. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/benches/matcher_bench.rs +0 -0
  107. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/benches/scanner_bench.rs +0 -0
  108. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/explanation.md +0 -0
  109. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/getting-started.md +0 -0
  110. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  111. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/howto/define-tables.md +0 -0
  112. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/howto/embed.md +0 -0
  113. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  114. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/howto/load-extension.md +0 -0
  115. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/howto/persist.md +0 -0
  116. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/howto/react-to-changes.md +0 -0
  117. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  118. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/howto/skip-files.md +0 -0
  119. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/index.md +0 -0
  120. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/migrations.md +0 -0
  121. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/reference/cli.md +0 -0
  122. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/reference/columns.md +0 -0
  123. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/reference/config.md +0 -0
  124. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/reference/hooks.md +0 -0
  125. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/reference/http-api.md +0 -0
  126. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/docs/reference/sdk.md +0 -0
  127. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/bin/dirsql.rs +0 -0
  128. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/cli/init.rs +0 -0
  129. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/cli/mod.rs +0 -0
  130. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/cli/router.rs +0 -0
  131. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/cli/server.rs +0 -0
  132. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/command.rs +0 -0
  133. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/config.rs +0 -0
  134. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/differ.rs +0 -0
  135. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/matcher.rs +0 -0
  136. {dirsql-0.3.63 → dirsql-0.3.65}/packages/rust/src/scanner.rs +0 -0
  137. {dirsql-0.3.63 → dirsql-0.3.65}/pyproject.toml +0 -0
@@ -500,7 +500,7 @@ dependencies = [
500
500
 
501
501
  [[package]]
502
502
  name = "dirsql-py-ext"
503
- version = "0.3.63"
503
+ version = "0.3.65"
504
504
  dependencies = [
505
505
  "dirsql",
506
506
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.3.63
3
+ Version: 0.3.65
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.63"
7
+ version = "0.3.65"
8
8
  edition.workspace = true
9
9
  publish = false
10
10
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "command": "uv run python -m pytest tests/e2e/ -x -q",
3
- "ran_at": 1783184817,
3
+ "ran_at": 1783214202,
4
4
  "exit_code": 0,
5
- "commit": "ff790f3535072982be3fa349de4caf8c855fc406"
5
+ "commit": "b2c468d12c2e60a7df48884b391a788fd184a62f"
6
6
  }
File without changes
@@ -41,7 +41,11 @@ fn hex_encode(bytes: &[u8]) -> String {
41
41
  }
42
42
 
43
43
  pub(super) fn event_to_json(event: &RowEvent) -> String {
44
- let value = match event {
44
+ event_to_value(event).to_string()
45
+ }
46
+
47
+ fn event_to_value(event: &RowEvent) -> Value {
48
+ match event {
45
49
  RowEvent::Insert {
46
50
  table,
47
51
  row,
@@ -86,8 +90,7 @@ pub(super) fn event_to_json(event: &RowEvent) -> String {
86
90
  "file_path": file_path.to_string_lossy(),
87
91
  "error": error,
88
92
  }),
89
- };
90
- value.to_string()
93
+ }
91
94
  }
92
95
 
93
96
  // ---------------------------------------------------------------------------
@@ -119,8 +122,7 @@ mod tests {
119
122
  row,
120
123
  file_path: "posts/a.json".into(),
121
124
  };
122
- let s = event_to_json(&event);
123
- let parsed: Value = serde_json::from_str(&s).unwrap();
125
+ let parsed = event_to_value(&event);
124
126
  assert_eq!(parsed.get("action").and_then(Value::as_str), Some("insert"));
125
127
  assert_eq!(parsed.get("table").and_then(Value::as_str), Some("posts"));
126
128
  assert_eq!(
@@ -128,6 +130,8 @@ mod tests {
128
130
  Some("posts/a.json"),
129
131
  );
130
132
  assert!(parsed.get("old_row").unwrap().is_null());
133
+ // event_to_json is the string form of the value it builds.
134
+ assert_eq!(event_to_json(&event), parsed.to_string());
131
135
  }
132
136
 
133
137
  #[test]
@@ -142,7 +146,7 @@ mod tests {
142
146
  new_row: new,
143
147
  file_path: "posts/a.json".into(),
144
148
  };
145
- let parsed: Value = serde_json::from_str(&event_to_json(&event)).unwrap();
149
+ let parsed = event_to_value(&event);
146
150
  assert_eq!(
147
151
  parsed.pointer("/row/id").and_then(Value::as_str),
148
152
  Some("abc2")
@@ -160,7 +164,7 @@ mod tests {
160
164
  file_path: PathBuf::from("bad.json"),
161
165
  error: "parse failed".into(),
162
166
  };
163
- let parsed: Value = serde_json::from_str(&event_to_json(&event)).unwrap();
167
+ let parsed = event_to_value(&event);
164
168
  assert_eq!(parsed.get("action").and_then(Value::as_str), Some("error"));
165
169
  assert_eq!(
166
170
  parsed.get("error").and_then(Value::as_str),
@@ -177,7 +181,7 @@ mod tests {
177
181
  row,
178
182
  file_path: "posts/a.json".into(),
179
183
  };
180
- let parsed: Value = serde_json::from_str(&event_to_json(&event)).unwrap();
184
+ let parsed = event_to_value(&event);
181
185
  assert_eq!(parsed.get("action").and_then(Value::as_str), Some("delete"));
182
186
  assert_eq!(parsed.get("table").and_then(Value::as_str), Some("posts"));
183
187
  assert_eq!(
@@ -624,7 +624,6 @@ impl From<rusqlite::types::Value> for Value {
624
624
  #[cfg(test)]
625
625
  mod tests {
626
626
  use super::*;
627
- use rusqlite::types::ToSql;
628
627
 
629
628
  #[test]
630
629
  fn create_table_from_ddl() {
@@ -966,95 +965,12 @@ mod tests {
966
965
  assert_eq!(normalized.len(), 2);
967
966
  }
968
967
 
969
- // --- Value::to_sql coverage for all variants ---
970
-
971
- #[test]
972
- fn value_to_sql_null() {
973
- let v = Value::Null;
974
- let result = v.to_sql().unwrap();
975
- // Branch-free: compare against the expected value (`ToSqlOutput: PartialEq`)
976
- // rather than `matches!`, whose `_ => false` arm is a dead region here.
977
- assert_eq!(
978
- result,
979
- rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Null)
980
- );
981
- }
982
-
983
- #[test]
984
- fn value_to_sql_integer() {
985
- let v = Value::Integer(42);
986
- let result = v.to_sql().unwrap();
987
- // Branch-free: see `value_to_sql_null` -- compare values directly.
988
- assert_eq!(
989
- result,
990
- rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(42))
991
- );
992
- }
993
-
994
- #[test]
995
- fn value_to_sql_real() {
996
- let v = Value::Real(1.5);
997
- let result = v.to_sql().unwrap();
998
- // Branch-free: `ToSqlOutput` derives `PartialEq`, so compare against
999
- // the exact expected value instead of a `match` with a dead `_` arm.
1000
- // 1.5 is exactly representable in f64, so equality is precise here.
1001
- assert_eq!(
1002
- result,
1003
- rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Real(1.5))
1004
- );
1005
- }
1006
-
1007
- #[test]
1008
- fn value_to_sql_text() {
1009
- let v = Value::Text("hello".into());
1010
- let result = v.to_sql().unwrap();
1011
- assert!(matches!(
1012
- result,
1013
- rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Text(ref s)) if s == "hello"
1014
- ));
1015
- }
1016
-
1017
- #[test]
1018
- fn value_to_sql_blob() {
1019
- let v = Value::Blob(vec![1, 2, 3]);
1020
- let result = v.to_sql().unwrap();
1021
- assert!(matches!(
1022
- result,
1023
- rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Blob(ref b)) if b == &[1, 2, 3]
1024
- ));
1025
- }
1026
-
1027
- // --- Value::from coverage for all variants ---
1028
-
1029
- #[test]
1030
- fn value_from_sqlite_null() {
1031
- let v = Value::from(rusqlite::types::Value::Null);
1032
- assert_eq!(v, Value::Null);
1033
- }
1034
-
1035
- #[test]
1036
- fn value_from_sqlite_integer() {
1037
- let v = Value::from(rusqlite::types::Value::Integer(99));
1038
- assert_eq!(v, Value::Integer(99));
1039
- }
1040
-
1041
- #[test]
1042
- fn value_from_sqlite_real() {
1043
- let v = Value::from(rusqlite::types::Value::Real(1.25));
1044
- assert_eq!(v, Value::Real(1.25));
1045
- }
1046
-
1047
- #[test]
1048
- fn value_from_sqlite_text() {
1049
- let v = Value::from(rusqlite::types::Value::Text("world".into()));
1050
- assert_eq!(v, Value::Text("world".into()));
1051
- }
1052
-
1053
- #[test]
1054
- fn value_from_sqlite_blob() {
1055
- let v = Value::from(rusqlite::types::Value::Blob(vec![10, 20]));
1056
- assert_eq!(v, Value::Blob(vec![10, 20]));
1057
- }
968
+ // `Value::to_sql` (our type -> SQLite param) and `From<rusqlite::types::Value>`
969
+ // (SQLite value -> our type) are the marshaling boundary. Every variant's
970
+ // arm is exercised end-to-end by the insert/query round-trip tests below
971
+ // (Real, Null, Blob each have a dedicated one; Text + Integer via
972
+ // `insert_and_query_rows`), so the arms stay covered without a unit test
973
+ // naming `rusqlite::types::*` directly (the `unit lint` isolation rule).
1058
974
 
1059
975
  // --- Insert and query with real/blob values ---
1060
976
 
@@ -1731,6 +1731,20 @@ struct AsyncDirSqlInner {
1731
1731
  ready_notify: tokio::sync::Notify,
1732
1732
  }
1733
1733
 
1734
+ #[cfg(test)]
1735
+ impl AsyncDirSqlInner {
1736
+ /// Fresh inner with an empty `db` cell — the pre-`ready` state. Lets the unit
1737
+ /// tests build this fixture without naming the `tokio::sync` primitives (the
1738
+ /// `unit lint` isolation rule); real async runtime behavior is exercised in
1739
+ /// the integration/binding tiers.
1740
+ fn empty() -> Self {
1741
+ Self {
1742
+ db: tokio::sync::OnceCell::new(),
1743
+ ready_notify: tokio::sync::Notify::new(),
1744
+ }
1745
+ }
1746
+ }
1747
+
1734
1748
  impl AsyncDirSQL {
1735
1749
  /// Shortcut for `DirSQL::builder().root(root).tables(tables).build_async()`.
1736
1750
  pub fn new(root: impl Into<PathBuf>, tables: Vec<Table>) -> Result<Self> {
@@ -1844,14 +1858,11 @@ mod readonly_tests {
1844
1858
  assert!(matches!(err, DirSqlError::WriteForbidden), "got: {err:?}");
1845
1859
  }
1846
1860
 
1847
- #[test]
1848
- fn map_db_error_leaves_sqlite_errors_as_core() {
1849
- let err = map_db_error(DbError::Sqlite(rusqlite::Error::SqliteFailure(
1850
- rusqlite::ffi::Error::new(rusqlite::ffi::SQLITE_ERROR),
1851
- Some("syntax error".into()),
1852
- )));
1853
- assert!(matches!(err, DirSqlError::Core(_)), "got: {err:?}");
1854
- }
1861
+ // `map_db_error` treats every non-`WriteForbidden` variant identically (the
1862
+ // `other => Core` arm), so `map_db_error_leaves_schema_mismatch_as_core`
1863
+ // below fully covers that arm without a unit test naming `rusqlite::Error`.
1864
+ // The Sqlite-error mapping is additionally exercised end-to-end in the
1865
+ // integration/binding tiers.
1855
1866
 
1856
1867
  #[test]
1857
1868
  fn map_db_error_leaves_schema_mismatch_as_core() {
@@ -3197,10 +3208,7 @@ mod internal_tests {
3197
3208
  /// empty `OnceCell` so the state is deterministic (no build race).
3198
3209
  #[test]
3199
3210
  fn async_dirsql_sync_before_ready_is_not_ready() {
3200
- let inner = Arc::new(AsyncDirSqlInner {
3201
- db: tokio::sync::OnceCell::new(),
3202
- ready_notify: tokio::sync::Notify::new(),
3203
- });
3211
+ let inner = Arc::new(AsyncDirSqlInner::empty());
3204
3212
  let adb = AsyncDirSQL { inner };
3205
3213
  // `sync` returns `Result<DirSQL, _>`; DirSQL is not Debug, so match
3206
3214
  // rather than `unwrap_err`.
@@ -3215,10 +3223,7 @@ mod internal_tests {
3215
3223
  /// "init failed" error carrying the underlying diagnostic.
3216
3224
  #[tokio::test]
3217
3225
  async fn async_dirsql_surfaces_init_failure() {
3218
- let inner = Arc::new(AsyncDirSqlInner {
3219
- db: tokio::sync::OnceCell::new(),
3220
- ready_notify: tokio::sync::Notify::new(),
3221
- });
3226
+ let inner = Arc::new(AsyncDirSqlInner::empty());
3222
3227
  inner
3223
3228
  .db
3224
3229
  .set(Err(DirSqlError::Ddl("boom".into())))
@@ -564,7 +564,14 @@ mod tests {
564
564
  let p = dir.path().join("a.txt");
565
565
  fs::write(&p, b"hello").unwrap();
566
566
  let h = hash_file(&p).unwrap();
567
- let expected = *blake3::hash(b"hello").as_bytes();
567
+ // Golden BLAKE3 digest of b"hello" (pins the exact algorithm + output;
568
+ // hardcoded so the unit test does not call the blake3 crate itself).
569
+ // hex: ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f
570
+ let expected: [u8; 32] = [
571
+ 0xea, 0x8f, 0x16, 0x3d, 0xb3, 0x86, 0x82, 0x92, 0x5e, 0x44, 0x91, 0xc5, 0xe5, 0x8d,
572
+ 0x4b, 0xb3, 0x50, 0x6e, 0xf8, 0xc1, 0x4e, 0xb7, 0x8a, 0x86, 0xe9, 0x08, 0xc5, 0x62,
573
+ 0x4a, 0x67, 0x20, 0x0f,
574
+ ];
568
575
  assert_eq!(h, expected);
569
576
  }
570
577
 
@@ -84,6 +84,48 @@ fn translate_event(event: &Event) -> Vec<FileEvent> {
84
84
  results
85
85
  }
86
86
 
87
+ // Test fixtures: build `notify::Event`s for each top-level `EventKind` without
88
+ // the unit tests below naming the `notify::event::*` inner-kind types (the
89
+ // `unit lint` isolation rule). `translate_event` matches only the outer
90
+ // `EventKind` variant, so the inner kind here is an arbitrary valid value.
91
+ #[cfg(test)]
92
+ fn create_event(paths: Vec<PathBuf>) -> Event {
93
+ Event {
94
+ kind: EventKind::Create(notify::event::CreateKind::File),
95
+ paths,
96
+ attrs: Default::default(),
97
+ }
98
+ }
99
+
100
+ #[cfg(test)]
101
+ fn modify_event(paths: Vec<PathBuf>) -> Event {
102
+ Event {
103
+ kind: EventKind::Modify(notify::event::ModifyKind::Data(
104
+ notify::event::DataChange::Content,
105
+ )),
106
+ paths,
107
+ attrs: Default::default(),
108
+ }
109
+ }
110
+
111
+ #[cfg(test)]
112
+ fn remove_event(paths: Vec<PathBuf>) -> Event {
113
+ Event {
114
+ kind: EventKind::Remove(notify::event::RemoveKind::File),
115
+ paths,
116
+ attrs: Default::default(),
117
+ }
118
+ }
119
+
120
+ #[cfg(test)]
121
+ fn access_event(paths: Vec<PathBuf>) -> Event {
122
+ Event {
123
+ kind: EventKind::Access(notify::event::AccessKind::Read),
124
+ paths,
125
+ attrs: Default::default(),
126
+ }
127
+ }
128
+
87
129
  #[cfg(test)]
88
130
  mod tests {
89
131
  use super::*;
@@ -92,16 +134,13 @@ mod tests {
92
134
  // files (`std::fs`, `std::thread`, `Instant::now`) live in
93
135
  // `tests/watcher.rs` -- they're integration tests, and keeping them out of
94
136
  // this inline module is what the `unit lint` isolation rule requires. Only
95
- // the pure `translate_event` mapping tests belong here.
137
+ // the pure `translate_event` mapping tests belong here; they build their
138
+ // `notify::Event` inputs via the `*_event` fixtures above so no unit test
139
+ // names a `notify::event::*` type directly.
96
140
 
97
141
  #[test]
98
142
  fn translate_event_maps_create() {
99
- let event = Event {
100
- kind: EventKind::Create(notify::event::CreateKind::File),
101
- paths: vec![PathBuf::from("/tmp/test.txt")],
102
- attrs: Default::default(),
103
- };
104
- let results = translate_event(&event);
143
+ let results = translate_event(&create_event(vec![PathBuf::from("/tmp/test.txt")]));
105
144
  assert_eq!(
106
145
  results,
107
146
  vec![FileEvent::Created(PathBuf::from("/tmp/test.txt"))]
@@ -110,12 +149,7 @@ mod tests {
110
149
 
111
150
  #[test]
112
151
  fn translate_event_maps_remove() {
113
- let event = Event {
114
- kind: EventKind::Remove(notify::event::RemoveKind::File),
115
- paths: vec![PathBuf::from("/tmp/gone.txt")],
116
- attrs: Default::default(),
117
- };
118
- let results = translate_event(&event);
152
+ let results = translate_event(&remove_event(vec![PathBuf::from("/tmp/gone.txt")]));
119
153
  assert_eq!(
120
154
  results,
121
155
  vec![FileEvent::Deleted(PathBuf::from("/tmp/gone.txt"))]
@@ -124,25 +158,13 @@ mod tests {
124
158
 
125
159
  #[test]
126
160
  fn translate_event_ignores_access_events() {
127
- let event = Event {
128
- kind: EventKind::Access(notify::event::AccessKind::Read),
129
- paths: vec![PathBuf::from("/tmp/read.txt")],
130
- attrs: Default::default(),
131
- };
132
- let results = translate_event(&event);
161
+ let results = translate_event(&access_event(vec![PathBuf::from("/tmp/read.txt")]));
133
162
  assert!(results.is_empty());
134
163
  }
135
164
 
136
165
  #[test]
137
166
  fn translate_event_maps_modify() {
138
- let event = Event {
139
- kind: EventKind::Modify(notify::event::ModifyKind::Data(
140
- notify::event::DataChange::Content,
141
- )),
142
- paths: vec![PathBuf::from("/tmp/changed.txt")],
143
- attrs: Default::default(),
144
- };
145
- let results = translate_event(&event);
167
+ let results = translate_event(&modify_event(vec![PathBuf::from("/tmp/changed.txt")]));
146
168
  assert_eq!(
147
169
  results,
148
170
  vec![FileEvent::Modified(PathBuf::from("/tmp/changed.txt"))]
@@ -151,12 +173,10 @@ mod tests {
151
173
 
152
174
  #[test]
153
175
  fn translate_event_multiple_paths() {
154
- let event = Event {
155
- kind: EventKind::Create(notify::event::CreateKind::File),
156
- paths: vec![PathBuf::from("/tmp/a.txt"), PathBuf::from("/tmp/b.txt")],
157
- attrs: Default::default(),
158
- };
159
- let results = translate_event(&event);
176
+ let results = translate_event(&create_event(vec![
177
+ PathBuf::from("/tmp/a.txt"),
178
+ PathBuf::from("/tmp/b.txt"),
179
+ ]));
160
180
  assert_eq!(results.len(), 2);
161
181
  assert_eq!(results[0], FileEvent::Created(PathBuf::from("/tmp/a.txt")));
162
182
  assert_eq!(results[1], FileEvent::Created(PathBuf::from("/tmp/b.txt")));
@@ -1,6 +0,0 @@
1
- """CLI launcher package.
2
-
3
- The ``dirsql`` console-script entry resolves to
4
- ``dirsql.cli.main:main``. See each submodule for the individual
5
- functions: :mod:`is_windows`, :mod:`binary_path`, :mod:`main`.
6
- """
@@ -1,14 +0,0 @@
1
- """Empty package shell — the native-config ``interpret`` helper was removed.
2
-
3
- The ``dirsql interpret`` subcommand and its NDJSON ``extract`` loop (``run``,
4
- ``load_app``, ``dispatch_extract``, ``write_message``) were removed in #321
5
- (#323). The CLI now accepts only ``.dirsql.toml``; to run user-defined
6
- ``extract`` callbacks, use the programmatic SDK (``DirSQL(...)`` with
7
- in-process closures).
8
-
9
- This ``__init__.py`` carries no logic and no re-exports. It remains only
10
- because the colocated-test tooling cannot yet express *deleting* an exempt
11
- package barrel (the co-change check flags a deleted source that has no
12
- co-deleted colocated test, and a retained exempt for a deleted path is
13
- rejected as stale). The directory is removed once that is resolved.
14
- """
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