nibblecode 0.1.0

A serialization format based on rkyv
Documentation
//! An archived version of `Option`.

use core::hash::{Hash, Hasher};
use core::ops::{Deref, DerefMut};
use std::cmp::Ordering;

/// An archived [`Option`].
///
/// It functions identically to [`Option`] but has a different internal
/// representation to allow for archiving.
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum ArchivedOption<T> {
	/// No value
	None,
	/// Some value `T`
	Some(T),
}

impl<T> ArchivedOption<T> {
	/// Transforms the `ArchivedOption<T>` into a `Result<T, E>`, mapping
	/// `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
	pub fn ok_or<E>(self, err: E) -> Result<T, E> {
		if let ArchivedOption::Some(x) = self {
			Ok(x)
		} else {
			Err(err)
		}
	}

	/// Returns the contained [`Some`] value, consuming the `self` value.
	pub fn unwrap(self) -> T {
		if let ArchivedOption::Some(value) = self {
			value
		} else {
			panic!("called `ArchivedOption::unwrap()` on a `None` value")
		}
	}

	/// Returns the contained [`Some`] value or a provided default.
	pub fn unwrap_or(self, default: T) -> T {
		if let ArchivedOption::Some(value) = self {
			value
		} else {
			default
		}
	}

	/// Returns the contained [`Some`] value or computes it from a closure.
	pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
		if let ArchivedOption::Some(value) = self {
			value
		} else {
			f()
		}
	}

	/// Returns `true` if the option is a `None` value.
	pub fn is_none(&self) -> bool {
		matches!(self, ArchivedOption::None)
	}

	/// Returns `true` if the option is a `Some` value.
	pub fn is_some(&self) -> bool {
		matches!(self, ArchivedOption::Some(_))
	}

	/// Converts to an `Option<&T>`.
	pub const fn as_ref(&self) -> Option<&T> {
		if let ArchivedOption::Some(value) = self {
			Some(value)
		} else {
			None
		}
	}

	/// Converts to an `Option<&mut T>`.
	pub fn as_mut(&mut self) -> Option<&mut T> {
		if let ArchivedOption::Some(value) = self {
			Some(value)
		} else {
			None
		}
	}

	/// Inserts `v` into the option if it is `None`, then returns a mutable
	/// reference to the contained value.
	pub fn get_or_insert(&mut self, v: T) -> &mut T {
		self.get_or_insert_with(move || v)
	}

	/// Inserts a value computed from `f` into the option if it is `None`, then
	/// returns a mutable reference to the contained value.
	pub fn get_or_insert_with<F: FnOnce() -> T>(&mut self, f: F) -> &mut T {
		if let ArchivedOption::Some(value) = self {
			value
		} else {
			*self = ArchivedOption::Some(f());
			unsafe { self.as_mut().unwrap_unchecked() }
		}
	}
}

impl<T: Deref> ArchivedOption<T> {
	/// Converts from `&ArchivedOption<T>` to `Option<&T::Target>`.
	///
	/// Leaves the original `ArchivedOption` in-place, creating a new one with a
	/// reference to the original one, additionally coercing the contents
	/// via `Deref`.
	pub fn as_deref(&self) -> Option<&<T as Deref>::Target> {
		self.as_ref().map(Deref::deref)
	}
}

impl<T: DerefMut> ArchivedOption<T> {
	/// Converts from `&mut ArchivedOption<T>` to `Option<&mut T::Target>`.
	///
	/// Leaves the original `ArchivedOption` in-place, creating a new `Option`
	/// with a mutable reference to the inner type's `Deref::Target` type.
	pub fn as_deref_mut(&mut self) -> Option<&mut <T as Deref>::Target> {
		self.as_mut().map(DerefMut::deref_mut)
	}
}

impl<T: Eq> Eq for ArchivedOption<T> {}

impl<T: Hash> Hash for ArchivedOption<T> {
	fn hash<H: Hasher>(&self, state: &mut H) {
		self.as_ref().hash(state);
	}
}

impl<T: Ord> Ord for ArchivedOption<T> {
	fn cmp(&self, other: &Self) -> Ordering {
		self.as_ref().cmp(&other.as_ref())
	}
}

impl<T: PartialEq> PartialEq for ArchivedOption<T> {
	fn eq(&self, other: &Self) -> bool {
		self.as_ref().eq(&other.as_ref())
	}
}

impl<T: PartialOrd> PartialOrd for ArchivedOption<T> {
	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
		self.as_ref().partial_cmp(&other.as_ref())
	}
}

impl<T, U: PartialOrd<T>> PartialOrd<Option<T>> for ArchivedOption<U> {
	fn partial_cmp(&self, other: &Option<T>) -> Option<Ordering> {
		match (self, other) {
			(ArchivedOption::None, None) => Some(Ordering::Equal),
			(ArchivedOption::None, Some(_)) => Some(Ordering::Less),
			(ArchivedOption::Some(_), None) => Some(Ordering::Greater),
			(ArchivedOption::Some(self_value), Some(other_value)) => {
				self_value.partial_cmp(other_value)
			}
		}
	}
}

impl<T, U: PartialEq<T>> PartialEq<Option<T>> for ArchivedOption<U> {
	fn eq(&self, other: &Option<T>) -> bool {
		if let ArchivedOption::Some(self_value) = self
			&& let Some(other_value) = other
		{
			self_value.eq(other_value)
		} else {
			self.is_none() && other.is_none()
		}
	}
}

impl<T> From<T> for ArchivedOption<T> {
	/// Moves `val` into a new [`Some`].
	///
	/// # Examples
	///
	/// ```
	/// # use nibblecode::option::ArchivedOption;
	/// let o: ArchivedOption<u8> = ArchivedOption::from(67);
	///
	/// assert!(matches!(o, ArchivedOption::Some(67)));
	/// ```
	fn from(val: T) -> ArchivedOption<T> {
		ArchivedOption::Some(val)
	}
}

#[cfg(test)]
mod tests {
	#[test]
	fn partial_ord_option() {
		use core::cmp::Ordering;

		use super::ArchivedOption;

		let a: ArchivedOption<u8> = ArchivedOption::Some(42);
		let b = Some(42);
		assert_eq!(Some(Ordering::Equal), a.partial_cmp(&b));

		let a: ArchivedOption<u8> = ArchivedOption::Some(1);
		let b = Some(2);
		assert_eq!(Some(Ordering::Less), a.partial_cmp(&b));

		let a: ArchivedOption<u8> = ArchivedOption::Some(2);
		let b = Some(1);
		assert_eq!(Some(Ordering::Greater), a.partial_cmp(&b));
	}
}