Where Clauses

Where clauses add validation constraints to fields. They run automatically on encode and decode — if the data violates the constraint, you get an error and nothing goes on the wire.

Basic Syntax

message Player {
    health @0 : u8 where value >= 0 && value <= 100
    name   @1 : string where len(value) >= 1 && len(value) <= 32
    level  @2 : u16 where value in 1..999
}

value refers to the field being validated. The expression must evaluate to a boolean.

Comparison Operators

==, !=, <, >, <=, >=. They work like you'd expect.

Logical Operators

&&, ||, !. Standard boolean logic.

message Request {
    code @0 : u16 where value >= 100 && value < 600
    flag @1 : u8 where value == 0 || value == 1 || value == 255
}

Range Expressions

# Inclusive: 0 to 100 (both endpoints included)
health @0 : u8 where value in 0..100

# Exclusive: 0 to 99 (upper bound excluded)
age @1 : u8 where value in 0..<100

Bounds can be constants:

const MaxLevel : u16 = 999

level @0 : u16 where value in 1..MaxLevel

len()

Returns the length of a string, bytes, array, map, or set:

message User {
    username @0 : string where len(value) in 3..32
    tags     @1 : array<string> where len(value) <= 10
    key      @2 : bytes where len(value) == 32
}

Current boundaries

  • Cross-field constraints: where amount <= balance cannot reference other fields. This is backlog work, not a promise for a named release.
  • Regex: where value matches "..." doesn't exist. Use a length check and validate regex in application code.
  • User-defined functions: you can only use the built-in operators and len().

Error Behavior

On constraint violation:

  • Encode: returns EncodeError::ConstraintViolation, nothing written
  • Decode: returns DecodeError::ConstraintViolation, partial data discarded

Invalid data never makes it onto the wire. That's the whole point.