goldforge/wad/
decode_error.rs

1// Copyright 2025-2026 Gabriel Bjørnager Jensen.
2//
3// This Source Code Form is subject to the terms of
4// the Mozilla Public License, v. 2.0. If a copy of
5// the MPL was not distributed with this file, you
6// can obtain one at:
7// <https://mozilla.org/MPL/2.0/>.
8
9//! The [`DecodeError`] error type.
10
11use core::convert::Infallible;
12use core::error::Error;
13use core::fmt::{self, Display, Formatter};
14use oct::IntoOcts;
15
16#[cfg(feature = "std")]
17use std::io;
18
19/// A WAD file could not be read.
20#[derive(Debug)]
21pub struct DecodeError(Inner);
22
23impl DecodeError {
24	/// Constructs a [`EndOfFile`](Inner::EndOfFile) instance.
25	#[inline]
26	#[must_use]
27	pub(in crate::wad) fn end_of_file(found: usize, expected: usize) -> Self {
28		let inner = Inner::EndOfFile { found, expected };
29		Self(inner)
30	}
31
32	/// Constructs a [`LargeSize`](Inner::LargeSize) instance.
33	#[inline]
34	#[must_use]
35	pub(in crate::wad) fn large_size(temporary: u64) -> Self {
36		let inner = Inner::LargeSize { temporary };
37		Self(inner)
38	}
39
40	/// Constructs an [`UnknownMagic`](Inner::UnknownMagic) instance.
41	#[inline]
42	#[must_use]
43	pub(in crate::wad) fn unknown_magic(magic: [i8; 4]) -> Self {
44		let inner = Inner::UnknownMagic { magic };
45		Self(inner)
46	}
47}
48
49impl Display for DecodeError {
50	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
51		match self.0 {
52			Inner::EndOfFile { found, expected } => {
53				write!(f, "wad file of `{found}` byte(s) may not be smaller than `{expected}` byte(s)")?;
54			}
55
56			#[cfg(feature = "std")]
57			Inner::Io(ref e) => {
58				write!(f, "{e}")?;
59			}
60
61			Inner::LargeSize { temporary } => {
62				write!(f, "computed size value `{temporary}` cannot be represented")?;
63			}
64
65			Inner::UnknownMagic { ref magic } => {
66				write!(f, "magic `{}` is unknown or unsupported", magic.as_octs().escape_ascii())?;
67			}
68		}
69
70		Ok(())
71	}
72}
73
74impl Error for DecodeError {
75	fn source(&self) -> Option<&(dyn Error + 'static)> {
76		match self.0 {
77			#[cfg(feature = "std")]
78			Inner::Io(ref e) => Some(e),
79
80			_ => None,
81		}
82	}
83}
84
85#[cfg(feature = "std")]
86impl From<io::Error> for DecodeError {
87	#[inline]
88	fn from(value: io::Error) -> Self {
89		let inner = Inner::Io(value);
90		Self(inner)
91	}
92}
93
94impl From<Infallible> for DecodeError {
95	fn from(value: Infallible) -> Self {
96		match value {}
97	}
98}
99
100/// Internal variants of [`DecodeError`].
101#[derive(Debug)]
102enum Inner {
103	/// The WAD file ended unexpectedly.
104	EndOfFile {
105		/// The length of the file in question.
106		found: usize,
107
108		/// The minimum, expected length.
109		expected: usize,
110	},
111
112	/// An I/O error.
113	#[cfg(feature = "std")]
114	Io(io::Error),
115
116	/// A size value temporary could not be represented by [`usize`].
117	LargeSize {
118		/// The size value in question, after
119		/// signed-to-unsigned wrap.
120		temporary: u64,
121	},
122
123	/// The file magic was unknown or unsupported.
124	UnknownMagic {
125		/// The magic in question.
126		magic: [i8; 4],
127	},
128}