oct 0.25.0

Octonary transcodings.
Documentation
// Copyright 2024-2025 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 [`Error`] error type.

use crate::io::ErrorKind;

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

#[cfg(feature = "alloc")]
use alloc::boxed::Box;

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

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

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

/// An input/output error.
#[derive(Debug)]
pub struct Error(Inner);

impl Error {
	/// Constructs a new input/output error.
	#[cfg(feature = "alloc")]
	#[inline]
	#[must_use]
	pub fn new<E: Into<Box<dyn core::error::Error + Send + Sync>>>(kind: ErrorKind, source: E) -> Self {
		#[cfg(feature = "std")]
		let e = Inner::new(kind.into(), source).into();

		// Fall back to our own implementation.

		#[cfg(not(feature = "std"))]
		let e = Self::new_large(kind, source);

		e
	}

	/// Constructs a new [`Small`] error.
	///
	/// [`Small`]: Inner::Small
	#[cfg(not(feature = "std"))]
	#[inline]
	#[must_use]
	pub(crate) const fn new_small(kind: ErrorKind, message: Option<&'static str>) -> Self {
		let inner = Inner::Small { kind, message };
		Self(inner)
	}

	/// Constructs a new [`Large`] error.
	///
	/// [`Large`]: Inner::Large
	#[cfg(all(not(feature = "std"), feature = "alloc"))]
	#[inline]
	#[must_use]
	pub(crate) fn new_large<E: Into<Box<dyn core::error::Error + Send + Sync>>>(kind: ErrorKind, source: E) -> Self {
		let inner = Inner::Large { kind, source: source.into() };
		Self(inner)
	}

	/// Constructs a new input/output error from an
	/// operation system error.
	#[cfg(feature = "std")]
	#[inline]
	#[must_use]
	pub fn from_raw_os_error(raw: i32) -> Self {
		let inner = Inner::from_raw_os_error(raw);
		Self(inner)
	}

	/// Retrieves the last operating system error that
	/// occured, if any.
	#[cfg(feature = "std")]
	#[inline]
	#[must_use]
	pub fn last_os_error() -> Self {
		let inner = Inner::last_os_error();
		Self(inner)
	}

	/// Gets a constant reference to the inner error.
	#[must_use]
	pub fn get_ref(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
		#[cfg(feature = "std")]
		let e = self.0.get_ref();

		#[cfg(not(feature = "std"))]
		let e = match self.0 {
			#[cfg(feature = "alloc")]
			Inner::Large { ref source, .. } => Some(&**source),

			_ => None,
		};

		e
	}

	/// Gets a mutable reference to the inner error.
	#[must_use]
	pub fn get_mut(&mut self) -> Option<&mut (dyn core::error::Error + Send + Sync + 'static)> {
		#[cfg(feature = "std")]
		let e = self.0.get_mut();

		#[cfg(not(feature = "std"))]
		let e = match self.0 {
			#[cfg(feature = "alloc")]
			Inner::Large { ref mut source, .. } => Some(&mut **source),

			_ => None,
		};

		e
	}

	/// Retrieves the kind of error.
	#[must_use]
	pub fn kind(&self) -> ErrorKind {
		#[cfg(feature = "std")]
		let kind = self.0.kind().into();

		#[cfg(not(feature = "std"))]
		let kind = match self.0 {
			Inner::Small { kind, .. } => kind,

			#[cfg(feature = "alloc")]
			Inner::Large { kind, .. } => kind,
		};

		kind
	}

	/// Retrieves the raw, equivalent, operating system
	/// error.
	///
	/// If the error doesn't have an associated
	/// operating system error, this method will return
	/// [`None`].
	#[cfg(feature = "std")]
	#[inline]
	#[must_use]
	pub fn raw_os_error(&self) -> Option<i32> {
		self.0.raw_os_error()
	}

	/// Tests if the error is an interrupt.
	#[inline]
	#[must_use]
	pub(crate) fn is_interrupted(&self) -> bool {
		matches!(self.kind(), ErrorKind::Interrupted)
	}

	/// Deconstructs the error into the internal error.
	#[cfg(feature = "alloc")]
	#[must_use]
	pub fn into_inner(self) -> Option<Box<dyn core::error::Error + Send + Sync>> {
		#[cfg(feature = "std")]
		let e = self.0.into_inner();

		#[cfg(not(feature = "std"))]
		let e = match self.0 {
			Inner::Large { source, .. } => Some(source),

			_ => None,
		};

		e
	}
}

impl Display for Error {
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		Display::fmt(&self.0, f)
	}
}

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

		#[cfg(not(feature = "std"))]
		let e = match self.0 {
			#[cfg(feature = "alloc")]
			Inner::Large { ref source, .. } => source.source(),

			_ => None,
		};

		e
	}
}

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

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

		#[cfg(not(feature = "std"))]
		let e = Self::new_small(value, None);

		e
	}
}

#[cfg(feature = "alloc")]
impl From<NulError> for Error {
	#[cfg_attr(not(feature = "std"), expect(unused))]
	#[inline]
	fn from(value: NulError) -> Self {
		#[cfg(feature = "std")]
		let e = Self(value.into());

		#[cfg(not(feature = "std"))]
		let e = Self::new(ErrorKind::InvalidData, "nul byte found");

		e
	}
}

#[cfg(feature = "std")]
impl From<TryLockError> for Error {
	#[inline]
	fn from(value: TryLockError) -> Self {
		Self(value.into())
	}
}

#[cfg(feature = "alloc")]
impl From<TryReserveError> for Error {
	#[cfg_attr(not(feature = "std"), expect(unused))]
	#[inline]
	fn from(value: TryReserveError) -> Self {
		#[cfg(feature = "std")]
		let e = Self(value.into());

		#[cfg(not(feature = "std"))]
		let e = ErrorKind::OutOfMemory.into();

		e
	}
}

/// The inner type for [`Error`].
#[cfg(feature = "std")]
type Inner = std::io::Error;

/// Inner variants for [`Error`].
#[cfg(not(feature = "std"))]
#[derive(Debug)]
enum Inner {
	/// Denotes a small input/output error.
	Small {
		/// The kind of error.
		kind: ErrorKind,

		/// The error message.
		message: Option<&'static str>,
	},

	/// Denotes a large (heap-allocated) input/output
	/// error.
	#[cfg(feature = "alloc")]
	Large {
		/// The kind of error.
		kind: ErrorKind,

		/// The source error.
		source: Box<dyn core::error::Error + Send + Sync>,
	},
}


#[cfg(not(feature = "std"))]
impl Display for Inner {
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		match *self {
			#[cfg(not(feature = "std"))]
			Self::Small { ref kind, message: None } => {
				write!(f, "{kind}")
			}

			#[cfg(not(feature = "std"))]
			Self::Small { ref kind, message: Some(message) } => {
				write!(f, "{kind}: {message}")
			}

			#[cfg(all(not(feature = "std"), feature = "alloc"))]
			Self::Large { ref kind, ref source } => {
				write!(f, "{kind}: {source}")
			}

			#[cfg(feature = "std")]
			Self::Std(ref e) => {
				write!(f, "{e}")
			}
		}
	}
}

#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
	#[inline]
	fn from(value: Error) -> Self {
		value.0
	}
}