dirsql 0.3.87__tar.gz → 0.3.88__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.87 → dirsql-0.3.88}/Cargo.lock +1 -1
  2. {dirsql-0.3.87 → dirsql-0.3.88}/PKG-INFO +1 -1
  3. {dirsql-0.3.87/packages/rust → dirsql-0.3.88}/docs/reference/hooks.md +1 -1
  4. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/Cargo.toml +1 -1
  5. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/reference/hooks.md +1 -1
  6. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/e2e-attestation.json +2 -2
  7. {dirsql-0.3.87 → dirsql-0.3.88/packages/rust}/docs/reference/hooks.md +1 -1
  8. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/command.rs +9 -59
  9. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/lib.rs +7 -7
  10. {dirsql-0.3.87 → dirsql-0.3.88}/Cargo.toml +0 -0
  11. {dirsql-0.3.87 → dirsql-0.3.88}/README.md +0 -0
  12. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/__init__.py +0 -0
  13. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/_async.py +0 -0
  14. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/_dirsql.pyi +0 -0
  15. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/cli/__init__.py +0 -0
  16. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/cli/binary_path.py +0 -0
  17. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/cli/is_windows.py +0 -0
  18. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/cli/main.py +0 -0
  19. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/cli/resolve_config_extensions.py +0 -0
  20. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/py.typed +0 -0
  21. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/resolve_config_extensions.py +0 -0
  22. {dirsql-0.3.87 → dirsql-0.3.88}/dirsql/resolve_extension.py +0 -0
  23. {dirsql-0.3.87 → dirsql-0.3.88}/docs/.claude/CLAUDE.md +0 -0
  24. {dirsql-0.3.87 → dirsql-0.3.88}/docs/.vitepress/config.ts +0 -0
  25. {dirsql-0.3.87 → dirsql-0.3.88}/docs/.vitepress/theme/index.ts +0 -0
  26. {dirsql-0.3.87 → dirsql-0.3.88}/docs/.vitepress/theme/lang.ts +0 -0
  27. {dirsql-0.3.87 → dirsql-0.3.88}/docs/AGENTS.md +0 -0
  28. {dirsql-0.3.87 → dirsql-0.3.88}/docs/explanation.md +0 -0
  29. {dirsql-0.3.87 → dirsql-0.3.88}/docs/getting-started.md +0 -0
  30. {dirsql-0.3.87 → dirsql-0.3.88}/docs/howto/columns-from-paths.md +0 -0
  31. {dirsql-0.3.87 → dirsql-0.3.88}/docs/howto/define-tables.md +0 -0
  32. {dirsql-0.3.87 → dirsql-0.3.88}/docs/howto/embed.md +0 -0
  33. {dirsql-0.3.87 → dirsql-0.3.88}/docs/howto/extract-from-contents.md +0 -0
  34. {dirsql-0.3.87 → dirsql-0.3.88}/docs/howto/load-extension.md +0 -0
  35. {dirsql-0.3.87 → dirsql-0.3.88}/docs/howto/persist.md +0 -0
  36. {dirsql-0.3.87 → dirsql-0.3.88}/docs/howto/react-to-changes.md +0 -0
  37. {dirsql-0.3.87 → dirsql-0.3.88}/docs/howto/search-by-meaning.md +0 -0
  38. {dirsql-0.3.87 → dirsql-0.3.88}/docs/howto/skip-files.md +0 -0
  39. {dirsql-0.3.87 → dirsql-0.3.88}/docs/index.md +0 -0
  40. {dirsql-0.3.87 → dirsql-0.3.88}/docs/migrations.md +0 -0
  41. {dirsql-0.3.87 → dirsql-0.3.88}/docs/package.json +0 -0
  42. {dirsql-0.3.87 → dirsql-0.3.88}/docs/playwright.config.ts +0 -0
  43. {dirsql-0.3.87 → dirsql-0.3.88}/docs/pnpm-lock.yaml +0 -0
  44. {dirsql-0.3.87 → dirsql-0.3.88}/docs/pnpm-workspace.yaml +0 -0
  45. {dirsql-0.3.87 → dirsql-0.3.88}/docs/reference/cli.md +0 -0
  46. {dirsql-0.3.87 → dirsql-0.3.88}/docs/reference/columns.md +0 -0
  47. {dirsql-0.3.87 → dirsql-0.3.88}/docs/reference/config.md +0 -0
  48. {dirsql-0.3.87 → dirsql-0.3.88}/docs/reference/http-api.md +0 -0
  49. {dirsql-0.3.87 → dirsql-0.3.88}/docs/reference/sdk.md +0 -0
  50. {dirsql-0.3.87 → dirsql-0.3.88}/docs/tests/integration/home.spec.ts +0 -0
  51. {dirsql-0.3.87 → dirsql-0.3.88}/docs/tests/integration/language-flag.spec.ts +0 -0
  52. {dirsql-0.3.87 → dirsql-0.3.88}/docs/tests/integration/sidebar.spec.ts +0 -0
  53. {dirsql-0.3.87 → dirsql-0.3.88}/docs/tests/unit/config.test.ts +0 -0
  54. {dirsql-0.3.87 → dirsql-0.3.88}/docs/tests/unit/lang.test.ts +0 -0
  55. {dirsql-0.3.87 → dirsql-0.3.88}/docs/vitest.config.ts +0 -0
  56. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/README.md +0 -0
  57. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/conftest.py +0 -0
  58. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/.claude/CLAUDE.md +0 -0
  59. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/.vitepress/config.ts +0 -0
  60. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  61. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  62. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/AGENTS.md +0 -0
  63. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/explanation.md +0 -0
  64. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/getting-started.md +0 -0
  65. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/howto/columns-from-paths.md +0 -0
  66. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/howto/define-tables.md +0 -0
  67. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/howto/embed.md +0 -0
  68. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/howto/extract-from-contents.md +0 -0
  69. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/howto/load-extension.md +0 -0
  70. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/howto/persist.md +0 -0
  71. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/howto/react-to-changes.md +0 -0
  72. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/howto/search-by-meaning.md +0 -0
  73. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/howto/skip-files.md +0 -0
  74. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/index.md +0 -0
  75. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/migrations.md +0 -0
  76. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/package.json +0 -0
  77. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/playwright.config.ts +0 -0
  78. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/pnpm-lock.yaml +0 -0
  79. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/pnpm-workspace.yaml +0 -0
  80. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/reference/cli.md +0 -0
  81. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/reference/columns.md +0 -0
  82. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/reference/config.md +0 -0
  83. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/reference/http-api.md +0 -0
  84. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/reference/sdk.md +0 -0
  85. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/tests/integration/home.spec.ts +0 -0
  86. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/tests/integration/language-flag.spec.ts +0 -0
  87. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/tests/integration/sidebar.spec.ts +0 -0
  88. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/tests/unit/config.test.ts +0 -0
  89. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/tests/unit/lang.test.ts +0 -0
  90. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/docs/vitest.config.ts +0 -0
  91. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/src/lib.rs +0 -0
  92. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/tests/__init__.py +0 -0
  93. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/tests/conftest.py +0 -0
  94. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/tests/e2e/__init__.py +0 -0
  95. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/tests/integration/__init__.py +0 -0
  96. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/tests/integration/binding/__init__.py +0 -0
  97. {dirsql-0.3.87 → dirsql-0.3.88}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  98. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/Cargo.toml +0 -0
  99. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/README.md +0 -0
  100. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/benches/db_bench.rs +0 -0
  101. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/benches/differ_bench.rs +0 -0
  102. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/benches/matcher_bench.rs +0 -0
  103. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/benches/scanner_bench.rs +0 -0
  104. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/explanation.md +0 -0
  105. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/getting-started.md +0 -0
  106. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  107. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/howto/define-tables.md +0 -0
  108. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/howto/embed.md +0 -0
  109. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  110. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/howto/load-extension.md +0 -0
  111. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/howto/persist.md +0 -0
  112. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/howto/react-to-changes.md +0 -0
  113. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  114. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/howto/skip-files.md +0 -0
  115. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/index.md +0 -0
  116. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/migrations.md +0 -0
  117. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/reference/cli.md +0 -0
  118. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/reference/columns.md +0 -0
  119. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/reference/config.md +0 -0
  120. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/reference/http-api.md +0 -0
  121. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/docs/reference/sdk.md +0 -0
  122. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/bin/dirsql.rs +0 -0
  123. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/cli/execute.rs +0 -0
  124. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/cli/init.rs +0 -0
  125. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/cli/mod.rs +0 -0
  126. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/cli/router.rs +0 -0
  127. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/cli/serialize.rs +0 -0
  128. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/cli/server.rs +0 -0
  129. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/config.rs +0 -0
  130. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/db.rs +0 -0
  131. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/default_config.toml +0 -0
  132. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/differ.rs +0 -0
  133. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/matcher.rs +0 -0
  134. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/persist.rs +0 -0
  135. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/scanner.rs +0 -0
  136. {dirsql-0.3.87 → dirsql-0.3.88}/packages/rust/src/watcher.rs +0 -0
  137. {dirsql-0.3.87 → dirsql-0.3.88}/pyproject.toml +0 -0
