oct 0.36.2

Octonary transcodings.
Documentation
// Copyright 2024-2026 Gabriel Bjørnager Jensen.
//
// SPDX: MIT OR Apache-2.0

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

use core::fmt::{self, Display, Formatter};
use core::marker::PhantomData;

#[cfg(feature = "alloc")]
use alloc::collections::TryReserveError;

#[cfg(feature = "alloc")]
use alloc::ffi::NulError;

#[cfg(feature = "std")]
#[rustversion::since(1.89)]
use std::fs::TryLockError;

#[cfg(feature = "std")]
use std::io::{self, IntoInnerError};

/// A serialisation/deserialisation could not be
/// performed.
///
/// This error type represents a portable
/// input/output that can represent *at least* the
/// [`InvalidData`], [`InvalidInput`], and
/// [`OutOfMemory`], [`UnexpectedEof`], and
/// [`WriteZero`] error kinds. When the `std`
/// feature is enabled, this type serves as a
/// newtype wrapper for [`io::Error`].
///
#[cfg_attr(feature = "std",      doc = "[`InvalidData`]: io::ErrorKind::InvalidData")]
#[cfg_attr(not(feature = "std"), doc = "[`InvalidData`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.InvalidData>")]
#[cfg_attr(feature = "std",      doc = "[`InvalidInput`]: io::ErrorKind::InvalidInput")]
#[cfg_attr(not(feature = "std"), doc = "[`InvalidInput`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.InvalidInput>")]
#[cfg_attr(feature = "std",      doc = "[`OutOfMemory`]: io::ErrorKind::OutOfMemory")]
#[cfg_attr(not(feature = "std"), doc = "[`OutOfMemory`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.OutOfMemory>")]
#[cfg_attr(feature = "std",      doc = "[`UnexpectedEof`]: io::ErrorKind::UnexpectedEof")]
#[cfg_attr(not(feature = "std"), doc = "[`UnexpectedEof`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.UnexpectedEof>")]
#[cfg_attr(feature = "std",      doc = "[`WriteZero`]: io::ErrorKind::WriteZero")]
#[cfg_attr(not(feature = "std"), doc = "[`WriteZero`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.WriteZero>")]
#[cfg_attr(not(feature = "std"), doc = "[`io::Error`]: <https://doc.rust-lang.org/nightly/std/io/struct.Error.html>")]
#[derive(Debug)]
pub struct Error {
	/// The inner error.
	inner: Inner,

	/// Phantom source allocation so that auto traits
	/// always match.
	_source: PhantomData<&'static mut (dyn core::error::Error + Send + Sync)>,
}

impl Error {
	/// Constructs an [`InvalidData`] error.
	///
	#[cfg_attr(feature = "std",      doc = "[`InvalidData`]: io::ErrorKind::InvalidData")]
	#[cfg_attr(not(feature = "std"), doc = "[`InvalidData`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.InvalidData>")]
	#[must_use]
	pub fn invalid_data() -> Self {
		#[cfg(feature = "std")]
		{
			let inner = Inner::Io(io::ErrorKind::InvalidData.into());
			Self { inner, _source: PhantomData }
		}

		#[cfg(not(feature = "std"))]
		{
			let inner = Inner::InvalidData;
			Self { inner, _source: PhantomData }
		}
	}

	/// Constructs an [`InvalidInput`] error.
	///
	#[cfg_attr(feature = "std",      doc = "[`InvalidInput`]: io::ErrorKind::InvalidInput")]
	#[cfg_attr(not(feature = "std"), doc = "[`InvalidInput`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.InvalidInput>")]
	#[must_use]
	pub fn invalid_input() -> Self {
		#[cfg(feature = "std")]
		{
			let inner = Inner::Io(io::ErrorKind::InvalidInput.into());
			Self { inner, _source: PhantomData }
		}

		#[cfg(not(feature = "std"))]
		{
			let inner = Inner::InvalidInput;
			Self { inner, _source: PhantomData }
		}
	}

	/// Constructs an [`OutOfMemory`] error.
	///
	#[cfg_attr(feature = "std",      doc = "[`OutOfMemory`]: io::ErrorKind::OutOfMemory")]
	#[cfg_attr(not(feature = "std"), doc = "[`OutOfMemory`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.OutOfMemory>")]
	#[must_use]
	pub fn out_of_memory() -> Self {
		#[cfg(feature = "std")]
		{
			let inner = Inner::Io(io::ErrorKind::OutOfMemory.into());
			Self { inner, _source: PhantomData }
		}

		#[cfg(not(feature = "std"))]
		{
			let inner = Inner::OutOfMemory;
			Self { inner, _source: PhantomData }
		}
	}

