Skip to main content

iris_abi/
lib.rs

1//! The guest and host ABI for iris self-decoding datasets.
2//!
3//! The ABI is the only surface in iris that can ossify, so it is shaped like a wire protocol: length prefixed records, negotiated capabilities, and a defined way for either side to refuse politely.
4//!
5//! This crate is `no_std` and has no dependencies at all, and both of those are checked by CI. It
6//! ends up inside every decoder anyone writes, so anything it pulls in, everyone pays for.
7//!
8//! # How a conversation goes
9//!
10//! The host sends a [`Hello`] saying which ABI version it speaks and what it can do. The decoder
11//! answers with a [`HelloAck`] saying what it needs. [`negotiate`] compares the two and either
12//! produces an [`Agreement`] or a [`Refusal`] that names the capability that was missing. After
13//! that the host sends [`ScanRequest`] records, the decoder sends [`RangeRequest`] records back when
14//! it needs bytes it has not been given, and the rows come back as [`Batch`] records.
15//!
16//! ```
17//! use iris_abi::{Capability, CapabilitySet, Hello, HelloAck, negotiate};
18//!
19//! let host = Hello {
20//!     abi_major: iris_abi::ABI_MAJOR,
21//!     abi_minor: iris_abi::ABI_MINOR,
22//!     window_bytes: 64 << 20,
23//!     max_batch_rows: 8192,
24//!     offered: CapabilitySet::new()
25//!         .with(Capability::REQUIRE_RANGE)
26//!         .with(Capability::SLIDING_WINDOW),
27//!     source_bytes: 4 << 30,
28//! };
29//! let decoder = HelloAck {
30//!     abi_major: iris_abi::ABI_MAJOR,
31//!     abi_minor: iris_abi::ABI_MINOR,
32//!     required: CapabilitySet::new().with(Capability::REQUIRE_RANGE),
33//!     optional: CapabilitySet::new().with(Capability::PROJECTION),
34//!     decoder_id: "example",
35//! };
36//!
37//! let agreed = negotiate(&host, &decoder).expect("the host offers what the decoder needs");
38//! assert!(agreed.has(Capability::REQUIRE_RANGE));
39//! // The host offers sliding windows but this decoder never asked for them, so it is not on.
40//! assert!(!agreed.has(Capability::SLIDING_WINDOW));
41//! // The decoder would like projection pushdown but this host does not do it, and that is fine
42//! // because it was optional.
43//! assert!(!agreed.has(Capability::PROJECTION));
44//! ```
45//!
46//! # What is allowed to change
47//!
48//! Adding a field to the end of a record is allowed and does not bump that record's version, because
49//! a reader that does not know about the field steps over it. Adding a record is allowed, because a
50//! reader that does not know a tag steps over the whole record. Adding a capability is allowed,
51//! because a side that does not offer it says so and the other side decides what to do.
52//!
53//! Removing a field, reordering fields, changing what a field means, or changing what a capability
54//! bit means are all breaking, and all of them are supposed to be loud rather than silent. The
55//! tests in `tests/forward_compat.rs` hold the compatible half of that line.
56
57#![no_std]
58// Nothing in here parses untrusted bytes with a pointer. If a future change needs to, it needs a
59// conversation first, because this crate is the one piece of iris that runs inside every decoder
60// anybody writes.
61#![forbid(unsafe_code)]
62
63pub mod caps;
64pub mod error;
65pub mod handshake;
66pub mod message;
67pub mod record;
68pub mod wire;
69
70pub use caps::{Capability, CapabilitySet};
71pub use error::{Error, Result};
72pub use handshake::{Agreement, negotiate};
73pub use message::{
74    Batch, BufferRef, Buffers, Hello, HelloAck, Message, Node, Nodes, Projection, RangeRequest,
75    Refusal, RefusalReason, ScanRequest,
76};
77pub use record::{Header, Tag};
78pub use wire::{Reader, Writer};
79
80/// The major ABI version this build speaks.
81///
82/// Zero means the record layouts are still allowed to move. When this goes to one it means the
83/// layouts in [`message`] are frozen, and freezing them is a milestone with a written compatibility
84/// note behind it rather than something that happens because a refactor felt finished.
85pub const ABI_MAJOR: u16 = 0;
86
87/// The minor ABI version this build speaks.
88///
89/// This goes up when a field or a record or a capability is added. Two sides at different minor
90/// versions can always talk to each other, and they settle on the lower of the two.
91pub const ABI_MINOR: u16 = 2;