@@ -500,7 +500,7 @@ dependencies = [
500
500
 
501
501
  [[package]]
502
502
  name = "dirsql-py-ext"
503
- version = "0.3.87"
503
+ version = "0.3.88"
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.87
3
+ Version: 0.3.88
4
4
  Summary: Ephemeral SQL index over a local directory
5
5
  Keywords: sql,filesystem,directory,sqlite,index
6
6
  Author: Kevin Scott
@@ -101,7 +101,7 @@ row-mapping rules.
101
101
 
102
102
  | Placeholder | Value |
103
103
  |---|---|
104
- | `{path}` | The matched file's path **relative to the index root**. Appended automatically as a final argument when the command omits it, so `extract.py` and `extract.py {path}` behave identically. |
104
+ | `{path}` | The matched file's path **relative to the index root**. `on-file = "extract.py {path}"` |
105
105
  | `{root}` | The index root directory. |
106
106
 
107
107
  ### `pre-query`
@@ -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.87"
7
+ version = "0.3.88"
8
8
  # abi3 note: the `abi3-py311` pyo3 feature (below) builds ONE stable-ABI
9
9
  # `cp311-abi3` wheel per platform that loads on every CPython >= 3.11
10
10
  # (matching `pyproject.toml`'s `requires-python = ">=3.11"`), instead of
@@ -101,7 +101,7 @@ row-mapping rules.
101
101
 
102
102
  | Placeholder | Value |
103
103
  |---|---|
104
- | `{path}` | The matched file's path **relative to the index root**. Appended automatically as a final argument when the command omits it, so `extract.py` and `extract.py {path}` behave identically. |
104
+ | `{path}` | The matched file's path **relative to the index root**. `on-file = "extract.py {path}"` |
105
105
  | `{root}` | The index root directory. |
106
106
 
107
107
  ### `pre-query`
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "command": "uv run python -m pytest tests/e2e/ -x -q",
3
- "ran_at": 1783632150,
3
+ "ran_at": 1783633566,
4
4
  "exit_code": 0,
5
- "commit": "89d9e986dee0ee7abaab16b6d43e9b17543d8f01"
5
+ "commit": "8c54e97f3b78c679885b4a2fd7b7d0f8a543001b"
6
6
  }
