iris_abi/range.rs
1//! What the host answers when a decoder asks for bytes it has not been given.
2//!
3//! The request itself is [`crate::RangeRequest`] when it travels as a record. Across the WebAssembly
4//! boundary it is a call rather than a record, because the decoder is stopped inside it waiting for
5//! the answer and a record would mean framing a request the caller is going to consume immediately.
6//! What crosses is three numbers in and one number out, and the number that comes out is this.
7//!
8//! The reason it is an integer rather than a boolean is that the four ways a range can fail are not
9//! the same failure. Two of them are the decoder asking for the wrong thing and are worth reporting
10//! back so it can ask for the right thing instead, and two of them are the host being unable to
11//! serve a request that was perfectly reasonable. A decoder that cannot tell those apart has no
12//! choice but to give up on all four.
13
14/// The answer to `iris.require_range`.
15///
16/// Only [`RangeStatus::SERVED`] means the bytes are in the buffer the decoder named. For every other
17/// value the buffer has not been written to and holds whatever it held before.
18#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
19pub struct RangeStatus(pub u32);
20
21impl RangeStatus {
22 /// The bytes are in the buffer, all of them, exactly as many as were asked for.
23 pub const SERVED: Self = Self(0);
24
25 /// The range runs past the end of the source.
26 ///
27 /// The decoder asked for the wrong thing. This is never a short read, which is the whole reason
28 /// it is a status rather than a returned length: a decoder that gets fewer bytes than it asked
29 /// for and does not notice produces an answer that is wrong and looks right.
30 pub const OUT_OF_BOUNDS: Self = Self(1);
31
32 /// No single request to this host can cover a range that long.
33 ///
34 /// The host is reading through a window and the range does not fit in one view of it. Asking
35 /// again in pieces works, which is what makes this different from the two below.
36 pub const TOO_LARGE: Self = Self(2);
37
38 /// The host tried and could not get the bytes.
39 ///
40 /// A read that failed, a connection that dropped, a source that contradicted itself. Nothing the
41 /// decoder does differently will help, and the host already knows the details.
42 pub const UNAVAILABLE: Self = Self(3);
43
44 /// This host has nothing to serve ranges from.
45 ///
46 /// A decoder that pulls ranges was run by a host that handed it the whole source and did not
47 /// expect to be asked, or by one that has not attached a source yet. It is a host bug rather
48 /// than a decoder bug, and it is separate from [`RangeStatus::UNAVAILABLE`] because the fix is
49 /// in a different place.
50 pub const NO_SOURCE: Self = Self(4);
51
52 /// Whether the bytes arrived.
53 #[must_use]
54 pub const fn is_served(self) -> bool {
55 self.0 == Self::SERVED.0
56 }
57
58 /// The name of this status, if it is one we assigned.
59 #[must_use]
60 pub const fn name(self) -> Option<&'static str> {
61 match self {
62 Self::SERVED => Some("served"),
63 Self::OUT_OF_BOUNDS => Some("out of bounds"),
64 Self::TOO_LARGE => Some("larger than one request can cover"),
65 Self::UNAVAILABLE => Some("unavailable"),
66 Self::NO_SOURCE => Some("no source attached"),
67 _ => None,
68 }
69 }
70}
71
72impl core::fmt::Display for RangeStatus {
73 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 match self.name() {
75 Some(name) => f.write_str(name),
76 None => write!(f, "range status {}", self.0),
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 /// Somewhere to print into, because this crate has no allocator and so has no `to_string`.
86 struct Buf {
87 bytes: [u8; 64],
88 len: usize,
89 }
90
91 impl Buf {
92 const fn new() -> Self {
93 Self {
94 bytes: [0; 64],
95 len: 0,
96 }
97 }
98
99 fn text(&self) -> &str {
100 core::str::from_utf8(&self.bytes[..self.len]).expect("everything written here is utf8")
101 }
102 }
103
104 impl core::fmt::Write for Buf {
105 fn write_str(&mut self, text: &str) -> core::fmt::Result {
106 let end = self.len + text.len();
107 let room = self.bytes.get_mut(self.len..end).ok_or(core::fmt::Error)?;
108 room.copy_from_slice(text.as_bytes());
109 self.len = end;
110 Ok(())
111 }
112 }
113
114 fn printed(status: RangeStatus) -> Buf {
115 use core::fmt::Write as _;
116 let mut buf = Buf::new();
117 write!(&mut buf, "{status}").expect("a status is shorter than the buffer");
118 buf
119 }
120
121 #[test]
122 fn only_zero_means_the_bytes_are_there() {
123 assert!(RangeStatus::SERVED.is_served());
124 for status in [
125 RangeStatus::OUT_OF_BOUNDS,
126 RangeStatus::TOO_LARGE,
127 RangeStatus::UNAVAILABLE,
128 RangeStatus::NO_SOURCE,
129 ] {
130 assert!(!status.is_served(), "{status} is not the bytes arriving");
131 }
132 }
133
134 #[test]
135 fn a_status_from_a_later_host_still_prints() {
136 let future = RangeStatus(9001);
137 assert_eq!(future.name(), None);
138 assert_eq!(printed(future).text(), "range status 9001");
139 }
140}