iris_abi/error.rs
1//! Errors from reading and writing ABI records.
2//!
3//! There is no `thiserror` here, and there will not be. This crate ends up inside every decoder
4//! anyone writes, so it carries no dependencies at all and the boilerplate is written out by hand.
5
6use core::fmt;
7
8use crate::record::Tag;
9
10/// What went wrong while reading or writing an ABI record.
11///
12/// Every variant is a fact about the bytes rather than an opinion about them. Deciding what to do
13/// about a malformed record is the caller's job, because the host and the guest have very different
14/// options available to them.
15#[derive(Clone, Copy, PartialEq, Eq, Debug)]
16#[non_exhaustive]
17pub enum Error {
18 /// The buffer ended before the value did.
19 Truncated {
20 /// How many bytes the read wanted.
21 needed: usize,
22 /// How many bytes were left.
23 available: usize,
24 },
25 /// There was not enough room left in the output buffer.
26 BufferFull {
27 /// How many bytes the write wanted.
28 needed: usize,
29 /// How many bytes were left.
30 available: usize,
31 },
32 /// A length field described more bytes than can be addressed on this target.
33 LengthOverflow,
34 /// A field that is declared to be text was not valid UTF-8.
35 NotUtf8,
36 /// The bytes parsed but do not describe a record this code can make sense of.
37 Malformed(
38 /// What specifically was wrong. This is a fixed string rather than a formatted one so that
39 /// the crate stays free of allocation.
40 &'static str,
41 ),
42 /// The record is one we know, at a version we do not.
43 ///
44 /// This is not the same as an unknown tag. An unknown tag is skipped, because that is what
45 /// makes the format extensible. A known tag at an unknown version is a real disagreement and
46 /// the reader has to say so.
47 UnsupportedVersion {
48 /// Which record.
49 tag: Tag,
50 /// The version that was on the wire.
51 version: u16,
52 },
53}
54
55impl fmt::Display for Error {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match self {
58 Self::Truncated { needed, available } => write!(
59 f,
60 "record is truncated: wanted {needed} bytes, {available} available"
61 ),
62 Self::BufferFull { needed, available } => write!(
63 f,
64 "output buffer is full: wanted {needed} bytes, {available} available"
65 ),
66 Self::LengthOverflow => f.write_str("a length field does not fit in a pointer"),
67 Self::NotUtf8 => f.write_str("a text field is not valid UTF-8"),
68 Self::Malformed(what) => write!(f, "malformed record: {what}"),
69 Self::UnsupportedVersion { tag, version } => write!(
70 f,
71 "record {tag} is at version {version}, which this build does not understand"
72 ),
73 }
74 }
75}
76
77impl core::error::Error for Error {}
78
79/// The result of an ABI read or write.
80pub type Result<T> = core::result::Result<T, Error>;