goldforge 0.8.1

Library for handling file formats used by GoldSrc and related engines.
Documentation
// Copyright 2025-2026 Gabriel Bjørnager Jensen.
//
// This Source Code Form is subject to the terms of
// the Mozilla Public License, v. 2.0. If a copy of
// the MPL was not distributed with this file, you
// can obtain one at:
// <https://mozilla.org/MPL/2.0/>.

//! The [`DecodeError`] error type.

use core::convert::Infallible;
use core::error::Error;
use core::fmt::{self, Display, Formatter};
use oct::IntoOcts;

#[cfg(feature = "std")]
use std::io;

/// A WAD file could not be read.
#[derive(Debug)]
pub struct DecodeError(Inner);

impl DecodeError {
	/// Constructs a [`EndOfFile`](Inner::EndOfFile) instance.
	#[inline]
	#[must_use]
	pub(in crate::wad) fn end_of_file(found: usize, expected: usize) -> Self {
		let inner = Inner::EndOfFile { found, expected };
		Self(inner)
	}

	/// Constructs a [`LargeSize`](Inner::LargeSize) instance.
	#[inline]
	#[must_use]
	pub(in crate::wad) fn large_size(temporary: u64) -> Self {
		let inner = Inner::LargeSize { temporary };
		Self(inner)
	}

	/// Constructs an [`UnknownMagic`](Inner::UnknownMagic) instance.
	#[inline]
	#[must_use]
	pub(in crate::wad) fn unknown_magic(magic: [i8; 4]) -> Self {
		let inner = Inner::UnknownMagic { magic };
		Self(inner)
	}
}

impl Display for DecodeError {
	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
		match self.0 {
			Inner::EndOfFile { found, expected } => {
				write!(f, "wad file of `{found}` byte(s) may not be smaller than `{expected}` byte(s)")?;
			}

			#[cfg(feature = "std")]
			Inner::Io(ref e) => {
				write!(f, "{e}")?;
			}

			Inner::LargeSize { temporary } => {
				write!(f, "computed size value `{temporary}` cannot be represented")?;
			}

			Inner::UnknownMagic { ref magic } => {
				write!(f, "magic `{}` is unknown or unsupported", magic.as_octs().escape_ascii())?;
			}
		}

		Ok(())
	}
}

impl Error for DecodeError {
	fn source(&self) -> Option<&(dyn Error + 'static)> {
		match self.0 {
			#[cfg(feature = "std")]
			Inner::Io(ref e) => Some(e),

			_ => None,
		}
	}
}

#[cfg(feature = "std")]
impl From<io::Error> for DecodeError {
	#[inline]
	fn from(value: io::Error) -> Self {
		let inner = Inner::Io(value);
		Self(inner)
	}
}

impl From<Infallible> for DecodeError {
	fn from(value: Infallible) -> Self {
		match value {}
	}
}

/// Internal variants of [`DecodeError`].
#[derive(Debug)]
enum Inner {
	/// The WAD file ended unexpectedly.
	EndOfFile {
		/// The length of the file in question.
		found: usize,

		/// The minimum, expected length.
		expected: usize,
	},

	/// An I/O error.
	#[cfg(feature = "std")]
	Io(io::Error),

	/// A size value temporary could not be represented by [`usize`].
	LargeSize {
		/// The size value in question, after
		/// signed-to-unsigned wrap.
		temporary: u64,
	},

	/// The file magic was unknown or unsupported.
	UnknownMagic {
		/// The magic in question.
		magic: [i8; 4],
	},
}