Skip to main content

smol_bytes/
error.rs

1use core::ops::{Bound, RangeBounds};
2
3#[cfg(any(feature = "std", feature = "alloc"))]
4pub use bytes::TryGetError;
5
6/// Error type for the `try_get_` methods of [`crate::Buffer`].
7/// Indicates that there were not enough remaining
8/// bytes in the buffer while attempting
9/// to get a value from a [`crate::Buffer`] with one
10/// of the `try_get_` methods.
11#[derive(Debug, PartialEq, Eq)]
12#[cfg(not(any(feature = "std", feature = "alloc")))]
13pub struct TryGetError {
14  /// The number of bytes necessary to get the value
15  pub requested: usize,
16
17  /// The number of bytes available in the buffer
18  pub available: usize,
19}
20
21#[cfg(not(any(feature = "std", feature = "alloc")))]
22const _: () = {
23  impl core::fmt::Display for TryGetError {
24    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
25      write!(
26        f,
27        "Not enough bytes remaining in buffer to read value (requested {} but only {} available)",
28        self.requested, self.available
29      )
30    }
31  }
32
33  impl core::error::Error for TryGetError {}
34};
35
36/// Error type for the `try_put_` methods of [`crate::Buffer`].
37/// Indicates that there were not enough remaining
38/// capacity in the buffer while attempting
39/// to put a value to a [`crate::Buffer`] with one
40/// of the `try_put_` methods.
41#[derive(Debug, PartialEq, Eq)]
42pub struct TryPutError {
43  /// The number of bytes necessary to put the value
44  pub requested: usize,
45
46  /// The number of bytes available in the buffer
47  pub available: usize,
48}
49
50impl core::fmt::Display for TryPutError {
51  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
52    write!(
53      f,
54      "Not enough bytes remaining in buffer to write value (requested {} but only {} available)",
55      self.requested, self.available
56    )
57  }
58}
59
60impl core::error::Error for TryPutError {}
61
62#[cfg(feature = "std")]
63impl From<TryPutError> for std::io::Error {
64  fn from(value: TryPutError) -> Self {
65    std::io::Error::other(value)
66  }
67}
68
69/// Error type indicating an invalid integer length was specified.
70/// This is used when attempting to read or write
71/// an integer with an invalid number of bytes.
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
73pub struct InvalidIntegerLength(pub usize);
74
75impl core::fmt::Display for InvalidIntegerLength {
76  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
77    write!(
78      f,
79      "invalid integer length: {} (must be less or equal to 8)",
80      self.0
81    )
82  }
83}
84
85impl core::error::Error for InvalidIntegerLength {}
86
87#[cfg(feature = "std")]
88impl From<InvalidIntegerLength> for std::io::Error {
89  fn from(value: InvalidIntegerLength) -> Self {
90    std::io::Error::new(std::io::ErrorKind::InvalidInput, value)
91  }
92}
93
94impl From<InvalidIntegerLength> for TryPutIntegerError {
95  fn from(value: InvalidIntegerLength) -> Self {
96    TryPutIntegerError::InvalidLength(value)
97  }
98}
99
100/// Error type for the `try_put_` methods of [`crate::Buffer`].
101/// Indicates that there were not enough remaining
102/// capacity in the buffer while attempting
103/// to put a value to a [`crate::Buffer`] with one
104/// of the `try_put_` methods.
105#[derive(Debug, PartialEq, Eq)]
106pub enum TryPutIntegerError {
107  /// Not enough space to write all bytes.
108  NotEnoughSpace(TryPutError),
109  /// Invalid number of bytes specified.
110  InvalidLength(InvalidIntegerLength),
111}
112
113impl core::fmt::Display for TryPutIntegerError {
114  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
115    match self {
116      Self::NotEnoughSpace(err) => write!(f, "{}", err),
117      Self::InvalidLength(len) => write!(f, "{}", len),
118    }
119  }
120}
121
122impl core::error::Error for TryPutIntegerError {}
123
124#[cfg(feature = "std")]
125impl From<TryPutIntegerError> for std::io::Error {
126  fn from(value: TryPutIntegerError) -> Self {
127    std::io::Error::other(value)
128  }
129}
130/// Error type indicating an index or range is out of bounds.
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub struct OutOfBounds {
133  /// The requested index or size
134  pub requested: usize,
135  /// The available length or capacity
136  pub available: usize,
137}
138
139impl OutOfBounds {
140  /// Creates a new `OutOfBounds` error.
141  #[inline]
142  pub const fn new(requested: usize, available: usize) -> Self {
143    Self {
144      requested,
145      available,
146    }
147  }
148}
149
150impl core::fmt::Display for OutOfBounds {
151  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
152    write!(
153      f,
154      "index out of bounds: requested {} but only {} available",
155      self.requested, self.available
156    )
157  }
158}
159
160impl core::error::Error for OutOfBounds {}
161
162#[cfg(feature = "std")]
163impl From<OutOfBounds> for std::io::Error {
164  fn from(value: OutOfBounds) -> Self {
165    std::io::Error::new(std::io::ErrorKind::InvalidInput, value)
166  }
167}
168
169/// Error type indicating an index or range is out of bounds.
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub struct RangeOutOfBounds {
172  /// The start index of the range
173  pub start: usize,
174  /// The end index of the requested range
175  pub end: usize,
176  /// The available length or capacity
177  pub available: usize,
178}
179
180impl RangeOutOfBounds {
181  /// Creates a new `RangeOutOfBounds` error.
182  #[inline]
183  pub const fn new(start: usize, end: usize, available: usize) -> Self {
184    Self {
185      start,
186      end,
187      available,
188    }
189  }
190}
191
192impl core::fmt::Display for RangeOutOfBounds {
193  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
194    write!(
195      f,
196      "range out of bounds: requested {}..{} but only {} available",
197      self.start, self.end, self.available
198    )
199  }
200}
201
202impl core::error::Error for RangeOutOfBounds {}
203
204/// Normalizes a range against `len` without panicking on reversed or
205/// overflowing bounds.
206pub(crate) fn normalize_range(
207  range: impl RangeBounds<usize>,
208  len: usize,
209) -> Result<(usize, usize), RangeOutOfBounds> {
210  let start = match range.start_bound() {
211    Bound::Included(&n) => n,
212    Bound::Excluded(&n) => n
213      .checked_add(1)
214      .ok_or_else(|| RangeOutOfBounds::new(n, n, len))?,
215    Bound::Unbounded => 0,
216  };
217
218  let end = match range.end_bound() {
219    Bound::Included(&n) => n
220      .checked_add(1)
221      .ok_or_else(|| RangeOutOfBounds::new(start, n, len))?,
222    Bound::Excluded(&n) => n,
223    Bound::Unbounded => len,
224  };
225
226  if start > end || end > len {
227    return Err(RangeOutOfBounds::new(start, end, len));
228  }
229
230  Ok((start, end))
231}
232
233#[cfg(feature = "std")]
234impl From<RangeOutOfBounds> for std::io::Error {
235  fn from(value: RangeOutOfBounds) -> Self {
236    std::io::Error::new(std::io::ErrorKind::InvalidInput, value)
237  }
238}
239
240/// Error type for UTF-8 operations that indicates an invalid character boundary
241/// or out of bounds access.
242#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243pub enum Utf8Error {
244  /// The index does not lie on a UTF-8 character boundary.
245  InvalidCharBoundary {
246    /// The index that was not on a character boundary
247    at: usize,
248  },
249  /// The index or range is out of bounds.
250  OutOfBounds {
251    /// The requested index
252    at: usize,
253    /// The length of the buffer
254    len: usize,
255  },
256}
257
258impl core::fmt::Display for Utf8Error {
259  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
260    match self {
261      Self::InvalidCharBoundary { at } => {
262        write!(f, "index {} does not lie on a UTF-8 character boundary", at)
263      }
264      Self::OutOfBounds { at, len } => {
265        write!(f, "index {} out of bounds: length is {}", at, len)
266      }
267    }
268  }
269}
270
271impl core::error::Error for Utf8Error {}
272
273#[cfg(feature = "std")]
274impl From<Utf8Error> for std::io::Error {
275  fn from(value: Utf8Error) -> Self {
276    std::io::Error::new(std::io::ErrorKind::InvalidInput, value)
277  }
278}
279
280/// Error type for converting a raw byte slice into a fixed-capacity
281/// UTF-8 buffer. Combines UTF-8 validation and capacity errors.
282#[derive(Debug, PartialEq, Eq)]
283pub enum FromBytesError {
284  /// The bytes are not valid UTF-8.
285  InvalidUtf8(core::str::Utf8Error),
286  /// The bytes do not fit in the buffer's capacity.
287  TooLarge(TryPutError),
288}
289
290impl core::fmt::Display for FromBytesError {
291  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
292    match self {
293      Self::InvalidUtf8(e) => write!(f, "invalid UTF-8: {}", e),
294      Self::TooLarge(e) => write!(f, "{}", e),
295    }
296  }
297}
298
299impl core::error::Error for FromBytesError {}
300
301impl From<core::str::Utf8Error> for FromBytesError {
302  fn from(e: core::str::Utf8Error) -> Self {
303    Self::InvalidUtf8(e)
304  }
305}
306
307impl From<TryPutError> for FromBytesError {
308  fn from(e: TryPutError) -> Self {
309    Self::TooLarge(e)
310  }
311}
312
313#[cfg(feature = "std")]
314impl From<FromBytesError> for std::io::Error {
315  fn from(value: FromBytesError) -> Self {
316    std::io::Error::new(std::io::ErrorKind::InvalidInput, value)
317  }
318}