Skip to main content

printobserver_types/
lib.rs

1//! `printobserver-types`.
2//!
3//! Owns: the vocabulary every domain and every client must agree on, and only
4//! that — the identity rule ([`ids`]: the exported [`identifier!`] macro, its
5//! error, the UUID-v7 rule, and the three identifiers the envelope reaches),
6//! the representation rules ([`Timestamp`], [`RawBytes`], [`FileName`],
7//! [`Reported`] and [`Range`]), the event log's envelope ([`event`], with the
8//! image handle [`ImageRef`] it carries), and the schema toolkit
9//! ([`contract`]). It holds data and total functions over that data, never
10//! I/O.
11//!
12//! A type belongs here only if adding or changing one domain's concept does
13//! not require editing it. The event log is the case in point: [`event`]
14//! declares the envelope — an open [`EventKind`] name and an opaque payload —
15//! and **no kind**. Each kind is declared by the domain that owns the event, as
16//! a payload type implementing [`EventPayload`] under a `KIND` of its own, so
17//! that a domain adding an event edits its own crate and nothing central. The
18//! same holds for a domain's own shapes: a provider's wire format lives in its
19//! adapter, a port's vocabulary in the port, and the supervision domain's
20//! records — the print, the action, the intervention, the policy, the image —
21//! and the identifiers it mints live in the supervision domain, each declared
22//! into the schema set through [`TypeContract::of`] rather than through a
23//! list here.
24//!
25//! May depend on: no crate of this workspace. It is the root of the graph, so a
26//! type it does not hold is a type the rest of the workspace cannot agree on.
27//!
28//! # One authoritative source per restated fact
29//!
30//! The types here are declared once and imported by every consumer rather than
31//! re-declared. The JSON Schemas the clients and the agent's structured answer
32//! are validated against are **generated from these types** by the
33//! `printobserver-types:schemas` graph target, which the gate runs: a schema
34//! checked into `schemas/` that no longer matches what these types generate
35//! fails that target. Nothing transcribes a schema by hand, and nothing
36//! restates a field.
37//!
38//! [`contract`] is where that generation reads the declared set from: it is
39//! this crate's own claim about which types it declares and what a canonical
40//! value of each looks like.
41//!
42//! # The representations, stated once
43//!
44//! * **Identifiers.** Every identifier this system mints is a UUID version 7,
45//!   minted by the store, held as a distinct newtype per record, and serialized
46//!   as its lowercase hyphenated string. See [`ids`]; a domain declares its own
47//!   through the [`identifier!`] macro.
48//! * **Numbers.** Every temperature, factor, multiplier, percentage and
49//!   fraction is a 64-bit float. Every byte count and every integer identifier
50//!   an external system supplies is a 64-bit signed integer. Every duration is
51//!   whole seconds as a 64-bit signed integer.
52//! * **Timestamps.** Every timestamp is an instant in UTC, serialized as an RFC
53//!   3339 string with a zero offset. See [`Timestamp`].
54//! * **Optionality.** An optional field absent means *the source did not report
55//!   it*, and is never rendered as a zero, an empty string, or a default.
56//!   Absent serializes as absent and parses back as absent.
57//! * **Reported numbers.** A numeric field a domain declares a plausibility
58//!   range for is typed [`Reported<f64>`]. See [`reported`] for the whole rule,
59//!   including why a non-finite value is out of range and why one is refused
60//!   on emission; the ranges themselves are the declaring domain's.
61//!
62//! # Why `serde`, `schemars` and `serde_json` are re-exported
63//!
64//! The three port crates depend on this crate and on nothing else, and each of
65//! them declares request and answer shapes that cross a process boundary and so
66//! must derive the same traits these types derive. They reach the derive macros
67//! through [`serde`], [`schemars`] and [`serde_json`] here rather than by
68//! declaring a dependency of their own.
69
70pub mod contract;
71pub mod event;
72pub mod file_name;
73pub mod ids;
74pub mod image;
75pub mod raw;
76pub mod reported;
77pub mod timestamp;
78
79pub use contract::{
80    EVENT_KIND_MARKER, Sample, TypeContract, WireField, declared, event_schema_of, schema_of,
81    wire_fields,
82};
83pub use event::{
84    EventBody, EventKind, EventKindError, EventPayload, EventRecord, EventSource, KIND_PATTERN,
85};
86pub use file_name::{FileName, FileNameError, FileNameRefusal, SEPARATORS};
87pub use ids::{EventId, IdentifierError, ImageId, PrintId};
88pub use image::ImageRef;
89pub use raw::RawBytes;
90pub use reported::{Range, Reported};
91pub use timestamp::{Timestamp, TimestampError};
92
93pub use schemars;
94pub use serde;
95pub use serde_json;