Skip to main content

osom_lib_primitives/
coption.rs

1//! Holds the [`COption`] enum.
2#![allow(clippy::derivable_impls)]
3
4use core::fmt::Display;
5use core::hash::Hash;
6use core::{mem::forget, ptr};
7
8use osom_lib_reprc::{macros::reprc, traits::ReprC};
9use osom_lib_try_clone::TryClone;
10
11/// This enum is essentially the same as the standard `Option`,
12/// except it is `#[repr(C)]` and thus safe to use a across the
13/// ffi boundaries.
14///
15/// It additionally provides `const` variants of `Option` methods.
16#[reprc]
17#[repr(u8)]
18#[derive(Debug)]
19#[must_use]
20pub enum COption<TValue>
21where
22    TValue: ReprC,
23{
24    None = 0,
25    Some(TValue) = 1,
26}
27
28impl<TValue> COption<TValue>
29where
30    TValue: ReprC,
31{
32    /// Returns `true` if the enum holds [`COption::Some`],
33    /// `false` otherwise.
34    #[inline]
35    pub const fn is_some(&self) -> bool {
36        match self {
37            COption::Some(_) => true,
38            COption::None => false,
39        }
40    }
41
42    /// Returns `true` if the enum holds [`COption::None`],
43    /// `false` otherwise.
44    #[inline]
45    pub const fn is_none(&self) -> bool {
46        !self.is_some()
47    }
48
49    /// Unwraps currently stored [`COption::Some`] value.
50    ///
51    /// # Panics
52    ///
53    /// Only when `self` actually holds a [`COption::None`] value.
54    #[inline]
55    pub fn expect(self, message: &str) -> TValue {
56        match &self {
57            COption::Some(value) => {
58                let data = unsafe { ptr::read(value) };
59                forget(self);
60                data
61            }
62            COption::None => {
63                panic!("`COption::expect()`: {}.", message);
64            }
65        }
66    }
67
68    /// Unwraps currently stored [`COption::Some`] value.
69    ///
70    /// # Panics
71    ///
72    /// Only when `self` actually holds a [`COption::None`] value.
73    #[inline]
74    pub const fn unwrap(self) -> TValue {
75        match &self {
76            COption::Some(value) => {
77                let data = unsafe { ptr::read(value) };
78                forget(self);
79                data
80            }
81            COption::None => {
82                panic!("called `COption::unwrap()` on a `None` value.");
83            }
84        }
85    }
86
87    /// Unwraps currently stored [`COption::Some`] value.
88    ///
89    /// # Safety
90    ///
91    /// This function does not verify whether the stored value
92    /// is actually [`COption::Some`]. The behaviour is undefined if it is not.
93    #[inline]
94    pub const unsafe fn unwrap_unchecked(self) -> TValue {
95        match &self {
96            COption::Some(value) => {
97                let data = unsafe { ptr::read(value) };
98                forget(self);
99                data
100            }
101            COption::None => {
102                unsafe { core::hint::unreachable_unchecked() };
103            }
104        }
105    }
106
107    /// Converts [`COption`] into standard `Option`.
108    #[inline]
109    pub const fn into_option(self) -> Option<TValue> {
110        match &self {
111            COption::Some(value) => {
112                let data = unsafe { ptr::read(value) };
113                forget(self);
114                Some(data)
115            }
116            COption::None => {
117                forget(self);
118                None
119            }
120        }
121    }
122
123    /// Converts standard `Option` into [`COption`].
124    #[inline]
125    #[allow(clippy::single_match_else)]
126    pub const fn from_option(option: Option<TValue>) -> Self {
127        match &option {
128            Some(value) => {
129                let data = unsafe { ptr::read(value) };
130                forget(option);
131                COption::Some(data)
132            }
133            None => {
134                forget(option);
135                COption::None
136            }
137        }
138    }
139
140    /// Converts [`COption`] into a reference to the stored value.
141    #[inline]
142    pub const fn as_ref(&self) -> COption<&TValue> {
143        match *self {
144            COption::Some(ref value) => COption::Some(value),
145            COption::None => COption::None,
146        }
147    }
148
149    /// Converts [`COption`] into a mutable reference to the stored value.
150    #[inline]
151    pub const fn as_mut(&mut self) -> COption<&mut TValue> {
152        match *self {
153            COption::Some(ref mut value) => COption::Some(value),
154            COption::None => COption::None,
155        }
156    }
157}
158
159impl<TValue: ReprC> From<Option<TValue>> for COption<TValue> {
160    fn from(option: Option<TValue>) -> Self {
161        COption::from_option(option)
162    }
163}
164
165impl<TValue: ReprC> From<COption<TValue>> for Option<TValue> {
166    fn from(option: COption<TValue>) -> Self {
167        option.into_option()
168    }
169}
170
171impl<TValue: ReprC> Default for COption<TValue> {
172    fn default() -> Self {
173        COption::None
174    }
175}
176
177impl<TValue: ReprC + Clone> Clone for COption<TValue> {
178    fn clone(&self) -> Self {
179        match self {
180            COption::Some(value) => COption::Some(value.clone()),
181            COption::None => COption::None,
182        }
183    }
184}
185
186impl<TValue: ReprC + TryClone> TryClone for COption<TValue> {
187    type Error = <TValue as TryClone>::Error;
188    fn try_clone(&self) -> Result<Self, Self::Error> {
189        match self {
190            COption::Some(value) => Ok(COption::Some(value.try_clone()?)),
191            COption::None => Ok(COption::None),
192        }
193    }
194}
195
196impl<TValue: ReprC + PartialEq> PartialEq for COption<TValue> {
197    fn eq(&self, other: &Self) -> bool {
198        match (self, other) {
199            (Self::Some(l0), Self::Some(r0)) => l0 == r0,
200            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
201        }
202    }
203}
204
205impl<TValue: ReprC + Eq> Eq for COption<TValue> {}
206
207impl<TValue: ReprC + Hash> Hash for COption<TValue> {
208    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
209        core::mem::discriminant(self).hash(state);
210        if let COption::Some(value) = self {
211            value.hash(state);
212        }
213    }
214}
215
216impl<TValue: ReprC + Display> Display for COption<TValue> {
217    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
218        match self {
219            COption::Some(value) => {
220                f.write_str("Some(")?;
221                value.fmt(f)?;
222                f.write_str(")")?;
223                Ok(())
224            }
225            COption::None => write!(f, "None"),
226        }
227    }
228}