Skip to main content

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	 * Maps a `ChiselError<F, S>` to a `ChiselError<N, S>` using the `From` trait.
70	 *
71	 * Note that both [`EndOfInput`][ChiselErrorData::EndOfInput] and [`Source`][ChiselErrorData::Source] errors are returned untouched.
72	 */
73	pub fn map_into<N : From<F>>(self) -> ChiselError<N, S> {
74		self.map(|x| x.into())
75	}
76}
77
78/**
79 * Helper function to work around the Rust Compiler's inability to
80 * automatically declare branches with zero-variant-enum values as unreachable.
81 */
82fn static_unreachable(proof: core::convert::Infallible) -> ! {
83	match proof {}
84}
85
86impl<S> ChiselError<core::convert::Infallible, S> {
87	/** Creates a `ChiselError` from a [`ChiselSourceError`] + an offset. */
88	pub fn from_source(offset: usize, error: ChiselSourceError<S>) -> Self {
89		match error {
90			ChiselSourceError::Underlying(x) => Self::source(offset, x),
91			ChiselSourceError::EndOfInput => Self::end_of_input(offset)
92		}
93	}
94
95	/**
96	 * Convert an [`InfallibleFormatError`][crate::InfallibleFormatError] into an error with arbitrary format type.
97	 *
98	 * (Note that because `Infallible` is uninhabited, this is always valid.)
99	 *
100	 * This is basically a small hack to allow easy error propagation,
101	 * as `ChiselError<F, S>` cannot implement `From<ChiselError<Infallible, S>>`.
102	 */
103	pub fn forward<F>(self) -> ChiselError<F, S> {
104		ChiselError {
105			error: match self.error {
106				ChiselErrorData::EndOfInput => ChiselErrorData::EndOfInput,
107				ChiselErrorData::Source(x) => ChiselErrorData::Source(x),
108				ChiselErrorData::Format(x) => { static_unreachable(x) }
109			},
110			offset: self.offset
111		}
112	}
113}
114
115impl<F : Display, S : Display> Display for ChiselError<F, S> {
116	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
117		f.write_str("couldn't decode: ")?;
118		match &self.error {
119			ChiselErrorData::Format(x) => write!(f, "bad data: {x}"),
120			ChiselErrorData::Source(x) => write!(f, "input error: {x}"),
121			ChiselErrorData::EndOfInput => f.write_str("unexpected end of data")
122		}?;
123		write!(f, " near offset 0x{:08x}", self.offset)
124	}
125}
126
127impl<F : Error + 'static, S : Error + 'static> Error for ChiselError<F, S> {
128	fn cause(&self) -> Option<&(dyn Error + 'static)> {
129		match &self.error {
130			ChiselErrorData::Format(x) => Some(x),
131			ChiselErrorData::Source(x) => Some(x),
132			ChiselErrorData::EndOfInput => None
133		}
134	}
135}