iris_abi/message.rs
1//! The records themselves.
2//!
3//! Each record has a fixed-width part and then a variable-length part, in that order, and new
4//! fields go on the end. A reader reads what it knows and stops; the framing tells it where the
5//! record ended, so stopping early is safe. That single rule is what lets a decoder built against
6//! one version of this crate keep working against a host built against a later one.
7//!
8//! The version number on a record is not a "how new is this" counter. It only goes up when a field
9//! is removed or changes meaning, which is a break, and a break is supposed to be loud.
10
11use crate::caps::{Capability, CapabilitySet};
12use crate::error::{Error, Result};
13use crate::record::{Header, Tag};
14use crate::wire::{Reader, Writer};
15
16/// The host introducing itself.
17#[derive(Clone, Copy, PartialEq, Eq, Debug)]
18pub struct Hello {
19 /// The major ABI version the host speaks. A mismatch here is fatal.
20 pub abi_major: u16,
21 /// The minor ABI version the host speaks. A mismatch here is not fatal.
22 pub abi_minor: u16,
23 /// How many bytes of the source the host is willing to keep visible to the guest at once.
24 ///
25 /// Zero means the host will map the whole source and the decoder never has to think about
26 /// windows.
27 pub window_bytes: u64,
28 /// The largest number of rows the host will ask for in one scan request.
29 pub max_batch_rows: u64,
30 /// What the host can do.
31 pub offered: CapabilitySet,
32 /// How many bytes the source has in total, or zero if the host is not saying.
33 ///
34 /// A decoder that has to find its own footer needs this, and a decoder that reads forwards from
35 /// the start does not, which is why zero is allowed rather than being an error.
36 ///
37 /// This field was appended after the ABI shipped, so it is the first real exercise of the
38 /// grow-at-the-end rule. A host built before it existed writes a `Hello` that ends after
39 /// `offered`, and a decoder built after it reads zero and carries on.
40 pub source_bytes: u64,
41}
42
43impl Hello {
44 /// The layout version of this record.
45 pub const VERSION: u16 = 1;
46
47 /// Writes the record.
48 ///
49 /// # Errors
50 ///
51 /// Returns [`Error::BufferFull`] if the buffer runs out.
52 pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
53 w.record(Tag::HELLO, Self::VERSION, |w| {
54 w.u16(self.abi_major)?;
55 w.u16(self.abi_minor)?;
56 w.u32(0)?;
57 w.u64(self.window_bytes)?;
58 w.u64(self.max_batch_rows)?;
59 w.var_bytes(self.offered.as_bytes())?;
60 w.u64(self.source_bytes)
61 })
62 }
63
64 /// Reads the record from its payload.
65 ///
66 /// # Errors
67 ///
68 /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows, or
69 /// [`Error::Truncated`] if the payload ends early.
70 pub fn decode(version: u16, p: &mut Reader<'_>) -> Result<Self> {
71 expect_version(Tag::HELLO, version)?;
72 let abi_major = p.u16()?;
73 let abi_minor = p.u16()?;
74 p.skip(4)?;
75 let window_bytes = p.u64()?;
76 let max_batch_rows = p.u64()?;
77 let offered = p.capability_set()?;
78 let source_bytes = p.opt_u64()?.unwrap_or(0);
79 Ok(Self {
80 abi_major,
81 abi_minor,
82 window_bytes,
83 max_batch_rows,
84 offered,
85 source_bytes,
86 })
87 }
88}
89
90/// The decoder answering the host.
91#[derive(Clone, Copy, PartialEq, Eq, Debug)]
92pub struct HelloAck<'a> {
93 /// The major ABI version the decoder was built against.
94 pub abi_major: u16,
95 /// The minor ABI version the decoder was built against.
96 pub abi_minor: u16,
97 /// What the decoder cannot run without. If the host does not offer all of these, the two sides
98 /// stop here.
99 pub required: CapabilitySet,
100 /// What the decoder will use if it is there and do without if it is not.
101 pub optional: CapabilitySet,
102 /// A name for the decoder, for logs and error messages. Not interpreted.
103 pub decoder_id: &'a str,
104}
105
106impl<'a> HelloAck<'a> {
107 /// The layout version of this record.
108 pub const VERSION: u16 = 1;
109
110 /// Writes the record.
111 ///
112 /// # Errors
113 ///
114 /// Returns [`Error::BufferFull`] if the buffer runs out.
115 pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
116 w.record(Tag::HELLO_ACK, Self::VERSION, |w| {
117 w.u16(self.abi_major)?;
118 w.u16(self.abi_minor)?;
119 w.u32(0)?;
120 w.var_bytes(self.required.as_bytes())?;
121 w.var_bytes(self.optional.as_bytes())?;
122 w.var_str(self.decoder_id)
123 })
124 }
125
126 /// Reads the record from its payload.
127 ///
128 /// # Errors
129 ///
130 /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows,
131 /// [`Error::Truncated`] if the payload ends early, or [`Error::NotUtf8`] if the decoder name is
132 /// not text.
133 pub fn decode(version: u16, p: &mut Reader<'a>) -> Result<Self> {
134 expect_version(Tag::HELLO_ACK, version)?;
135 let abi_major = p.u16()?;
136 let abi_minor = p.u16()?;
137 p.skip(4)?;
138 let required = p.capability_set()?;
139 let optional = p.capability_set()?;
140 let decoder_id = p.var_str()?;
141 Ok(Self {
142 abi_major,
143 abi_minor,
144 required,
145 optional,
146 decoder_id,
147 })
148 }
149}
150
151/// Why one side is declining to go on.
152#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
153pub struct RefusalReason(pub u16);
154
155impl RefusalReason {
156 /// The other side needs a capability this side does not have.
157 pub const MISSING_CAPABILITY: Self = Self(1);
158 /// The other side speaks a major ABI version from the future.
159 pub const ABI_TOO_NEW: Self = Self(2);
160 /// The other side speaks a major ABI version that is no longer supported.
161 pub const ABI_TOO_OLD: Self = Self(3);
162 /// A record arrived that this side does not know how to handle and cannot skip.
163 pub const UNSUPPORTED_RECORD: Self = Self(4);
164 /// The bytes did not parse.
165 pub const MALFORMED: Self = Self(5);
166 /// The request is larger than this side is willing to serve.
167 pub const RESOURCE_LIMIT: Self = Self(6);
168 /// This side is able to do what was asked and is choosing not to.
169 pub const POLICY: Self = Self(7);
170
171 /// The name of this reason, if it is one we assigned.
172 #[must_use]
173 pub const fn name(self) -> Option<&'static str> {
174 match self {
175 Self::MISSING_CAPABILITY => Some("missing capability"),
176 Self::ABI_TOO_NEW => Some("ABI version too new"),
177 Self::ABI_TOO_OLD => Some("ABI version too old"),
178 Self::UNSUPPORTED_RECORD => Some("unsupported record"),
179 Self::MALFORMED => Some("malformed record"),
180 Self::RESOURCE_LIMIT => Some("resource limit"),
181 Self::POLICY => Some("refused by policy"),
182 _ => None,
183 }
184 }
185}
186
187impl core::fmt::Display for RefusalReason {
188 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189 match self.name() {
190 Some(name) => f.write_str(name),
191 None => write!(f, "refusal reason {}", self.0),
192 }
193 }
194}
195
196/// One side declining to go on, and saying why.
197///
198/// The reason a refusal is a record rather than a dropped connection is that "this did not work" is
199/// not an actionable message. Somebody has to be able to read the failure and know which capability
200/// to go and implement.
201#[derive(Clone, Copy, PartialEq, Eq, Debug)]
202pub struct Refusal<'a> {
203 /// The category of problem.
204 pub reason: RefusalReason,
205 /// Which capability was missing, when `reason` is [`RefusalReason::MISSING_CAPABILITY`].
206 pub capability: Capability,
207 /// Text for a human. Not parsed by anything.
208 pub detail: &'a str,
209}
210
211impl<'a> Refusal<'a> {
212 /// The layout version of this record.
213 pub const VERSION: u16 = 1;
214
215 /// A refusal with no particular capability attached.
216 #[must_use]
217 pub const fn new(reason: RefusalReason, detail: &'a str) -> Self {
218 Self {
219 reason,
220 capability: Capability(u16::MAX),
221 detail,
222 }
223 }
224
225 /// Writes the record.
226 ///
227 /// # Errors
228 ///
229 /// Returns [`Error::BufferFull`] if the buffer runs out.
230 pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
231 w.record(Tag::REFUSAL, Self::VERSION, |w| {
232 w.u16(self.reason.0)?;
233 w.u16(self.capability.0)?;
234 w.u32(0)?;
235 w.var_str(self.detail)
236 })
237 }
238
239 /// Reads the record from its payload.
240 ///
241 /// # Errors
242 ///
243 /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows,
244 /// [`Error::Truncated`] if the payload ends early, or [`Error::NotUtf8`] if the detail is not
245 /// text.
246 pub fn decode(version: u16, p: &mut Reader<'a>) -> Result<Self> {
247 expect_version(Tag::REFUSAL, version)?;
248 let reason = RefusalReason(p.u16()?);
249 let capability = Capability(p.u16()?);
250 p.skip(4)?;
251 let detail = p.var_str()?;
252 Ok(Self {
253 reason,
254 capability,
255 detail,
256 })
257 }
258}
259
260/// The columns a scan is being asked for.
261///
262/// This is a list of indices and not a bitmask. A bitmask has to pick a width, and whatever width
263/// it picks becomes the maximum number of columns the format can ever describe. Wide tables are
264/// exactly where a columnar format is supposed to win, so putting a ceiling on the column count is
265/// the wrong place to save four bytes.
266#[derive(Clone, Copy, PartialEq, Eq, Debug)]
267pub struct Projection<'a> {
268 raw: &'a [u8],
269}
270
271impl<'a> Projection<'a> {
272 /// An empty projection, which means every column.
273 pub const ALL: Self = Self { raw: &[] };
274
275 /// Wraps the encoded form.
276 ///
277 /// # Errors
278 ///
279 /// Returns [`Error::Malformed`] if the length is not a whole number of column indices.
280 pub fn from_bytes(raw: &'a [u8]) -> Result<Self> {
281 if !raw.len().is_multiple_of(4) {
282 return Err(Error::Malformed(
283 "a projection must be a whole number of four byte column indices",
284 ));
285 }
286 Ok(Self { raw })
287 }
288
289 /// How many columns are named.
290 #[must_use]
291 pub const fn len(self) -> usize {
292 self.raw.len() / 4
293 }
294
295 /// Whether the projection names no columns, which means every column.
296 #[must_use]
297 pub const fn is_empty(self) -> bool {
298 self.raw.is_empty()
299 }
300
301 /// The column indices, in the order the caller wrote them.
302 pub fn iter(self) -> impl Iterator<Item = u32> + 'a {
303 self.raw
304 .as_chunks::<4>()
305 .0
306 .iter()
307 .map(|c| u32::from_le_bytes(*c))
308 }
309
310 /// The encoded form.
311 #[must_use]
312 pub const fn as_bytes(self) -> &'a [u8] {
313 self.raw
314 }
315}
316
317/// The host asking the decoder for a run of rows.
318#[derive(Clone, Copy, PartialEq, Eq, Debug)]
319pub struct ScanRequest<'a> {
320 /// The first row wanted, counting from zero.
321 ///
322 /// This is 64 bits because a row count that fits in 32 bits is a limit somebody will hit, and
323 /// the whole point of the exercise is not to build limits into a format that ossifies.
324 pub row_start: u64,
325 /// How many rows are wanted. `u64::MAX` means "everything from `row_start` on".
326 pub row_count: u64,
327 /// Flags, all currently reserved and required to be zero.
328 pub flags: u64,
329 /// Which columns are wanted.
330 pub projection: Projection<'a>,
331 /// A filter for the decoder to apply, in a form the decoder and the host have agreed on
332 /// separately. Empty means no filter.
333 pub filter: &'a [u8],
334}
335
336impl<'a> ScanRequest<'a> {
337 /// The layout version of this record.
338 pub const VERSION: u16 = 1;
339
340 /// A request for every row and every column.
341 #[must_use]
342 pub const fn everything() -> Self {
343 Self {
344 row_start: 0,
345 row_count: u64::MAX,
346 flags: 0,
347 projection: Projection::ALL,
348 filter: &[],
349 }
350 }
351
352 /// Writes the record.
353 ///
354 /// # Errors
355 ///
356 /// Returns [`Error::BufferFull`] if the buffer runs out.
357 pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
358 w.record(Tag::SCAN_REQUEST, Self::VERSION, |w| {
359 w.u64(self.row_start)?;
360 w.u64(self.row_count)?;
361 w.u64(self.flags)?;
362 w.var_bytes(self.projection.as_bytes())?;
363 w.var_bytes(self.filter)
364 })
365 }
366
367 /// Reads the record from its payload.
368 ///
369 /// # Errors
370 ///
371 /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows,
372 /// [`Error::Truncated`] if the payload ends early, or [`Error::Malformed`] if the projection is
373 /// not a whole number of column indices.
374 pub fn decode(version: u16, p: &mut Reader<'a>) -> Result<Self> {
375 expect_version(Tag::SCAN_REQUEST, version)?;
376 let row_start = p.u64()?;
377 let row_count = p.u64()?;
378 let flags = p.u64()?;
379 let projection = Projection::from_bytes(p.var_bytes()?)?;
380 let filter = p.var_bytes()?;
381 Ok(Self {
382 row_start,
383 row_count,
384 flags,
385 projection,
386 filter,
387 })
388 }
389}
390
391/// The decoder asking for bytes of the source.
392///
393/// This is the record the whole design turns on. The decoder says which bytes it needs and the host
394/// decides how to get them, which is what keeps file handles, caching, prefetching, object store
395/// credentials and retry policy on the host side of the boundary where they can be fixed without
396/// recompiling anybody's decoder.
397#[derive(Clone, Copy, PartialEq, Eq, Debug)]
398pub struct RangeRequest {
399 /// Where the wanted bytes start in the source.
400 pub offset: u64,
401 /// How many bytes are wanted.
402 pub len: u64,
403}
404
405impl RangeRequest {
406 /// The layout version of this record.
407 pub const VERSION: u16 = 1;
408
409 /// Writes the record.
410 ///
411 /// # Errors
412 ///
413 /// Returns [`Error::BufferFull`] if the buffer runs out.
414 pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
415 w.record(Tag::RANGE_REQUEST, Self::VERSION, |w| {
416 w.u64(self.offset)?;
417 w.u64(self.len)
418 })
419 }
420
421 /// Reads the record from its payload.
422 ///
423 /// # Errors
424 ///
425 /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows, or
426 /// [`Error::Truncated`] if the payload ends early.
427 pub fn decode(version: u16, p: &mut Reader<'_>) -> Result<Self> {
428 expect_version(Tag::RANGE_REQUEST, version)?;
429 let offset = p.u64()?;
430 let len = p.u64()?;
431 Ok(Self { offset, len })
432 }
433}
434
435/// One array in a batch, in the flattened order the schema puts them in.
436///
437/// This is the same pair Arrow IPC calls a field node, and it is here for the same reason: a
438/// column's length and null count are not derivable from its buffers, so somebody has to say them
439/// out loud.
440#[derive(Clone, Copy, PartialEq, Eq, Debug)]
441pub struct Node {
442 /// How many slots the array has.
443 pub length: u64,
444 /// How many of those slots are null.
445 pub null_count: u64,
446}
447
448impl Node {
449 /// How wide the encoded form is, in bytes.
450 pub const SIZE: usize = 16;
451}
452
453/// Where one Arrow buffer sits in the decoder's memory.
454///
455/// Offsets are 64 bits wide even though a `wasm32` guest cannot address more than four gigabytes,
456/// because the width of a guest address is not something worth writing into a format that ossifies.
457#[derive(Clone, Copy, PartialEq, Eq, Debug)]
458pub struct BufferRef {
459 /// Where the buffer starts in the decoder's memory.
460 pub offset: u64,
461 /// How many bytes long it is.
462 pub len: u64,
463}
464
465impl BufferRef {
466 /// How wide the encoded form is, in bytes.
467 pub const SIZE: usize = 16;
468
469 /// One past the last byte, or `None` if the two fields overflow.
470 #[must_use]
471 pub const fn end(&self) -> Option<u64> {
472 self.offset.checked_add(self.len)
473 }
474}
475
476/// A list of [`Node`]s, still in its encoded form.
477#[derive(Clone, Copy, PartialEq, Eq, Debug)]
478pub struct Nodes<'a> {
479 raw: &'a [u8],
480}
481
482impl<'a> Nodes<'a> {
483 /// No arrays at all.
484 pub const EMPTY: Self = Self { raw: &[] };
485
486 /// Wraps the encoded form.
487 ///
488 /// # Errors
489 ///
490 /// Returns [`Error::Malformed`] if the length is not a whole number of nodes.
491 pub fn from_bytes(raw: &'a [u8]) -> Result<Self> {
492 if !raw.len().is_multiple_of(Node::SIZE) {
493 return Err(Error::Malformed(
494 "a node list must be a whole number of sixteen byte nodes",
495 ));
496 }
497 Ok(Self { raw })
498 }
499
500 /// How many arrays are described.
501 #[must_use]
502 pub const fn len(self) -> usize {
503 self.raw.len() / Node::SIZE
504 }
505
506 /// Whether no arrays are described.
507 #[must_use]
508 pub const fn is_empty(self) -> bool {
509 self.raw.is_empty()
510 }
511
512 /// The nodes, in order.
513 pub fn iter(self) -> impl Iterator<Item = Node> + 'a {
514 self.raw.as_chunks::<{ Node::SIZE }>().0.iter().map(|c| {
515 let (length, null_count) = c.split_at(8);
516 Node {
517 length: u64::from_le_bytes(length.try_into().unwrap_or([0; 8])),
518 null_count: u64::from_le_bytes(null_count.try_into().unwrap_or([0; 8])),
519 }
520 })
521 }
522
523 /// The encoded form.
524 #[must_use]
525 pub const fn as_bytes(self) -> &'a [u8] {
526 self.raw
527 }
528}
529
530/// A list of [`BufferRef`]s, still in its encoded form.
531#[derive(Clone, Copy, PartialEq, Eq, Debug)]
532pub struct Buffers<'a> {
533 raw: &'a [u8],
534}
535
536impl<'a> Buffers<'a> {
537 /// No buffers at all.
538 pub const EMPTY: Self = Self { raw: &[] };
539
540 /// Wraps the encoded form.
541 ///
542 /// # Errors
543 ///
544 /// Returns [`Error::Malformed`] if the length is not a whole number of buffer references.
545 pub fn from_bytes(raw: &'a [u8]) -> Result<Self> {
546 if !raw.len().is_multiple_of(BufferRef::SIZE) {
547 return Err(Error::Malformed(
548 "a buffer list must be a whole number of sixteen byte references",
549 ));
550 }
551 Ok(Self { raw })
552 }
553
554 /// How many buffers are described.
555 #[must_use]
556 pub const fn len(self) -> usize {
557 self.raw.len() / BufferRef::SIZE
558 }
559
560 /// Whether no buffers are described.
561 #[must_use]
562 pub const fn is_empty(self) -> bool {
563 self.raw.is_empty()
564 }
565
566 /// The buffer references, in order.
567 pub fn iter(self) -> impl Iterator<Item = BufferRef> + 'a {
568 self.raw
569 .as_chunks::<{ BufferRef::SIZE }>()
570 .0
571 .iter()
572 .map(|c| {
573 let (offset, len) = c.split_at(8);
574 BufferRef {
575 offset: u64::from_le_bytes(offset.try_into().unwrap_or([0; 8])),
576 len: u64::from_le_bytes(len.try_into().unwrap_or([0; 8])),
577 }
578 })
579 }
580
581 /// The encoded form.
582 #[must_use]
583 pub const fn as_bytes(self) -> &'a [u8] {
584 self.raw
585 }
586}
587
588/// The decoder handing back one batch of decoded rows.
589///
590/// A batch says how many rows it has and then describes the Arrow arrays behind them as a flat list
591/// of nodes and a flat list of buffers, both in the pre-order the schema puts its fields in. That is
592/// the same shape Arrow IPC uses, and it is the right one here for the same reason: the host already
593/// has the schema, so the schema decides how many nodes and buffers there should be and the batch
594/// only has to supply them.
595///
596/// Neither list carries a count. The record length bounds both of them, so a batch cannot claim a
597/// million columns without being large enough to describe a million columns. That is the same rule
598/// the container format follows, and it is the difference between allocation safety being structural
599/// and allocation safety being something a reviewer has to remember.
600///
601/// The buffers are not in this record. They are in the decoder's memory, and the offsets say where.
602/// Whether those offsets are inside the decoder's memory, and whether the bytes at them are a valid
603/// Arrow array, are two separate questions and neither of them is answered here.
604#[derive(Clone, Copy, PartialEq, Eq, Debug)]
605pub struct Batch<'a> {
606 /// How many rows the batch has.
607 pub rows: u64,
608 /// Flags, all currently reserved and required to be zero.
609 pub flags: u64,
610 /// One node per array, in schema pre-order.
611 pub nodes: Nodes<'a>,
612 /// One reference per Arrow buffer, in schema pre-order.
613 pub buffers: Buffers<'a>,
614}
615
616impl<'a> Batch<'a> {
617 /// The layout version of this record.
618 pub const VERSION: u16 = 1;
619
620 /// An empty batch, which is how a decoder says a scan produced no more rows.
621 #[must_use]
622 pub const fn empty() -> Self {
623 Self {
624 rows: 0,
625 flags: 0,
626 nodes: Nodes::EMPTY,
627 buffers: Buffers::EMPTY,
628 }
629 }
630
631 /// Writes the record.
632 ///
633 /// # Errors
634 ///
635 /// Returns [`Error::BufferFull`] if the buffer runs out.
636 pub fn encode(&self, w: &mut Writer<'_>) -> Result<()> {
637 w.record(Tag::BATCH, Self::VERSION, |w| {
638 w.u64(self.rows)?;
639 w.u64(self.flags)?;
640 w.var_bytes(self.nodes.as_bytes())?;
641 w.var_bytes(self.buffers.as_bytes())
642 })
643 }
644
645 /// Reads the record from its payload.
646 ///
647 /// # Errors
648 ///
649 /// Returns [`Error::UnsupportedVersion`] if the layout version is not one this build knows,
650 /// [`Error::Truncated`] if the payload ends early, or [`Error::Malformed`] if either list is not
651 /// a whole number of entries.
652 pub fn decode(version: u16, p: &mut Reader<'a>) -> Result<Self> {
653 expect_version(Tag::BATCH, version)?;
654 let rows = p.u64()?;
655 let flags = p.u64()?;
656 let nodes = Nodes::from_bytes(p.var_bytes()?)?;
657 let buffers = Buffers::from_bytes(p.var_bytes()?)?;
658 Ok(Self {
659 rows,
660 flags,
661 nodes,
662 buffers,
663 })
664 }
665}
666
667/// One record, decoded.
668#[derive(Clone, Copy, PartialEq, Eq, Debug)]
669#[non_exhaustive]
670pub enum Message<'a> {
671 /// See [`Hello`].
672 Hello(Hello),
673 /// See [`HelloAck`].
674 HelloAck(HelloAck<'a>),
675 /// See [`Refusal`].
676 Refusal(Refusal<'a>),
677 /// See [`ScanRequest`].
678 ScanRequest(ScanRequest<'a>),
679 /// See [`RangeRequest`].
680 RangeRequest(RangeRequest),
681 /// See [`Batch`].
682 Batch(Batch<'a>),
683 /// A record this build has no code for. It has already been stepped over, so a reader that gets
684 /// one of these can carry on to the next record.
685 Unknown(
686 /// What was on the front of the record that could not be handled.
687 Header,
688 ),
689}
690
691impl<'a> Reader<'a> {
692 /// Reads the next record and decodes it.
693 ///
694 /// An unrecognised tag comes back as [`Message::Unknown`] rather than an error, and the reader
695 /// is left pointing at the record after it. That is the extension point: adding a record to the
696 /// ABI does not break anything that was compiled before it existed.
697 ///
698 /// # Errors
699 ///
700 /// Returns [`Error::Truncated`] if the buffer ends inside the record, or whatever the
701 /// individual record's decoder returns.
702 pub fn message(&mut self) -> Result<Message<'a>> {
703 let (header, mut p) = self.record()?;
704 let v = header.version;
705 Ok(match header.tag {
706 Tag::HELLO => Message::Hello(Hello::decode(v, &mut p)?),
707 Tag::HELLO_ACK => Message::HelloAck(HelloAck::decode(v, &mut p)?),
708 Tag::REFUSAL => Message::Refusal(Refusal::decode(v, &mut p)?),
709 Tag::SCAN_REQUEST => Message::ScanRequest(ScanRequest::decode(v, &mut p)?),
710 Tag::RANGE_REQUEST => Message::RangeRequest(RangeRequest::decode(v, &mut p)?),
711 Tag::BATCH => Message::Batch(Batch::decode(v, &mut p)?),
712 _ => Message::Unknown(header),
713 })
714 }
715}
716
717fn expect_version(tag: Tag, version: u16) -> Result<()> {
718 // Adding a field to the end of a record does not change this number, because a reader that does
719 // not know about the field skips it and is still correct. The number only moves when a field is
720 // removed or changes meaning, and at that point a reader that guesses is worse than one that
721 // stops. A future version 2 of a record keeps a branch here for version 1.
722 let known = match tag {
723 Tag::HELLO => Hello::VERSION,
724 Tag::HELLO_ACK => HelloAck::VERSION,
725 Tag::REFUSAL => Refusal::VERSION,
726 Tag::SCAN_REQUEST => ScanRequest::VERSION,
727 Tag::RANGE_REQUEST => RangeRequest::VERSION,
728 Tag::BATCH => Batch::VERSION,
729 _ => return Err(Error::Malformed("no version is defined for this tag")),
730 };
731 if version == known {
732 Ok(())
733 } else {
734 Err(Error::UnsupportedVersion { tag, version })
735 }
736}