@@ -101,7 +101,7 @@ row-mapping rules.
101
101
 
102
102
  | Placeholder | Value |
103
103
  |---|---|
104
- | `{path}` | The matched file's path **relative to the index root**. Appended automatically as a final argument when the command omits it, so `extract.py` and `extract.py {path}` behave identically. |
104
+ | `{path}` | The matched file's path **relative to the index root**. `on-file = "extract.py {path}"` |
105
105
  | `{root}` | The index root directory. |
106
106
 
107
107
  ### `pre-query`
@@ -15,9 +15,6 @@
15
15
  //! value that itself contains `{…}` is never re-scanned — a substituted value
16
16
  //! is always exactly one argv element, keeping values with spaces (and
17
17
  //! untrusted input) injection-safe. An unknown `{…}` is left literal.
18
- //! - **append-if-absent.** A [`Placeholder`] marked [`Placeholder::append`] is
19
- //! appended as a final argv element when its `{name}` does not appear in the
20
- //! template (the `on-file` ergonomic: `cmd {path}` and `cmd` behave the same).
21
18
  //! - **cwd / env / timeout.** The child runs in `cwd` (the config file's
22
19
  //! directory), inherits dirsql's environment (so `uvx --with …` / `npx …`
23
20
  //! dependency resolution works), and is killed if it exceeds `timeout`.
