Skip to content

fix: impl AsCelValue for FieldMask, Timestamp, Duration WKTs#6

Merged
jrandolf merged 1 commit into
mainfrom
fix/ascelvalue-wkt
May 14, 2026
Merged

fix: impl AsCelValue for FieldMask, Timestamp, Duration WKTs#6
jrandolf merged 1 commit into
mainfrom
fix/ascelvalue-wkt

Conversation

@jrandolf
Copy link
Copy Markdown
Contributor

Summary

Field-level predefined CEL rules on google.protobuf.FieldMask, Timestamp, and Duration failed to compile because the codegen plugin emits ::protovalidate_buffa::cel::AsCelValue::as_cel_value(inner) for the field value, but no AsCelValue impl existed for those WKT structs.

For example a downstream proto like

extend buf.validate.FieldMaskRules {
  repeated string mutable_paths = 80000001 [
    (buf.validate.predefined).cel = {
      id: "field_mask.mutable_paths"
      message: "update_mask paths must be one of the allowed values"
      expression: "this.paths.all(p, rule.exists(a, p == a || p.startsWith(a + '.')))"
    }
  ];
}

// ...

google.protobuf.FieldMask update_mask = 2 [
  (buf.validate.field).field_mask = {
    [pkg.mutable_paths]: ["display_name", "icon"]
  }
];

failed with the trait bound FieldMask: AsCelValue is not satisfied. Same shape for Timestamp- and Duration-typed fields.

Fix

Three new impl AsCelValue blocks in protovalidate-buffa/src/cel.rs:

  • FieldMask -> CEL Map { paths: List<String> }, so this.paths.all(p, ...), size(this.paths), etc. work.
  • Timestamp -> native CEL Timestamp via the existing timestamp_from_secs_nanos helper, so this < timestamp(...), this < now, int(this) work.
  • Duration -> native CEL Duration via the existing duration_from_secs_nanos helper, so this > duration('0s') works.

Pulls buffa-types = "0.5" into the runtime crate to reference the WKT struct definitions. No changes to the codegen plugin or to public API beyond the new impls.

Test plan

  • cargo test -p protovalidate-buffa — new tests/wkt_cel.rs exercises each impl through the real CelConstraint::eval path (this.paths.all(...), size(this.paths), this > duration('0s'), this < timestamp('9999-12-31T23:59:59Z'), this < now, sub-second nanos resolution).
  • cargo test --workspace --all-targets — no regressions.
  • cargo clippy --workspace --all-targets -- -D warnings — clean.
  • cargo fmt --all -- --check — clean.
  • Downstream verification: bump consumer to released version, swap a message-level update_mask CEL for a field-level predefined rule on a google.protobuf.FieldMask field, confirm it compiles.

Field-level predefined CEL rules on `google.protobuf.FieldMask`,
`Timestamp`, and `Duration` failed to compile because the plugin emits
`AsCelValue::as_cel_value(inner)` for the field value but no
`AsCelValue` impl existed for those WKT structs.

Add the three impls in `protovalidate-buffa/src/cel.rs`:

- `FieldMask` -> CEL `Map { paths: List<String> }`, so expressions
  like `this.paths.all(p, ...)` work.
- `Timestamp` -> native CEL `Timestamp` via `timestamp_from_secs_nanos`.
- `Duration` -> native CEL `Duration` via `duration_from_secs_nanos`.

Pulls `buffa-types = "0.5"` into the runtime crate for the WKT struct
definitions, and adds an integration test exercising each impl through
the real `CelConstraint::eval` path.
@jrandolf jrandolf merged commit c454d46 into main May 14, 2026
7 checks passed
@jrandolf jrandolf deleted the fix/ascelvalue-wkt branch May 14, 2026 23:26
@jrandolf jrandolf mentioned this pull request May 14, 2026
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.

1 participant