Skip to main content

osom_lib_primitives/
cresult.rs

1//! Holds the [`CResult`] enum.
2
3use core::{mem::forget, ptr};
4
5use osom_lib_reprc::{macros::reprc, traits::ReprC};
6
7/// This enum is essentially the same as the standard `Result`,
8/// except it is `#[repr(C)]` and thus safe to use a across the
9/// ffi boundaries.
10///
11/// It additionally provides `const` variants of `Result` methods.
12///
13/// Also note that the layout is fixed, and unlike standard Result,
14/// it doesn't depend on `TOk`, `TErr`.
15#[reprc]
16#[repr(u8)]
17#[derive(Debug)]
18#[must_use]
19pub enum CResult<TOk, TErr>
20where
21    TOk: ReprC,
22    TErr: ReprC,
23{
24    Ok(TOk) = 0,
25    Err(TErr) = 1,
26}
27
28impl<TOk, TErr> CResult<TOk, TErr>
29where
30    TOk: ReprC,
31    TErr: ReprC,
32{
33    /// Returns `true` if the enum holds [`CResult::Ok`],
34    /// `false` otherwise.
35    #[inline]
36    pub const fn is_ok(&self) -> bool {
37        match self {
38            CResult::Ok(_) => true,
39            CResult::Err(_) => false,
40        }
41    }
42
43    /// Returns `true` if the enum holds [`CResult::Err`],
44    /// `false` otherwise.
45    #[inline]
46    pub const fn is_err(&self) -> bool {
47        !self.is_ok()
48    }
49
50    /// Unwraps currently stored [`CResult::Ok`] value.
51    ///
52    /// # Panics
53    ///
54    /// Only when `self` actually holds a [`CResult::Err`] value.
55    #[inline]
56    pub fn expect(self, message: &str) -> TOk {
57        match &self {
58            CResult::Ok(ok) => {
59                let data = unsafe { ptr::read(ok) };
60                forget(self);
61                data
62            }
63            CResult::Err(_) => {
64                panic!("`CResult::expect()`: {}.", message);
65            }
66        }
67    }
68
69    /// Unwraps currently stored [`CResult::Ok`] value.
70    ///
71    /// # Panics
72    ///
73    /// Only when `self` actually holds a [`CResult::Err`] value.
74    #[inline]
75    pub const fn unwrap(self) -> TOk {
76        match &self {
77            CResult::Ok(ok) => {
78                let data = unsafe { ptr::read(ok) };
79                forget(self);
80                data
81            }
82            CResult::Err(_) => {
83                panic!("called `CResult::unwrap()` on an `Err` value.");
84            }
85        }
86    }
87
88    /// Unwraps currently stored [`CResult::Ok`] value.
89    ///
90    /// # Safety
91    ///
92    /// This function does not verify whether the stored value
93    /// is actually [`CResult::Ok`]. The behaviour is undefined if it is not.
94    #[inline]
95    pub const unsafe fn unwrap_unchecked(self) -> TOk {
96        match &self {
97            CResult::Ok(ok) => {
98                let data = unsafe { ptr::read(ok) };
99                forget(self);
100                data
101            }
102            CResult::Err(_) => {
103                unsafe { core::hint::unreachable_unchecked() };
104            }
105        }
106    }
107
108    /// Unwraps currently stored [`CResult::Err`] value.
109    ///
110    /// # Panics
111    ///
112    /// Only when `self` actually holds an [`CResult::Ok`] value.
113    #[inline]
114    pub const fn unwrap_err(self) -> TErr {
115        match &self {
116            CResult::Ok(_) => {
117                panic!("called `CResult::unwrap_err()` on an `Ok` value.");
118            }
119            CResult::Err(err) => {
120                let data = unsafe { ptr::read(err) };
121                forget(self);
122                data
123            }
124        }
125    }
126
127    /// Unwraps currently stored [`CResult::Err`] value.
128    ///
129    /// # Safety
130    ///
131    /// This function does not verify whether the stored value
132    /// is actually [`CResult::Err`]. The behaviour is undefined if it is not.
133    #[inline]
134    pub const unsafe fn unwrap_err_unchecked(self) -> TErr {
135        match &self {
136            CResult::Ok(_) => {
137                unsafe { core::hint::unreachable_unchecked() };
138            }
139            CResult::Err(err) => {
140                let data = unsafe { ptr::read(err) };
141                forget(self);
142                data
143            }
144        }
145    }
146
147    #[allow(clippy::missing_errors_doc)]
148    /// Converts [`CResult`] into standard `Result`.
149    #[inline]
150    pub const fn into_result(self) -> Result<TOk, TErr> {
151        match &self {
152            CResult::Ok(ok) => {
153                let data = unsafe { ptr::read(ok) };
154                forget(self);
155                Ok(data)
156            }
157            CResult::Err(err) => {
158                let data = unsafe { ptr::read(err) };
159                forget(self);
160                Err(data)
161            }
162        }
163    }
164
165    /// Converts standard `Result` into [`CResult`].
166    #[inline]
167    pub const fn from_result(result: Result<TOk, TErr>) -> Self {
168        match &result {
169            Ok(ok) => {
170                let data = unsafe { ptr::read(ok) };
171                forget(result);
172                Self::Ok(data)
173            }
174            Err(err) => {
175                let data = unsafe { ptr::read(err) };
176                forget(result);
177                Self::Err(data)
178            }
179        }
180    }
181}
182
183impl<TOk, TErr> From<CResult<TOk, TErr>> for Result<TOk, TErr>
184where
185    TOk: ReprC,
186    TErr: ReprC,
187{
188    fn from(value: CResult<TOk, TErr>) -> Self {
189        value.into_result()
190    }
191}
192
193impl<TOk, TErr> From<Result<TOk, TErr>> for CResult<TOk, TErr>
194where
195    TOk: ReprC,
196    TErr: ReprC,
197{
198    fn from(value: Result<TOk, TErr>) -> Self {
199        Self::from_result(value)
200    }
201}