osom_primitives/
cresult.rs

1use core::{mem::forget, ptr};
2
3/// This enum is essentially the same as the standard `Result`,
4/// except it is `#[repr(C)]` and thus safe to use a cross the
5/// ffi boundaries.
6///
7/// Note that the safety is guaranteed only for the enum itself,
8/// not for the data it holds. Meaning you still have to manually
9/// use `#[repr(C)]` on `TOk` and `TErr` for the inner data to be
10/// ffi safe.
11///
12/// It additionally provides `const` variants of `Result` methods.
13#[repr(u8)]
14#[derive(Debug)]
15#[must_use]
16pub enum CResult<TOk, TErr> {
17    Ok(TOk) = 0,
18    Err(TErr) = 1,
19}
20
21impl<TOk, TErr> CResult<TOk, TErr> {
22    /// Returns `true` if the enum holds [`CResult::Ok`],
23    /// `false` otherwise.
24    #[inline(always)]
25    pub const fn is_ok(&self) -> bool {
26        match self {
27            CResult::Ok(_) => true,
28            CResult::Err(_) => false,
29        }
30    }
31
32    /// Returns `true` if the enum holds [`CResult::Err`],
33    /// `false` otherwise.
34    #[inline(always)]
35    pub const fn is_err(&self) -> bool {
36        !self.is_ok()
37    }
38
39    /// Unwraps currently stored [`CResult::Ok`] value.
40    ///
41    /// # Panics
42    ///
43    /// Only when `self` actually holds a [`CResult::Err`] value.
44    #[inline(always)]
45    pub const fn unwrap(self) -> TOk {
46        match &self {
47            CResult::Ok(ok) => {
48                let data = unsafe { ptr::read(ok) };
49                forget(self);
50                data
51            }
52            CResult::Err(_) => {
53                panic!("called `CResult::unwrap()` on an `Err` value.");
54            }
55        }
56    }
57
58    /// Unwraps currently stored [`CResult::Ok`] value.
59    ///
60    /// # Safety
61    ///
62    /// This function does not verify whether the stored value
63    /// is actually [`CResult::Ok`]. The behaviour is undefined if it is not.
64    #[inline(always)]
65    pub const unsafe fn unwrap_unchecked(self) -> TOk {
66        match &self {
67            CResult::Ok(ok) => {
68                let data = unsafe { ptr::read(ok) };
69                forget(self);
70                data
71            }
72            CResult::Err(_) => {
73                unsafe { core::hint::unreachable_unchecked() };
74            }
75        }
76    }
77
78    /// Unwraps currently stored [`CResult::Err`] value.
79    ///
80    /// # Panics
81    ///
82    /// Only when `self` actually holds an [`CResult::Ok`] value.
83    #[inline(always)]
84    pub const fn unwrap_err(self) -> TErr {
85        match &self {
86            CResult::Ok(_) => {
87                panic!("called `CResult::unwrap_err()` on an `Ok` value.");
88            }
89            CResult::Err(err) => {
90                let data = unsafe { ptr::read(err) };
91                forget(self);
92                data
93            }
94        }
95    }
96
97    /// Unwraps currently stored [`CResult::Err`] value.
98    ///
99    /// # Safety
100    ///
101    /// This function does not verify whether the stored value
102    /// is actually [`CResult::Err`]. The behaviour is undefined if it is not.
103    #[inline(always)]
104    pub const unsafe fn unwrap_err_unchecked(self) -> TErr {
105        match &self {
106            CResult::Ok(_) => {
107                unsafe { core::hint::unreachable_unchecked() };
108            }
109            CResult::Err(err) => {
110                let data = unsafe { ptr::read(err) };
111                forget(self);
112                data
113            }
114        }
115    }
116
117    #[allow(clippy::missing_errors_doc)]
118    /// Converts [`CResult`] into standard `Result`.
119    #[inline(always)]
120    pub const fn into_result(self) -> Result<TOk, TErr> {
121        match &self {
122            CResult::Ok(ok) => {
123                let data = unsafe { ptr::read(ok) };
124                forget(self);
125                Ok(data)
126            }
127            CResult::Err(err) => {
128                let data = unsafe { ptr::read(err) };
129                forget(self);
130                Err(data)
131            }
132        }
133    }
134
135    /// Converts standard `Result` into [`CResult`].
136    #[inline(always)]
137    pub const fn from_result(result: Result<TOk, TErr>) -> Self {
138        match &result {
139            Ok(ok) => {
140                let data = unsafe { ptr::read(ok) };
141                forget(result);
142                Self::Ok(data)
143            }
144            Err(err) => {
145                let data = unsafe { ptr::read(err) };
146                forget(result);
147                Self::Err(data)
148            }
149        }
150    }
151}
152
153impl<TOk, TErr> From<CResult<TOk, TErr>> for Result<TOk, TErr> {
154    fn from(value: CResult<TOk, TErr>) -> Self {
155        value.into_result()
156    }
157}
158
159impl<TOk, TErr> From<Result<TOk, TErr>> for CResult<TOk, TErr> {
160    fn from(value: Result<TOk, TErr>) -> Self {
161        Self::from_result(value)
162    }
163}