@@ -44,34 +41,21 @@ pub const DEFAULT_COMMAND_TIMEOUT: Duration = Duration::from_secs(30);
44
41
  /// A named placeholder substituted into a command's argv.
45
42
  ///
46
43
  /// `name` is the bare identifier (no braces): a `name` of `path` matches the
47
- /// template token `{path}`.
44
+ /// template token `{path}`. Substitution only — a placeholder whose `{name}`
45
+ /// never appears in the template is a no-op.
48
46
  #[derive(Debug, Clone)]
49
47
  pub struct Placeholder {
50
48
  pub name: String,
51
49
  pub value: String,
52
- /// When `true` and the template contains no `{name}`, `value` is appended
53
- /// as a final argv element instead of being dropped.
54
- pub append_if_absent: bool,
55
50
  }
56
51
 
57
52
  impl Placeholder {
58
- /// A substitute-only placeholder: replaces `{name}` where it appears, and
59
- /// is a no-op when the template omits it.
53
+ /// Replaces `{name}` where it appears, and is a no-op when the template
54
+ /// omits it.
60
55
  pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
61
56
  Self {
62
57
  name: name.into(),
63
58
  value: value.into(),
64
- append_if_absent: false,
65
- }
66
- }
67
-
68
- /// A placeholder that is *appended* to argv when the template omits its
69
- /// `{name}` (the `on-file` `{path}` ergonomic).
70
- pub fn append(name: impl Into<String>, value: impl Into<String>) -> Self {
71
- Self {
72
- name: name.into(),
73
- value: value.into(),
74
- append_if_absent: true,
75
59
  }
76
60
  }
77
61
  }
@@ -246,32 +230,23 @@ fn build_argv(command: &str, placeholders: &[Placeholder]) -> Result<Vec<String>
246
230
  return Err(CommandError::InvalidCommand(command.to_string()));
247
231
  }
248
232
 
249
- let mut used = vec![false; placeholders.len()];
250
- let mut argv: Vec<String> = tokens
233
+ let argv: Vec<String> = tokens
251
234
  .iter()
252
- .map(|token| substitute(token, placeholders, &mut used))
235
+ .map(|token| substitute(token, placeholders))
253
236
  .collect();
254
237
 
255
- for (i, ph) in placeholders.iter().enumerate() {
256
- if ph.append_if_absent && !used[i] {
257
- argv.push(ph.value.clone());
258
- }
259
- }
260
-
261
238
  Ok(argv)
262
239
  }
263
240
 
264
241
  /// Replace every `{name}` in `token` with its placeholder value in a single
265
242
  /// left-to-right pass. Injected values are never re-scanned, so an untrusted
266
243
  /// value containing `{…}` is inert. Unknown `{…}` sequences are left literal.
