Skip to content

(10) feat(match-action): MatchKey trait + derive + bolero generators#1574

Open
daniel-noland wants to merge 2 commits into
pr/daniel-noland/fixed-size-lookupfrom
pr/daniel-noland/match-action
Open

(10) feat(match-action): MatchKey trait + derive + bolero generators#1574
daniel-noland wants to merge 2 commits into
pr/daniel-noland/fixed-size-lookupfrom
pr/daniel-noland/match-action

Conversation

@daniel-noland
Copy link
Copy Markdown
Collaborator

@daniel-noland daniel-noland commented May 31, 2026

Stack (10). Base: pr/daniel-noland/fixed-size-lookup.

The match-action core: the trait + derive macro that everything keys off, plus
its property-test generators.

  • feat(match-action): MatchKey trait + #[derive(MatchKey)] proc-macro.
  • feat(match-action): bolero FieldHit / FieldMiss generators.

Review focus: derive semantics and generator coverage, in isolation from ACL.

Review stack (merge bottom -> top):

Lands the match-action key vocabulary plus its derive.  Backends in
downstream PRs consume the result via a structural FIELD_SPECS view
and a byte-packing as_key_into(); the bolero property-test
generators land next as their own feature gate.

match-action/src/:

- lib.rs publishes the FieldKind enum (Prefix / Mask / Range /
  Exact), the FieldSpec layout record (name / kind / size / offset),
  and the MatchKey trait (N, KEY_SIZE, field_specs(),
  as_key_into()).  Trait methods take slices rather than
  Self::N-sized arrays because Self::N in a trait fn needs unstable
  generic_const_exprs; the derive emits sized inherent helpers on
  concrete types instead.
- field.rs is a thin re-export of FixedSize from the fixed-size
  crate, so consumers can refer to it via match_action::FixedSize.
- predicate.rs holds the type-erased FieldPredicate form (Prefix,
  Mask, Range, Exact variants over FieldBytes) plus the Erased
  backend marker used by the reference oracle.
- rule.rs defines the four kind-typed *Spec wrappers (PrefixSpec,
  MaskSpec, RangeSpec, ExactSpec), the Backend / Accepts /
  IntoBackendField / IsUniversal traits, and the RuleField envelope
  carrying name / spec.

match-action-derive (proc-macro crate) emits, from a struct annotated
with #[prefix] / #[mask] / #[range] / #[exact]:

- the MatchKey impl,
- a parallel <Name>Rule struct holding the typed *Spec data,
- an inherent FIELD_SPECS const for compile-time inspection by
  downstream backends, and
- (for non-generic structs) an inherent as_key() -> [u8; KEY_SIZE]
  for ergonomic byte packing.

tests/derive_roundtrip.rs exercises a 5-tuple-shaped key covering
all four predicate kinds: asserts N, KEY_SIZE, the field_specs()
content, and that as_key_into / as_key produce the expected
big-endian byte layout (with declared field ordering preserved).

just fmt; cargo check --workspace --all-targets passes.
Adds property-test generators over the rule wrappers, behind a new
`bolero` feature gate so the bolero dep stays out of the default
build graph.  Downstream consumers (acl property tests, future
cascade Upsert-laws harnesses) enable the feature in their
dev-deps and draw random matching / non-matching packets for any
single rule.

match-action/src/generator.rs:

- FieldHit / FieldMiss TypeGenerators yielding bytes that satisfy /
  violate a given field predicate.  Cover all four predicate kinds:
  Prefix (any address sharing the rule's high-order bits / one with
  flipped bits in that prefix), Mask (value bits match / a flipped
  in-mask bit), Range (uniform draw in [min, max] / outside the
  bounds), Exact (the value / a different value).
- Miss generators skip universal predicates (range covering all
  values, mask of all zeros, prefix length zero, etc.); the
  derive-emitted IsUniversal check on <Name>Rule lets callers detect
  whole rules that have an empty miss set.

match-action/src/lib.rs picks up #[cfg(feature = "bolero")] gates on
the generator mod + its FieldHit / FieldMiss re-exports.

match-action/Cargo.toml grows the bolero optional dep and the
`bolero` feature, which also turns on the matching forwarded feature
on match-action-derive (a no-op today, kept for future emit changes
in the derive).  match-action-derive/Cargo.toml mirrors the
`bolero` feature declaration so cargo can forward through.

just fmt; cargo check --workspace --all-targets and
cargo check -p dataplane-match-action --features bolero pass.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the match-action core crate and derive macro, providing typed field/rule specs, erased predicates, MatchKey derivation, and bolero-based field generators for future ACL and cascade layers.

Changes:

  • Introduces dataplane-match-action with MatchKey, field specs, predicates, rule specs, and generator support.
  • Adds dataplane-match-action-derive proc macro for deriving key layout and companion rule structs.
  • Adds workspace registration, lockfile entries, and derive/generator roundtrip tests.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Cargo.toml Registers match-action crates and package metadata.
Cargo.lock Adds lock entries for the new crates.
match-action/Cargo.toml Defines crate dependencies and feature flags.
match-action/src/lib.rs Exposes public match-action API types and traits.
match-action/src/field.rs Re-exports FixedSize.
match-action/src/rule.rs Adds rule field specs and universal checks.
match-action/src/predicate.rs Adds erased predicate matching and conversions.
match-action/src/generator.rs Adds bolero hit/miss generators for field predicates.
match-action/tests/derive_roundtrip.rs Tests derive layout, packing, rules, and generics.
match-action-derive/Cargo.toml Defines proc-macro crate dependencies/features.
match-action-derive/src/lib.rs Implements #[derive(MatchKey)].


#[test]
fn ipv6_prefix_hits_on_high_chunk() {
let spec = PrefixSpec::new("2001:db8::".parse::<Ipv6Addr>().unwrap(), 32);
impl_specs_for!(u32, u32, high_mask_u32);
impl_specs_for!(u64, u64, high_mask_u64);
impl_specs_for!(Ipv4Addr, u32, high_mask_u32);
impl_specs_for!(Ipv6Addr, u128, high_mask_u128);
Comment on lines +293 to +296
let lo = be_to_u32(min);
let hi = be_to_u32(max);
let v = (lo..=hi).generate(d)?;
Some(u32_to_be(v, min.len()))
Copy link
Copy Markdown
Contributor

@mvachhar mvachhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the use of the macro, I hate the definition since it is really hard to understand, but that is the nature of these macros I suppose.

Copilot has some things that should be addressed.

Also, your FiveTuple doc example should be ThreeTuple or expanded to an actual FiveTuple.

I also feel like the macro testing is light, but that might be ok if we are mainly just using a single data type for most of dataplane. We can fix bugs later as we add more data types, assuming bugs crop up.

Comment thread match-action/src/lib.rs Outdated
//!
//! ```ignore
//! #[derive(MatchKey)]
//! struct FiveTuple {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a FiveTuple, it is a ThreeTuple match.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants