Validate a typed record

The record fetched in Chapter 1 came from the network. We now use a bundled fixture so that validation has a stable input and a reproducible result.

An ATProto lexicon defines a record's fields and constraints. idiolect-records generates a Rust type for each dev.idiolect.* lexicon and dispatches incoming JSON by its namespaced identifier (NSID).

Run the valid case

From the repository root, run the tutorial validator:

cargo run --quiet \
  --manifest-path scripts/publish-tutorial-lens/Cargo.toml \
  --bin validate-tutorial-record
validated dev.idiolect.dialect: ud-en-2026

The executable serializes the bundled Dialect fixture to JSON and sends it through the runtime dispatcher:

let value = serde_json::to_value(examples::dialect())?;
let record = decode_record(&Dialect::nsid(), value)?;

Dialect::nsid() selects the generated type. Deserialization then checks the required fields and the field formats represented by that type. The returned AnyRecord::Dialect value is safe to pass to code that expects a typed dialect.

See a rejection

The same executable can remove the required createdAt field before decoding:

cargo run --quiet \
  --manifest-path scripts/publish-tutorial-lens/Cargo.toml \
  --bin validate-tutorial-record -- --invalid
rejected invalid dev.idiolect.dialect record: record deserialization failed: missing field `createdAt`

Validation occurs here: malformed JSON is rejected before application logic receives a record. Chapter 3 applies the live lens from Chapter 1 to a small source record.