#![cfg_attr(docsrs, feature(doc_cfg))]
#![no_std]
use core::{
convert, mem,
ops::{Deref, DerefMut},
};
#[cfg(feature = "nullable")]
use solana_nullable::{MaybeNull, MaybeNullError, Nullable};
#[repr(C)]
#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum COption<T> {
None,
Some(T),
}
impl<T> COption<T> {
#[must_use = "if you intended to assert that this has a value, consider `.unwrap()` instead"]
#[inline]
pub fn is_some(&self) -> bool {
match *self {
COption::Some(_) => true,
COption::None => false,
}
}
#[must_use = "if you intended to assert that this doesn't have a value, consider \
`.and_then(|| panic!(\"`COption` had a value when expected `COption::None`\"))` instead"]
#[inline]
pub fn is_none(&self) -> bool {
!self.is_some()
}
#[must_use]
#[inline]
pub fn contains<U>(&self, x: &U) -> bool
where
U: PartialEq<T>,
{
match self {
COption::Some(y) => x == y,
COption::None => false,
}
}
#[inline]
pub fn as_ref(&self) -> COption<&T> {
match *self {
COption::Some(ref x) => COption::Some(x),
COption::None => COption::None,
}
}
#[inline]
pub fn as_mut(&mut self) -> COption<&mut T> {
match *self {
COption::Some(ref mut x) => COption::Some(x),
COption::None => COption::None,
}
}
#[inline]
pub fn expect(self, msg: &str) -> T {
match self {
COption::Some(val) => val,
COption::None => expect_failed(msg),
}
}
#[inline]
pub fn unwrap(self) -> T {
match self {
COption::Some(val) => val,
COption::None => panic!("called `COption::unwrap()` on a `COption::None` value"),
}
}
#[inline]
pub fn unwrap_or(self, def: T) -> T {
match self {
COption::Some(x) => x,
COption::None => def,
}
}
#[inline]
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
COption::Some(x) => x,
COption::None => f(),
}
}
#[inline]
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> COption<U> {
match self {
COption::Some(x) => COption::Some(f(x)),
COption::None => COption::None,
}
}
#[inline]
pub fn map_or<U, F: FnOnce(T) -> U>(self, default: U, f: F) -> U {
match self {
COption::Some(t) => f(t),
COption::None => default,
}
}
#[inline]
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
match self {
COption::Some(t) => f(t),
COption::None => default(),
}
}
#[inline]
pub fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
COption::Some(v) => Ok(v),
COption::None => Err(err),
}
}
#[inline]
pub fn ok_or_else<E, F: FnOnce() -> E>(self, err: F) -> Result<T, E> {
match self {
COption::Some(v) => Ok(v),
COption::None => Err(err()),
}
}
#[inline]
pub fn and<U>(self, optb: COption<U>) -> COption<U> {
match self {
COption::Some(_) => optb,
COption::None => COption::None,
}
}
#[inline]
pub fn and_then<U, F: FnOnce(T) -> COption<U>>(self, f: F) -> COption<U> {
match self {
COption::Some(x) => f(x),
COption::None => COption::None,
}
}
#[inline]
pub fn filter<P: FnOnce(&T) -> bool>(self, predicate: P) -> Self {
if let COption::Some(x) = self {
if predicate(&x) {
return COption::Some(x);
}
}
COption::None
}
#[inline]
pub fn or(self, optb: COption<T>) -> COption<T> {
match self {
COption::Some(_) => self,
COption::None => optb,
}
}
#[inline]
pub fn or_else<F: FnOnce() -> COption<T>>(self, f: F) -> COption<T> {
match self {
COption::Some(_) => self,
COption::None => f(),
}
}
#[inline]
pub fn xor(self, optb: COption<T>) -> COption<T> {
match (self, optb) {
(COption::Some(a), COption::None) => COption::Some(a),
(COption::None, COption::Some(b)) => COption::Some(b),
_ => COption::None,
}
}
#[inline]
pub fn get_or_insert(&mut self, v: T) -> &mut T {
self.get_or_insert_with(|| v)
}
#[inline]
pub fn get_or_insert_with<F: FnOnce() -> T>(&mut self, f: F) -> &mut T {
if let COption::None = *self {
*self = COption::Some(f())
}
match *self {
COption::Some(ref mut v) => v,
COption::None => unreachable!(),
}
}
#[inline]
pub fn replace(&mut self, value: T) -> COption<T> {
mem::replace(self, COption::Some(value))
}
}
impl<T: Copy> COption<&T> {
pub fn copied(self) -> COption<T> {
self.map(|&t| t)
}
}
impl<T: Copy> COption<&mut T> {
pub fn copied(self) -> COption<T> {
self.map(|&mut t| t)
}
}
impl<T: Clone> COption<&T> {
pub fn cloned(self) -> COption<T> {
self.map(|t| t.clone())
}
}
impl<T: Clone> COption<&mut T> {
pub fn cloned(self) -> COption<T> {
self.map(|t| t.clone())
}
}
impl<T: Default> COption<T> {
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
COption::Some(x) => x,
COption::None => T::default(),
}
}
}
impl<T: Deref> COption<T> {
pub fn as_deref(&self) -> COption<&T::Target> {
self.as_ref().map(|t| t.deref())
}
}
impl<T: DerefMut> COption<T> {
pub fn as_deref_mut(&mut self) -> COption<&mut T::Target> {
self.as_mut().map(|t| t.deref_mut())
}
}
impl<T, E> COption<Result<T, E>> {
#[inline]
pub fn transpose(self) -> Result<COption<T>, E> {
match self {
COption::Some(Ok(x)) => Ok(COption::Some(x)),
COption::Some(Err(e)) => Err(e),
COption::None => Ok(COption::None),
}
}
}
#[inline(never)]
#[cold]
fn expect_failed(msg: &str) -> ! {
panic!("{}", msg)
}
impl<T: Clone> Clone for COption<T> {
#[inline]
fn clone(&self) -> Self {
match self {
COption::Some(x) => COption::Some(x.clone()),
COption::None => COption::None,
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
match (self, source) {
(COption::Some(to), COption::Some(from)) => to.clone_from(from),
(to, from) => to.clone_from(from),
}
}
}
impl<T> Default for COption<T> {
#[inline]
fn default() -> COption<T> {
COption::None
}
}
impl<T> From<T> for COption<T> {
fn from(val: T) -> COption<T> {
COption::Some(val)
}
}
impl<'a, T> From<&'a COption<T>> for COption<&'a T> {
fn from(o: &'a COption<T>) -> COption<&'a T> {
o.as_ref()
}
}
impl<'a, T> From<&'a mut COption<T>> for COption<&'a mut T> {
fn from(o: &'a mut COption<T>) -> COption<&'a mut T> {
o.as_mut()
}
}
impl<T> COption<COption<T>> {
#[inline]
pub fn flatten(self) -> COption<T> {
self.and_then(convert::identity)
}
}
impl<T> From<Option<T>> for COption<T> {
fn from(option: Option<T>) -> Self {
match option {
Some(value) => COption::Some(value),
None => COption::None,
}
}
}
impl<T> From<COption<T>> for Option<T> {
fn from(coption: COption<T>) -> Self {
match coption {
COption::Some(value) => Some(value),
COption::None => None,
}
}
}
#[cfg(feature = "nullable")]
impl<T: Nullable> TryFrom<COption<T>> for MaybeNull<T> {
type Error = MaybeNullError;
fn try_from(value: COption<T>) -> Result<Self, Self::Error> {
match value {
COption::Some(value) if value.is_none() => Err(MaybeNullError::NoneValueInSome),
COption::Some(value) => Ok(MaybeNull::from(value)),
COption::None => Ok(MaybeNull::from(T::NONE)),
}
}
}
#[cfg(feature = "nullable")]
impl<T: Nullable> From<MaybeNull<T>> for COption<T> {
fn from(value: MaybeNull<T>) -> Self {
match value.get() {
Some(value) => COption::Some(value),
None => COption::None,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_from_rust_option() {
let option = Some(99u64);
let c_option: COption<u64> = option.into();
assert_eq!(c_option, COption::Some(99u64));
let expected = c_option.into();
assert_eq!(option, expected);
let option = None;
let c_option: COption<u64> = option.into();
assert_eq!(c_option, COption::None);
let expected = c_option.into();
assert_eq!(option, expected);
}
#[cfg(feature = "nullable")]
mod maybe_null_tests {
use {
super::*,
solana_nullable::{MaybeNullError, Nullable},
};
#[derive(Clone, Copy, Debug, PartialEq)]
struct TestVal(u32);
impl Nullable for TestVal {
const NONE: Self = TestVal(0);
}
#[test]
fn test_maybe_null_try_from_coption_some() {
let some = COption::Some(TestVal(42));
assert_eq!(
MaybeNull::try_from(some).unwrap(),
MaybeNull::from(TestVal(42)),
);
}
#[test]
fn test_maybe_null_try_from_coption_none() {
let none: COption<TestVal> = COption::None;
assert_eq!(
MaybeNull::try_from(none).unwrap(),
MaybeNull::from(TestVal::NONE),
);
}
#[test]
fn test_maybe_null_try_from_coption_rejects_none_value() {
let invalid = COption::Some(TestVal(0));
assert_eq!(
MaybeNull::try_from(invalid).unwrap_err(),
MaybeNullError::NoneValueInSome,
);
}
#[test]
fn test_maybe_null_coption_roundtrip() {
let some = COption::Some(TestVal(42));
let maybe_null: MaybeNull<TestVal> = MaybeNull::try_from(some).unwrap();
let coption: COption<TestVal> = maybe_null.into();
assert_eq!(coption, COption::Some(TestVal(42)));
let none: COption<TestVal> = COption::None;
let maybe_null: MaybeNull<TestVal> = MaybeNull::try_from(none).unwrap();
let coption: COption<TestVal> = maybe_null.into();
assert_eq!(coption, COption::None);
}
}
}