feat(vars): add pattern type to definitions.vars#334
Conversation
…-prefix patterns
Adds VarType::Pattern alongside Literal and Path. Each `pattern`-typed
value is parsed as a rule-pattern fragment and inlined wherever
`<var:name>` appears, so a base CLI plus its global flags can be named
once and reused across rules:
definitions:
vars:
kubectl:
type: pattern
values:
- "kubectl [-n|--namespace *] [--context *] [--cluster *]"
rules:
- allow: "<var:kubectl> get|describe|logs *"
- allow: "<var:kubectl> top node|pod|nodes|pods *"
intent(vars): let users name a "command + global flags" prefix once
instead of repeating `[-n *] [--context *] ...` on every rule, which
was previously unworkable because flag_groups requires at-least-one
occurrence and `<opts>` does not absorb space-separated long-flag
values
decision(vars): inline-expand pre-parsed sub-patterns at `<var:name>`
positions and splice their tokens into the outer pattern's remaining
tokens before recursing through match_engine, reusing the existing
Optional/Wildcard/Flag matching paths instead of inventing a parallel
engine
decision(vars): forbid placeholder nesting (<cmd>, <opts>, <vars>,
<var:>, <path:>, <flag:>) inside pattern values, and reject
[<var:pattern-typed>] (pattern var inside an optional group), both
at validate() time, keeping semantics single-pass and mirroring the
long-standing <flag:name> restriction
rejected(vars): extending <flag:name> with a "zero-or-more" mode -
bool/value-flag aliases must collapse into flag_groups captures and
the change would break that contract
rejected(vars): adding implicit recursive expansion of nested <var:>
references - opens the door to cycles and conflicts with pre-parse
caching (parsed_pattern_vars)
constraint(matcher): pattern-typed values cannot be consumed via the
single-token match_var_ref path, so match_value_with_type returns
false for VarType::Pattern; multi-token consumption goes through
match_pattern_prefix / inline expansion in match_engine instead
learned(matcher): rule_engine::collect_value_flags and matcher's
collect_value_flag_aliases both walk pattern tokens to register
value-flag aliases for the command parser. Both must recurse into
parsed_pattern_vars sub-patterns - otherwise `--namespace foo` from a
sub-pattern's `[--namespace *]` is mis-tokenised as a positional
…refix budget intent(docs): the previous commit's "Pattern Type Inline Expansion" table embedded the rule pattern (which contains `|`) inside a header cell, so remark-gfm parsed it as extra columns and the rendered Starlight page showed an unaligned, mostly-empty grid intent(matcher): doc comment of match_pattern_prefix described an approach (append `Wildcard`, read remaining-suffix length) that does not match the implementation, which enumerates `for k in 0..=rest.len()` and runs full-match per k. Rewrite the comment to match the code. intent(matcher): each `k` iteration of match_pattern_prefix used to allocate a fresh `Cell::new(0)` step budget, so the global MAX_MATCH_STEPS DoS guard was effectively multiplied by `cmd_tokens` length when a pattern-typed `<var:>` sat in command position. Share one budget across all k trials. decision(docs): also document that pattern-typed `<var:name>` exposes only the command-name token under `vars.<name>` in `when` clauses -- users could otherwise expect the captured prefix (kubectl + global flags) since `<flag:name>` does capture every absorbed value constraint(matcher): only the `steps` cell is shared across k trials; `after_double_dash` / `var_captures` / `flag_group_captures` must still be re-initialised per k because each k is an independent trial and any state leakage would cross-contaminate sibling alternatives
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #334 +/- ##
==========================================
- Coverage 89.04% 88.75% -0.29%
==========================================
Files 53 53
Lines 11871 12131 +260
==========================================
+ Hits 10570 10767 +197
- Misses 1301 1364 +63
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review
This pull request introduces a new pattern type for definitions.vars, enabling reusable command-prefix patterns that expand inline within rule patterns. The implementation includes matching engine updates for recursive expansion, prefix consumption logic, and validation to prevent nested placeholders or invalid usage in optional groups. Documentation and integration tests are also provided. I have no feedback to provide.
intent(releases): release notes follow the convention of suffixing each entry's heading with the PR link, e.g. "### Feature ([no.NNN](https://github.com/fohte/runok/pull/NNN))". The pattern-type entry was missing this link, leaving readers unable to trace the change back to its PR.
…n var section intent(placeholders): placeholders.md documents what a placeholder expands into syntactically; "which commands a particular rule pattern matches" is rule-evaluation behaviour and belongs elsewhere. Replace the multi-rule + match-result table with a single before/after expansion sentence so the section stays focused on the placeholder's semantics
Purpose
kubectl) and reuse it across multiple rules<flag:name>requires "at least one occurrence", so commands without any flag are not covered<opts>does not absorb space-separated long-flag values (e.g.--context foo);foois left as a positionaldefinitions.vars(literal/path) only accepts a single token sequence or a canonicalised path, not pattern syntax such as[--flag *]Approach
Add a new
patternvalue todefinitions.vars[<name>].type. Each value is written using the regular rule-pattern syntaxA
<var:name>reference inlines the matchingpatternvalue at the position where it appearsReject the following at config validation time
<cmd>,<opts>,<vars>,<var:...>,<path:...>,<flag:...>)[<var:pattern-typed>](a pattern-typed var inside an optional group)Example:
With this definition, the first rule matches every one of
kubectl get pods,kubectl --context foo get pods, andkubectl --kubeconfig ~/.kube/work --context prod get pods -ADesign decisions
How to express reusable global-flag prefixes
patterntype todefinitions.varsand expand the value as a rule pattern fragment in placeliteral/path. Rule authors keep using<var:name>without caring whether it ispattern<flag:name>with a "zero-or-more" modeflag_groups<opts>to absorb space-separated long-flag valuesWhether pattern values can nest other placeholders
<cmd>,<opts>,<vars>,<var:...>,<path:...>, and<flag:...>inside pattern values at validation time<flag:>and similar placeholders inside a pattern var requires a separate future designparsed_pattern_vars) design no longer holds