byte_chisel/
error.rs

1use core::error::Error;
2use core::fmt::Display;
3use crate::source::ChiselSourceError;
4
5/** The error data type for chiseling operations. */
6#[derive(Debug)]
7pub enum ChiselErrorData<F, S> {
8	/** The provided data was invalid. */
9	Format(F),
10	/** The byte source produced an error. */
11	Source(S),
12	/** The input ended unexpectedly. */
13	EndOfInput
14}
15
16/**
17 * The error type for chiseling operations.
18 *
19 * This type provides offset information alongside the error data itself to allow for more precise diagnostics.
20 *
21 * # Kinds
22 *
23 * This crate distinguishes between 3 'kinds' of errors:
24 * - `EndOfInput`: An unexpected end-of-input condition. If chiseling is retried with additional data provided in the input, it may succeed.
25 * - `Source`: The `Chisel`'s source signalled an error condition.
26 * - `Format`: The provided data to chisel is invalid in some way.
27 */
28#[derive(Debug)]
29pub struct ChiselError<F, S> {
30	/**
31	 * The byte offset that the error occurred at.
32	 *
33	 * This is the offset of the _beginning_ of the erroring operation or item.
34	 */
35	pub offset: usize,
36	/** The error data itself. */
37	pub error: ChiselErrorData<F, S>
38}
39
40impl<F, S> ChiselError<F, S> {
41	/** Creates a new Chisel Error with the provided offset and error data. */
42	pub fn new(offset: usize, error: ChiselErrorData<F, S>) -> Self { Self { offset, error } }
43	/** Creates a new "unexpected end-of-input" Chisel Error with the provided offset. */
44	pub fn end_of_input(offset: usize) -> Self { Self { offset, error: ChiselErrorData::EndOfInput } }
45	/** Creates a new Chisel Source Error with the provided offset and 'underlying' error. */
46	pub fn source(offset: usize, error: S) -> Self { Self { offset, error: ChiselErrorData::Source(error) } }
47	/** Creates a new Chisel Format Error with the provided offset and detail. */
48	pub fn format(offset: usize, error: F) -> Self { Self { offset, error: ChiselErrorData::Format(error) } }
49
50	/**
51	 * Maps a `ChiselError<F, S>` to `ChiselError<N, S>` by applying a function to the contained format error, if any.
52	 *
53	 * This can be used to provide additional information about the issue with the provided data.
54	 *
55	 * Note that both [`EndOfInput`][ChiselErrorData::EndOfInput] and [`Source`][ChiselErrorData::Source] errors are returned untouched.
56	 */
57	pub fn map<M : FnOnce(F) -> N, N>(self, mapper: M) -> ChiselError<N, S> {
58		ChiselError {
59			offset: self.offset,
60			error: match self.error {
61				ChiselErrorData::EndOfInput => ChiselErrorData::EndOfInput,
62				ChiselErrorData::Format(x) => ChiselErrorData::Format(mapper(x)),
63				ChiselErrorData::Source(x) => ChiselErrorData::Source(x)
64			}
65		}
66	}
67}
68
69/**
70 * Helper function to work around the Rust Compiler's inability to
71 * automatically declare branches with zero-variant-enum values as unreachable.
72 */
73fn static_unreachable(proof: core::convert::Infallible) -> ! {
74	match proof {}
75}
76
77impl<S> ChiselError<core::convert::Infallible, S> {
78	/** Creates a `ChiselError` from a [`ChiselSourceError`] + an offset. */
79	pub fn from_source(offset: usize, error: ChiselSourceError<S>) -> Self {
80		match error {
81			ChiselSourceError::Underlying(x) => Self::source(offset, x),
82			ChiselSourceError::EndOfInput => Self::end_of_input(offset)
83		}
84	}
85
86	/**
87	 * Convert an [`InfallibleFormatError`][crate::InfallibleFormatError] into an error with arbitrary format type.
88	 *
89	 * (Note that because `Infallible` is uninhabited, this is always valid.)
90	 *
91	 * This is basically a small hack to allow easy error propagation,
92	 * as `ChiselError<F, S>` cannot implement `From<ChiselError<Infallible, S>>`.
93	 */
94	pub fn forward<F>(self) -> ChiselError<F, S> {
95		ChiselError {
96			error: match self.error {
97				ChiselErrorData::EndOfInput => ChiselErrorData::EndOfInput,
98				ChiselErrorData::Source(x) => ChiselErrorData::Source(x),
99				ChiselErrorData::Format(x) => { static_unreachable(x) }
100			},
101			offset: self.offset
102		}
103	}
104}
105
106impl<F : Display, S : Display> Display for ChiselError<F, S> {
107	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
108		f.write_str("couldn't decode: ")?;
109		match &self.error {
110			ChiselErrorData::Format(x) => write!(f, "bad data: {x}"),
111			ChiselErrorData::Source(x) => write!(f, "input error: {x}"),
112			ChiselErrorData::EndOfInput => f.write_str("unexpected end of data")
113		}?;
114		write!(f, " near offset 0x{:08x}", self.offset)
115	}
116}
117
118impl<F : Error + 'static, S : Error + 'static> Error for ChiselError<F, S> {
119	fn cause(&self) -> Option<&(dyn Error + 'static)> {
120		match &self.error {
121			ChiselErrorData::Format(x) => Some(x),
122			ChiselErrorData::Source(x) => Some(x),
123			ChiselErrorData::EndOfInput => None
124		}
125	}
126}