	/// Constructs an [`UnexpectedEof`] error.
	///
	#[cfg_attr(feature = "std",      doc = "[`UnexpectedEof`]: io::ErrorKind::UnexpectedEof")]
	#[cfg_attr(not(feature = "std"), doc = "[`UnexpectedEof`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.UnexpectedEof>")]
	#[must_use]
	pub fn unexpected_eof() -> Self {
		#[cfg(feature = "std")]
		{
			let inner = Inner::Io(io::ErrorKind::UnexpectedEof.into());
			Self { inner, _source: PhantomData }
		}

		#[cfg(not(feature = "std"))]
		{
			let inner = Inner::UnexpectedEof;
			Self { inner, _source: PhantomData }
		}
	}

	/// Constructs a [`WriteZero`] error.
	///
	#[cfg_attr(feature = "std",      doc = "[`WriteZero`]: io::ErrorKind::WriteZero")]
	#[cfg_attr(not(feature = "std"), doc = "[`WriteZero`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.WriteZero>")]
	#[must_use]
	pub fn write_zero() -> Self {
		#[cfg(feature = "std")]
		{
			let inner = Inner::Io(io::ErrorKind::WriteZero.into());
			Self { inner, _source: PhantomData }
		}

		#[cfg(not(feature = "std"))]
		{
			let inner = Inner::WriteZero;
			Self { inner, _source: PhantomData }
		}
	}
}

impl Display for Error {
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		#[cfg(feature = "std")]
		{
			let Inner::Io(ref e) = self.inner;
			Display::fmt(e, f)
		}

		#[cfg(not(feature = "std"))]
		{
			let s = match self.inner {
				Inner::InvalidData   => "invalid data",
				Inner::InvalidInput  => "invalid input parameter",
				Inner::OutOfMemory   => "out of memory",
				Inner::UnexpectedEof => "unexpected end of file",
				Inner::WriteZero     => "write zero",
			};

			f.write_str(s)
		}
	}
}

impl core::error::Error for Error {
	#[inline]
	fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
		#[cfg(feature = "std")]
		{
			let Inner::Io(ref e) = self.inner;
			Some(e)
		}

		#[cfg(not(feature = "std"))]
		{
			None
		}
	}
}

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

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

#[cfg(feature = "std")]
impl<W> From<IntoInnerError<W>> for Error {
	#[inline]
	fn from(value: IntoInnerError<W>) -> Self {
		let inner = Inner::Io(value.into());
		Self { inner, _source: PhantomData }
	}
}

#[cfg(feature = "alloc")]
impl From<NulError> for Error {
	#[inline]
	fn from(value: NulError) -> Self {
		#[cfg(feature = "std")]
		{
			let inner = Inner::Io(value.into());
			Self { inner, _source: PhantomData }
		}

		#[cfg(not(feature = "std"))]
		{
			let _ = value;

			let inner = Inner::InvalidData;
			Self { inner, _source: PhantomData }
		}
	}
}

#[expect(clippy::incompatible_msrv)]
#[cfg(feature = "std")]
#[rustversion::since(1.89)]
impl From<TryLockError> for Error {
	#[inline]
	fn from(value: TryLockError) -> Self {
		let inner = Inner::Io(value.into());
		Self { inner, _source: PhantomData }
	}
}

#[cfg(feature = "alloc")]
impl From<TryReserveError> for Error {
	#[inline]
	fn from(value: TryReserveError) -> Self {
		#[cfg(feature = "std")]
		{
			let inner = Inner::Io(value.into());
			Self { inner, _source: PhantomData }
		}

		#[cfg(not(feature = "std"))]
		{
			let _ = value;

			let inner = Inner::OutOfMemory;
			Self { inner, _source: PhantomData }
		}
	}
}

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

/// Inner variants for [`Error`].
#[cfg_attr(not(feature = "std"), repr(usize))]
#[derive(Debug)]
enum Inner {
	/// An input/output error.
	#[cfg(feature = "std")]
	Io(io::Error),

	/// See [`std::io::ErrorKind::InvalidData`].
	///
	/// [`std::io::ErrorKind::InvalidData`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.InvalidData>
	#[cfg(not(feature = "std"))]
	InvalidData,

	/// See [`std::io::ErrorKind::InvalidInput`].
	///
	/// [`std::io::ErrorKind::InvalidInput`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.InvalidInput>
	#[cfg(not(feature = "std"))]
	InvalidInput,

	/// See [`std::io::ErrorKind::OutOfMemory`].
	///
	/// [`std::io::ErrorKind::OutOfMemory`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.OutOfMemory>
	#[cfg(not(feature = "std"))]
	OutOfMemory,

	/// See [`std::io::ErrorKind::UnexpectedEof`].
	///
	/// [`std::io::ErrorKind::UnexpectedEof`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.UnexpectedEof>
	#[cfg(not(feature = "std"))]
	UnexpectedEof,

	/// See [`std::io::ErrorKind::WriteZero`].
	///
	/// [`std::io::ErrorKind::WriteZero`]: <https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.WriteZero>
	#[cfg(not(feature = "std"))]
	WriteZero,
}