# Schemas

> How a scope's struct describes itself, and how its declarations become its starting value.

Every scope is a Rust struct that implements `Schema`. The schema does two jobs. It describes the struct's shape as data, so the store can claim and name fields and so a validator can check what views read. And it builds the struct's starting value from the same declarations, so the two can never disagree.

> **No derive yet.** A `#[derive]` for `Schema` exists, but it lives in a separate, unpublished crate. Until it moves into Carte you write the impl by hand, as on this page. TODO: the derive's name and attribute syntax once it ships.

## The trait

```rust
pub trait Schema {
    fn reflect() -> Kind;
    fn at_mount(declared: Option<&Lit>) -> Self where Self: Sized;
    fn type_name() -> Option<&'static str> where Self: Sized { None }
}
```

- `reflect` returns the type's shape.
- `at_mount` returns the value a field of this type holds when its scope mounts. `declared` is the starting value the schema declared, or `None` if it declared nothing.
- `type_name` is only for recursive types. See below.

## A hand-written schema

```rust
use carte::schema::{Field, Kind, Lit, Schema};

#[derive(Clone, Debug, PartialEq)]
struct Editor {
    title: String,
    dirty: bool,
    words: usize,
}

impl Schema for Editor {
    fn reflect() -> Kind {
        Kind::Record(vec![
            Field::new("title", Kind::Str).starting_at(Lit::Str("Untitled".into())),
            Field::new("dirty", Kind::Bool),
            Field::new("words", Kind::Int),
        ])
    }
    fn at_mount(_: Option<&Lit>) -> Self {
        Self {
            title: String::at_mount(Some(&Lit::Str("Untitled".into()))),
            dirty: bool::at_mount(None),
            words: usize::at_mount(None),
        }
    }
}
```

The field names in `reflect` must match the struct's fields, because producers set fields by name. The `set!` macro takes the name and the field from one token, so a producer cannot set one under the other's name. See [Producers and the tick](/docs/state/producers).

By hand, you also have to keep `reflect` and `at_mount` in step. Above, `"Untitled"` is written twice. This is the duplication the derive removes: it emits both from one attribute on the field.

## The declaration is the starting value

`Store::provides` takes no value. It builds the starting value by calling `at_mount(None)` on the type, and every mount starts from a copy of it. So "the schema says `title` starts as Untitled" and "the scope mounted with `title` Untitled" are one fact, not two that have to agree.

A starting value matters because a silent zero can look exactly like a bug: a fade that starts at `0.0` draws perfectly and shows nothing. So `Field` records whether anybody chose the value:

- `Initial::Declared(lit)` when the schema said so, with `starting_at(lit)`.
- `Initial::Implied(lit)` when it fell through to the kind's zero: an empty string, `0`, `false`, an enum's first name.

A starting value a type cannot hold, such as a string literal for an integer field, panics at mount and names both. It is a startup bug with a declaration behind it.

## Kinds

`Kind` is the shape vocabulary:

| Kind | Rust types with a built-in `Schema` impl |
| --- | --- |
| `Str` | `String` |
| `Int` | `i8` to `i64`, `isize`, `u8` to `u64`, `usize` |
| `Float` | `f32`, `f64` |
| `Bool` | `bool` |
| `List(kind)` | `Vec<T>` |
| `Map(kind)` | `BTreeMap<K, V>`, `HashMap<K, V>` (only the value is part of the shape) |
| `Tuple(kinds)` | Tuples of two to four, and `[T; N]` |
| `Record(fields)` | Your structs |
| `Enum(names)` | Payload-free enums, written by hand |
| `Union(variants)` | Enums with payloads, written by hand |
| `Cycle` | A back-edge in a recursive type |

Use `reflect_of::<T>()` to get a kind from a type, such as `reflect_of::<Vec<(usize, String)>>()`. It is cycle-safe: a type that reaches itself and returns `Some` from `type_name` reflects the back-edge as `Kind::Cycle`.

## Field declarations

`Field::new(name, kind)` makes a required field with an implied starting value and exact precision. Three builders add declarations:

- `starting_at(lit)` declares the starting value.
- `absent_when(sentence)` says the field may be absent, and when, as one sentence: "until the list has loaded". Optionality is stated on the field, not implied by a wrapper.
- `at_grain(step)` declares the field's precision. `set_num!` floors values to it. See [Producers and the tick](/docs/state/producers).

> TODO: there is no `Schema` impl for `Option<T>` in the crate today. How an absent field is represented in the Rust struct is not settled.

## Structural comparison

`Kind::compare` checks two shapes structurally. Field names and kinds must match. Type names are never compared, and field order does not matter. Presence, starting values and grain are not compared either, because two providers of the same shape may legitimately differ there. A mismatch is a `Mismatch` whose `field()` is the dotted path to where the shapes stop agreeing, and whose `difference()` says how.

`Kind::field_at("a.b")` resolves a dotted path through records. It refuses to step into a list, a map, a union or a back-edge: a collection is one field.

## The printed form

`Kind` implements `Display`, printing a small schema language. It is what an error quotes when it says what shape it wanted. Declared starting values print after `=`, grains after `@`:

```text
{
    title: str = "Untitled",
    dirty: bool,
    words: int,
}
```

It only prints. Nothing parses it back.

## Next

[Producers and the tick](/docs/state/producers): writing those fields.
