diff --git a/src/either.rs b/src/either.rs new file mode 100644 index 0000000..6147957 --- /dev/null +++ b/src/either.rs @@ -0,0 +1,507 @@ +use std::{convert::identity, hint, ops::{Deref, DerefMut}}; + +use Either::{Inl, Inr}; +use crate::convert::{clone, clone_mut, copy, copy_mut}; +pub enum Either { + Inl(L), + Inr(R), +} + +impl Either { + #[inline] + #[must_use] + pub const fn as_ref(&self) -> Either<&L, &R> { + match self { + Inl(l) => Inl(l), + Inr(r) => Inr(r), + } + } + + #[inline] + #[must_use] + pub fn as_mut(&mut self) -> Either<&mut L, &mut R> { + match self { + Inl(l) => Inl(l), + Inr(r) => Inr(r), + } + } + + #[inline] + #[must_use] + pub fn as_deref(&self) -> Either<&::Target, &::Target> + where + L: Deref, + R: Deref, + { + match self { + Inl(l) => Inl(l), + Inr(r) => Inr(r), + } + } + + #[inline] + #[must_use] + pub fn as_deref_l(&self) -> Either<&::Target, &R> + where + L: Deref, + { + match self { + Inl(l) => Inl(l), + Inr(r) => Inr(r), + } + } + + #[inline] + #[must_use] + pub fn as_deref_r(&self) -> Either<&L, &::Target> + where + R: Deref, + { + match self { + Inl(l) => Inl(l), + Inr(r) => Inr(r), + } + } + + #[inline] + #[must_use] + pub fn as_deref_mut(&mut self) -> Either<&mut ::Target, &mut ::Target> + where + L: DerefMut, + R: DerefMut, + { + match self { + Inl(l) => Inl(l), + Inr(r) => Inr(r), + } + } + + #[inline] + #[must_use] + pub fn as_deref_mut_l(&mut self) -> Either<&mut ::Target, &mut R> + where + L: DerefMut, + { + match self { + Inl(l) => Inl(l), + Inr(r) => Inr(r), + } + } + + #[inline] + #[must_use] + pub fn as_deref_mut_r(&mut self) -> Either<&mut L, &mut ::Target> + where + R: DerefMut, + { + match self { + Inl(l) => Inl(l), + Inr(r) => Inr(r), + } + } + + #[inline] + #[must_use] + pub fn fold(self, f: F, g: G) -> T + where + F: FnOnce(L) -> T, + G: FnOnce(R) -> T, + { + match self { + Inl(l) => f(l), + Inr(r) => g(r), + } + } + + #[inline] + #[must_use] + pub fn fold_l R>(self, f: F) -> R { + self.fold(f, identity) + } + + #[inline] + #[must_use] + pub fn fold_r L>(self, f: F) -> L { + self.fold(identity, f) + } + + #[inline] + #[must_use] + pub fn select(self, if_l: T, if_r: T) -> T { + match self { + Inl(_) => if_l, + Inr(_) => if_r, + } + } + + #[inline] + #[must_use] + pub fn map(self, f: F, g: G) -> Either + where + F: FnOnce(L) -> T, + G: FnOnce(R) -> U, + { + self.fold(|l| Inl(f(l)), |r| Inr(g(r))) + } + + #[inline] + #[must_use] + pub fn map_l T>(self, f: F) -> Either { + self.fold(|l| Inl(f(l)), Inr) + } + + #[inline] + #[must_use] + pub fn map_r T>(self, f: F) -> Either { + self.fold(Inl, |r| Inr(f(r))) + } + + #[inline] + #[must_use] + pub fn is_l(self) -> bool { + self.select(true, false) + } + + #[inline] + #[must_use] + pub fn is_r(self) -> bool { + self.select(false, true) + } + + #[inline] + #[must_use] + pub fn flip(self) -> Either { + self.fold(Inr, Inl) + } + + #[inline] + #[must_use] + pub fn some_l(self) -> Option { + self.fold(Some, |_| None) + } + + #[inline] + #[must_use] + pub fn some_r(self) -> Option { + self.fold(|_| None, Some) + } + + #[inline] + #[must_use] + pub fn into_options(self) -> (Option, Option) { + self.fold(|l| (Some(l), None), |r| (None, Some(r))) + } + + #[inline] + pub fn ok_l_or(self, err: E) -> Result { + self.fold(Ok, |_| Err(err)) + } + + #[inline] + pub fn ok_l_or_else E>(self, err: F) -> Result { + self.fold(Ok, |r| Err(err(r))) + } + + #[inline] + pub fn ok_r_or(self, err: E) -> Result { + self.fold(|_| Err(err), Ok) + } + + #[inline] + pub fn ok_r_or_else E>(self, err: F) -> Result { + self.fold(|l| Err(err(l)), Ok) + } + + #[inline] + pub fn into_result(self) -> Result { + self.fold(Ok, Err) + } + + #[inline] + #[must_use] + pub fn unwrap_l_or(self, default: L) -> L { + self.fold_r(|_| default) + } + + #[inline] + #[must_use] + pub fn unwrap_r_or(self, default: R) -> R { + self.fold_l(|_| default) + } + + #[inline] + #[must_use] + pub fn unwrap_l_or_else(self, f: F) -> L + where + F: FnOnce() -> L, + { + self.fold_r(|_| f()) + } + + #[inline] + #[must_use] + pub fn unwrap_r_or_else(self, f: F) -> R + where + F: FnOnce() -> R, + { + self.fold_l(|_| f()) + } + + #[inline] + #[must_use] + pub fn unwrap_l_or_default(self) -> L + where + L: Default, + { + self.unwrap_l_or_else(Default::default) + } + + #[inline] + #[must_use] + pub fn unwrap_r_or_default(self) -> R + where + R: Default, + { + self.unwrap_r_or_else(Default::default) + } + + #[inline] + pub fn unwrap_l(self) -> L { + match self { + Inl(l) => l, + Inr(_) => panic!("called `Either::unwrap_l` on a `Inr` value"), + } + } + + #[inline] + pub fn unwrap_r(self) -> R { + match self { + Inl(_) => panic!("called `Either::unwrap_r` on a `Inl` value"), + Inr(r) => r, + } + } + + /// # Safety + /// The value must be `Inl`. Calling this method on an `Inr` value is undefined behaviour. + #[inline] + pub unsafe fn unwrap_l_unchecked(self) -> L { + match self { + Inl(l) => l, + // SAFETY: + // The caller is responsible for ensuring that the value is not `Inr`. + Inr(_) => hint::unreachable_unchecked(), + } + } + + /// # Safety + /// The value must be `Inr`. Calling this method on an `Inl` value is undefined behaviour. + #[inline] + pub unsafe fn unwrap_r_unchecked(self) -> R { + match self { + // SAFETY: + // The caller is responsible for ensuring that the value is not `Inl`. + Inl(_) => hint::unreachable_unchecked(), + Inr(r) => r, + } + } + + #[inline] + pub fn expect_l(self, msg: &str) -> L { + match self { + Inl(l) => l, + Inr(_) => panic!("{}", msg), + } + } + + #[inline] + pub fn expect_r(self, msg: &str) -> R { + match self { + Inl(_) => panic!("{}", msg), + Inr(r) => r, + } + } + + #[inline] + pub fn inspect(self, f: F, g: G) -> Either + where + F: FnOnce(&L), + G: FnOnce(&R), + { + match self { + Inl(ref l) => f(l), + Inr(ref r) => g(r), + }; + self + } + + #[inline] + pub fn inspect_l(self, f: F) -> Either + where + F: FnOnce(&L), + { + if let Inl(ref l) = self { + f(l); + } + self + } + + #[inline] + pub fn inspect_r(self, f: F) -> Either + where + F: FnOnce(&R), + { + if let Inr(ref r) = self { + f(r); + } + self + } +} + +impl Either<&L, &R> { + #[inline] + #[must_use] + pub fn cloned(self) -> Either + where + L: Clone, + R: Clone, + { + self.map(clone, clone) + } + + #[inline] + #[must_use] + pub fn copied(self) -> Either + where + L: Copy, + R: Copy, + { + self.map(copy, copy) + } +} + +impl Either<&L, R> { + #[inline] + #[must_use] + pub fn cloned_l(self) -> Either + where + L: Clone, + { + self.map_l(clone) + } + + #[inline] + #[must_use] + pub fn copied_l(self) -> Either + where + L: Copy, + { + self.map_l(copy) + } +} + +impl Either { + #[inline] + #[must_use] + pub fn cloned_r(self) -> Either + where + R: Clone, + { + self.map_r(clone) + } + + #[inline] + #[must_use] + pub fn copied_r(self) -> Either + where + R: Copy, + { + self.map_r(copy) + } +} + +impl Either<&mut L, &mut R> { + #[inline] + #[must_use] + pub fn cloned(self) -> Either + where + L: Clone, + R: Clone, + { + self.map(clone_mut, clone_mut) + } + + #[inline] + #[must_use] + pub fn copied(self) -> Either + where + L: Copy, + R: Copy, + { + self.map(copy_mut, copy_mut) + } +} + +impl Either<&mut L, R> { + #[inline] + #[must_use] + pub fn cloned_l(self) -> Either + where + L: Clone, + { + self.map_l(clone_mut) + } + + #[inline] + #[must_use] + pub fn copied_l(self) -> Either + where + L: Copy, + { + self.map_l(copy_mut) + } +} + +impl Either { + #[inline] + #[must_use] + pub fn cloned_r(self) -> Either + where + R: Clone, + { + self.map_r(clone_mut) + } + + #[inline] + #[must_use] + pub fn copied_r(self) -> Either + where + R: Copy, + { + self.map_r(copy_mut) + } +} + +impl Either { + #[inline] + #[must_use] + pub fn fold_symmetric(self) -> T { + self.fold(identity, identity) + } +} + +impl Clone for Either +where + L: Clone, + R: Clone, +{ + #[inline] + fn clone(&self) -> Self { + self.as_ref().map(::clone, ::clone) + } +} + +impl Copy for Either +where + L: Copy, + R: Copy, +{} diff --git a/src/lib.rs b/src/lib.rs index bd081ec..60d0467 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ pub mod convert; +pub mod either; pub mod strings;