267
- /// Marks `used[i]` for each placeholder that was substituted at least once.
268
- fn substitute(token: &str, placeholders: &[Placeholder], used: &mut [bool]) -> String {
244
+ fn substitute(token: &str, placeholders: &[Placeholder]) -> String {
269
245
  let mut out = String::with_capacity(token.len());
270
246
  let mut i = 0;
271
247
  while i < token.len() {
272
248
  if let Some((idx, consumed)) = match_placeholder(token, i, placeholders) {
273
249
  out.push_str(&placeholders[idx].value);
274
- used[idx] = true;
275
250
  i += consumed;
276
251
  continue;
277
252
  }
@@ -417,27 +392,8 @@ mod tests {
417
392
  }
418
393
 
419
394
  #[test]
420
- fn append_if_absent_appends_when_the_template_omits_the_token() {
421
- assert_eq!(
422
- argv("extract.py", &[Placeholder::append("path", "docs/a.md")]),
423
- ["extract.py", "docs/a.md"]
424
- );
425
- }
426
-
427
- #[test]
428
- fn append_if_absent_does_not_append_when_the_token_is_present() {
429
- assert_eq!(
430
- argv(
431
- "extract.py --file {path}",
432
- &[Placeholder::append("path", "docs/a.md")]
433
- ),
434
- ["extract.py", "--file", "docs/a.md"]
435
- );
436
- }
437
-
438
- #[test]
439
- fn substitute_only_placeholder_is_dropped_when_absent() {
440
- assert_eq!(argv("run", &[Placeholder::new("extra", "/tmp/x")]), ["run"]);
395
+ fn a_placeholder_the_template_omits_is_dropped() {
396
+ assert_eq!(argv("run", &[Placeholder::new("path", "/tmp/x")]), ["run"]);
441
397
  }
442
398
 
443
399
  #[test]
@@ -476,12 +432,6 @@ mod tests {
476
432
  assert!(tail.ends_with('x'));
477
433
  }
478
434
 
479
- #[test]
480
- fn placeholder_constructors_set_append_flag() {
481
- assert!(!Placeholder::new("a", "b").append_if_absent);
482
- assert!(Placeholder::append("a", "b").append_if_absent);
483
- }
484
-
485
435
  // The run_command tests spawn real `sh`/`echo`/`cat`. Their test code must
486
436
  // statically reference only `super::` items (plus pure std) to satisfy the
487
437
  // unit-lint isolation rule.
@@ -1427,9 +1427,10 @@ fn build_tables_from_config(
1427
1427
  /// Run a table's `on-file` command for one matched file and parse its output
1428
1428
  /// into rows.
1429
1429
  ///
1430
- /// Placeholders: `{path}` (the file relative to `root`, append-if-absent so
1431
- /// `cmd` and `cmd {path}` behave identically) and `{root}` (the index root).
1432
- /// `{path}` falls back to the absolute path when the file is not under `root`.
1430
+ /// Placeholders: `{path}` (the file relative to `root`) and `{root}` (the
1431
+ /// index root). `{path}` falls back to the absolute path when the file is not
1432
+ /// under `root`. A template that omits a placeholder simply never receives its
1433
+ /// value — nothing is appended.
1433
1434
  ///
1434
1435
  /// Per-file isolation: any failure — a spawn/exit/timeout error from
1435
1436
  /// [`command::run_command`], or output that is not a JSON array of objects —
@@ -1449,7 +1450,7 @@ fn run_on_file(
1449
1450
  .map(|p| p.to_string_lossy().into_owned())
1450
1451
  .unwrap_or_else(|_| abs_path.to_string());
1451
1452
  let placeholders = [
1452
- Placeholder::append("path", rel),
1453
+ Placeholder::new("path", rel),
1453
1454
  Placeholder::new("root", root.to_string_lossy().into_owned()),
1454
1455
  ];
1455
1456
 
@@ -2874,9 +2875,8 @@ mod internal_tests {
2874
2875
  fn run_on_file_parses_command_json_output() {
2875
2876
  let dir = TempDir::new().unwrap();
2876
2877
  let abs = dir.path().join("f.txt");
2877
- // `{path}` is append-if-absent, so the file path lands as a trailing
2878
- // argv element; `printf` with a conversion-free format ignores it,
2879
- // keeping the JSON payload clean.
2878
+ // The template omits every placeholder, so nothing is appended; the
2879
+ // `printf` payload is the whole output.
2880
2880
  let rows = run_on_file(
2881
2881
  "printf '[{\"n\":1}]'",
2882
2882
  &abs.to_string_lossy(),
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