Schema Validation

Validation is the core pillar of Adapters. You can build validation schemas programmatically using builder types, or let the procedural macros generate them automatically.


Declarative Attributes (#[derive(Schema)])

When deriving Schema on your structures, you can use the #[schema(...)] attribute to specify validation constraints. The macro supports a rich set of rules:

String Constraints

  • min_length = <usize>: The string must contain at least N characters.
  • max_length = <usize>: The string must contain at most M characters.
  • non_empty: The string cannot be empty (equivalent to min_length = 1).
  • email: Enforces a standard RFC 5322-compliant email format check.
  • url: Enforces a valid absolute URL format check.
  • regex = "<pattern>": Matches the string against a custom regular expression.

Numeric Constraints

  • min = <number>: Value must be greater than or equal to N.
  • max = <number>: Value must be less than or equal to M.
  • positive: Enforces that the number is strictly greater than zero (> 0).
  • negative: Enforces that the number is strictly less than zero (< 0).
  • non_zero: Fails validation if the number is exactly 0.

Structural Controls

  • strict: Enforces strict type checking. When true, types like numbers will not be coerced from strings.
  • optional: Declares the field as optional (permits Null values).
  • default = <expression>: Applies a default value if the key is missing in the source payload.

Programmatic Schema Building

If you need to construct schemas dynamically at runtime, use our highly expressive builder APIs:

#![allow(unused)]
fn main() {
use adapters::prelude::*;
use adapters::schema::{ObjectSchema, StringSchema, IntegerSchema};

let schema = ObjectSchema::new()
    .field("username", StringSchema::new().required().non_empty().alphanumeric())
    .field("age", IntegerSchema::new().required().min(18).max(99).positive())
    .field("balance", IntegerSchema::new().required().non_zero())
    .strict(); // Rejects unknown object keys

// Validate a dynamic Value representation
let payload = Value::Null; // or Value::Object(...)
let result = schema.validate(&payload, "root");
}

Nested Schema Validation

Adapters natively supports recursive validation of complex nested structures. When a structure derives Schema, its schema definition incorporates the schema of any sub-structures that also implement SchemaProvider.

For example, when validating a parent struct like User, any nested objects (e.g. Address) will be fully validated against their own schemas. Any validation failures in the nested child are reported with correct dot-notation paths (e.g., address.city or address.zip_code).

#![allow(unused)]
fn main() {
use adapters::prelude::*;

#[derive(Schema, Debug)]
struct Address {
    #[schema(min_length = 3)]
    city: String,
    country: String,
}

#[derive(Schema, Debug)]
struct User {
    name: String,
    address: Address, // Automatically delegates validation to Address::schema()!
}
}

If you attempt to parse a payload where address.city is only two characters long, the validation engine will fail and report address.city as the failing field path.