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