Kinds and fields
A kind is a type of thing: host, user, environment. yen has no built-in kinds. You declare each one.
The fields of a kind are the data of each of its instances.
# A kind declares the fields of its instances. Every field has a type.{ yen, lib, ... }:{ yen.kinds.host.fields = { addr = lib.types.str; cores = lib.types.int; };}An instance is yen.<kind>.<name>. The attribute name is the instance name. Any file can declare instances.
# Instances: `yen.<kind>.<name>`. The name is the attribute name.{ yen, ... }:{ yen.host.web = { addr = "10.0.0.1"; cores = 4; }; yen.host.db = { addr = "10.0.0.2"; cores = 16; };}An aspect reads the fields. Here, each host gives one line to a report sink on the fleet
(Sinks explains how):
# An aspect reads the fields of the nearest host.{ yen, ... }:{ yen.aspects.report = { at = yen.kinds.host; report = { host, ... }: { ${host.name} = "${host.addr} (${toString host.cores} cores)"; }; };}{ db = "10.0.0.2 (16 cores)"; web = "10.0.0.1 (4 cores)";}Fields are strict
Section titled “Fields are strict”A missing field, an unknown field and a value that fails its type are error records.
Evaluation does not stop. The read gives null, and the errors query lists the problem.
Add this file to the example:
# A typo in a field name, and a string where an int goes.{ yen, ... }:{ yen.host.cache = { adr = "10.0.0.3"; cores = "8"; };}r.errors now has three records. near names the field you probably meant:
[ { class = "fixable"; code = "missing"; error = "missing addr"; fix = "set host:cache.addr"; from = "host:cache"; path = [ "addr" ]; want = "str"; } { class = "fixable"; code = "unknown-field"; error = "unknown field adr"; fix = "declare yen.kinds.host.fields.adr, or use a declared field"; from = "host:cache"; near = [ "addr" ]; path = [ "adr" ]; } { class = "fixable"; code = "check"; error = "cores fails its check"; fix = "give cores a value of type int"; from = "host:cache"; got = "8"; path = [ "cores" ]; want = "int"; }]{ addr = { type = "str"; }; cores = { type = "int"; };}