Skip to main content

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>;