Part of #936. Follows #985 (slice 4, shipped).
Background
Slice 4 (#985) shipped via schema(N) literal-value dispatch, deliberately
narrowing out range patterns: design/bynk-design-notes.md's original
worked example uses them (via schema(2..) for "version 2 or later"), but
no range-pattern syntax existed anywhere in bynk at the time, so ranges
were split to this unfiled slice.
This proposal adds the range half: via schema(v..), via schema(..v),
via schema(v1..v2), and an explicit via schema(_) wildcard (redundant
with omitting the clause entirely, but useful for symmetry once ranges
exist — a service listing several sibling via schema(...) clauses across
a family of subscribers may want every case written explicitly rather than
one left as "no clause").
Design
Bounds are inclusive on both ends, matching the one existing integer-
range concept in bynk: the InRange(lo, hi) refinement predicate lowers to
receiver >= lo && receiver <= hi (bynk-emit/src/emitter.rs), described
as "must be in range [lo, hi]" — bracket notation, inclusive. via schema(v1..v2) follows the identical convention: v1 <= schemaVersion <= v2. via schema(v..) is schemaVersion >= v; via schema(..v) is
schemaVersion <= v.
Grammar: extends schema_dispatch_clause's argument position (today:
optional("-") number_literal only) to also admit:
number_literal ".." (open-above)
".." number_literal (open-below)
number_literal ".." number_literal (closed)
"_" (wildcard, matches any version — same as omitting the clause)
Reuses the .. (DotDot) token slice 1 already added (previously only a
record-pattern's rest marker) and the _ (Underscore) token already used
by match-pattern wildcards — no new lexer tokens.
AST: SchemaVersionPattern (today: Literal(i64) only) gains three
variants — OpenAbove(i64), OpenBelow(i64), Closed(i64, i64) — plus
Wildcard. Additive to the enum, per slice 4's own ADR naming this as the
expected extension point.
Checker: each bound independently must be a positive Int literal
(the existing bynk.event.bad_schema_dispatch rule, unchanged). A new
check for Closed(lo, hi): lo > hi is an always-empty range — reject it
as malformed (mirrors InRange's own inconsistent-bounds check, "exactly
one error" per its own test) rather than silently accepting a clause that
can never match.
Emitter: the guard's comparison operator changes per variant —
=== (Literal, unchanged), >= (OpenAbove), <= (OpenBelow), >= lo && <= hi (Closed), and no guard at all for Wildcard (identical to the
no-clause case — the synthetic-envelope-parameter plumbing from slice 4
still applies whenever any via schema(...) is written, including
_, in case a sibling subscriber in the same family relies on it being
present for a different variant).
No new ambiguity check. Slice 4's own policy continues unchanged:
sibling subscribers with overlapping or gapped range coverage are not
diagnosed, for the identical reason slice 1's payload pattern and slice
4's literal dispatch aren't.
Verification
Positive behavioural test extending slice 4's own (events_schema_dispatch_ behaviour.rs): compile at three or more schema versions, assert each
lands in the correct one of via schema(1), via schema(2..4),
via schema(5..), via schema(_) (four sibling subscribers, one compile
per version tested). Negative fixture for the new lo > hi diagnostic.
Regression: existing literal-only fixtures/tests unaffected.
Part of #936. Follows #985 (slice 4, shipped).
Background
Slice 4 (#985) shipped
via schema(N)literal-value dispatch, deliberatelynarrowing out range patterns:
design/bynk-design-notes.md's originalworked example uses them (
via schema(2..)for "version 2 or later"), butno range-pattern syntax existed anywhere in bynk at the time, so ranges
were split to this unfiled slice.
This proposal adds the range half:
via schema(v..),via schema(..v),via schema(v1..v2), and an explicitvia schema(_)wildcard (redundantwith omitting the clause entirely, but useful for symmetry once ranges
exist — a service listing several sibling
via schema(...)clauses acrossa family of subscribers may want every case written explicitly rather than
one left as "no clause").
Design
Bounds are inclusive on both ends, matching the one existing integer-
range concept in bynk: the
InRange(lo, hi)refinement predicate lowers toreceiver >= lo && receiver <= hi(bynk-emit/src/emitter.rs), describedas "must be in range
[lo, hi]" — bracket notation, inclusive.via schema(v1..v2)follows the identical convention:v1 <= schemaVersion <= v2.via schema(v..)isschemaVersion >= v;via schema(..v)isschemaVersion <= v.Grammar: extends
schema_dispatch_clause's argument position (today:optional("-") number_literalonly) to also admit:number_literal ".."(open-above)".." number_literal(open-below)number_literal ".." number_literal(closed)"_"(wildcard, matches any version — same as omitting the clause)Reuses the
..(DotDot) token slice 1 already added (previously only arecord-pattern's rest marker) and the
_(Underscore) token already usedby match-pattern wildcards — no new lexer tokens.
AST:
SchemaVersionPattern(today:Literal(i64)only) gains threevariants —
OpenAbove(i64),OpenBelow(i64),Closed(i64, i64)— plusWildcard. Additive to the enum, per slice 4's own ADR naming this as theexpected extension point.
Checker: each bound independently must be a positive
Intliteral(the existing
bynk.event.bad_schema_dispatchrule, unchanged). A newcheck for
Closed(lo, hi):lo > hiis an always-empty range — reject itas malformed (mirrors
InRange's own inconsistent-bounds check, "exactlyone error" per its own test) rather than silently accepting a clause that
can never match.
Emitter: the guard's comparison operator changes per variant —
===(Literal, unchanged),>=(OpenAbove),<=(OpenBelow),>= lo && <= hi(Closed), and no guard at all forWildcard(identical to theno-clause case — the synthetic-envelope-parameter plumbing from slice 4
still applies whenever any
via schema(...)is written, including_, in case a sibling subscriber in the same family relies on it beingpresent for a different variant).
No new ambiguity check. Slice 4's own policy continues unchanged:
sibling subscribers with overlapping or gapped range coverage are not
diagnosed, for the identical reason slice 1's payload pattern and slice
4's literal dispatch aren't.
Verification
Positive behavioural test extending slice 4's own (
events_schema_dispatch_ behaviour.rs): compile at three or more schema versions, assert eachlands in the correct one of
via schema(1),via schema(2..4),via schema(5..),via schema(_)(four sibling subscribers, one compileper version tested). Negative fixture for the new
lo > hidiagnostic.Regression: existing literal-only fixtures/tests unaffected.