facet_core/impls/core/
result.rs

1//! Facet implementation for Result<T, E>
2
3use core::cmp::Ordering;
4
5use crate::{
6    Def, Facet, HashProxy, OxPtrConst, OxPtrMut, OxRef, PtrConst, PtrMut, ResultDef, ResultVTable,
7    Shape, ShapeBuilder, Type, TypeOpsIndirect, TypeParam, UserType, VTableIndirect,
8};
9
10/// Extract the ResultDef from a shape, returns None if not a Result
11#[inline]
12fn get_result_def(shape: &'static Shape) -> Option<&'static ResultDef> {
13    match shape.def {
14        Def::Result(ref def) => Some(def),
15        _ => None,
16    }
17}
18
19/// Debug for Result<T, E> - delegates to inner T/E's debug if available
20unsafe fn result_debug(
21    ox: OxPtrConst,
22    f: &mut core::fmt::Formatter<'_>,
23) -> Option<core::fmt::Result> {
24    let shape = ox.shape();
25    let def = get_result_def(shape)?;
26    let ptr = ox.ptr();
27
28    if unsafe { (def.vtable.is_ok)(ptr) } {
29        // SAFETY: is_ok returned true, so get_ok returns a valid pointer.
30        // The caller guarantees the OxPtrConst points to a valid Result.
31        let ok_ptr = unsafe { (def.vtable.get_ok)(ptr)? };
32        let ok_ox = unsafe { OxRef::new(ok_ptr, def.t) };
33        Some(f.debug_tuple("Ok").field(&ok_ox).finish())
34    } else {
35        // SAFETY: is_ok returned false, so get_err returns a valid pointer.
36        let err_ptr = unsafe { (def.vtable.get_err)(ptr)? };
37        let err_ox = unsafe { OxRef::new(err_ptr, def.e) };
38        Some(f.debug_tuple("Err").field(&err_ox).finish())
39    }
40}
41
42/// Hash for Result<T, E> - delegates to inner T/E's hash if available
43unsafe fn result_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
44    let shape = ox.shape();
45    let def = get_result_def(shape)?;
46    let ptr = ox.ptr();
47
48    use core::hash::Hash;
49    if unsafe { (def.vtable.is_ok)(ptr) } {
50        0u8.hash(hasher);
51        let ok_ptr = unsafe { (def.vtable.get_ok)(ptr)? };
52        unsafe { def.t.call_hash(ok_ptr, hasher)? };
53    } else {
54        1u8.hash(hasher);
55        let err_ptr = unsafe { (def.vtable.get_err)(ptr)? };
56        unsafe { def.e.call_hash(err_ptr, hasher)? };
57    }
58    Some(())
59}
60
61/// PartialEq for Result<T, E>
62unsafe fn result_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
63    let shape = a.shape();
64    let def = get_result_def(shape)?;
65
66    let a_ptr = a.ptr();
67    let b_ptr = b.ptr();
68    let a_is_ok = unsafe { (def.vtable.is_ok)(a_ptr) };
69    let b_is_ok = unsafe { (def.vtable.is_ok)(b_ptr) };
70
71    Some(match (a_is_ok, b_is_ok) {
72        (true, true) => {
73            let a_ok = unsafe { (def.vtable.get_ok)(a_ptr)? };
74            let b_ok = unsafe { (def.vtable.get_ok)(b_ptr)? };
75            unsafe { def.t.call_partial_eq(a_ok, b_ok)? }
76        }
77        (false, false) => {
78            let a_err = unsafe { (def.vtable.get_err)(a_ptr)? };
79            let b_err = unsafe { (def.vtable.get_err)(b_ptr)? };
80            unsafe { def.e.call_partial_eq(a_err, b_err)? }
81        }
82        _ => false,
83    })
84}
85
86/// PartialOrd for Result<T, E>
87unsafe fn result_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
88    let shape = a.shape();
89    let def = get_result_def(shape)?;
90
91    let a_ptr = a.ptr();
92    let b_ptr = b.ptr();
93    let a_is_ok = unsafe { (def.vtable.is_ok)(a_ptr) };
94    let b_is_ok = unsafe { (def.vtable.is_ok)(b_ptr) };
95
96    Some(match (a_is_ok, b_is_ok) {
97        (true, true) => {
98            let a_ok = unsafe { (def.vtable.get_ok)(a_ptr)? };
99            let b_ok = unsafe { (def.vtable.get_ok)(b_ptr)? };
100            unsafe { def.t.call_partial_cmp(a_ok, b_ok)? }
101        }
102        (false, false) => {
103            let a_err = unsafe { (def.vtable.get_err)(a_ptr)? };
104            let b_err = unsafe { (def.vtable.get_err)(b_ptr)? };
105            unsafe { def.e.call_partial_cmp(a_err, b_err)? }
106        }
107        // Ok is greater than Err (following std::cmp::Ord for Result)
108        (true, false) => Some(Ordering::Greater),
109        (false, true) => Some(Ordering::Less),
110    })
111}
112
113/// Ord for Result<T, E>
114unsafe fn result_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
115    let shape = a.shape();
116    let def = get_result_def(shape)?;
117
118    let a_ptr = a.ptr();
119    let b_ptr = b.ptr();
120    let a_is_ok = unsafe { (def.vtable.is_ok)(a_ptr) };
121    let b_is_ok = unsafe { (def.vtable.is_ok)(b_ptr) };
122
123    Some(match (a_is_ok, b_is_ok) {
124        (true, true) => {
125            let a_ok = unsafe { (def.vtable.get_ok)(a_ptr)? };
126            let b_ok = unsafe { (def.vtable.get_ok)(b_ptr)? };
127            unsafe { def.t.call_cmp(a_ok, b_ok)? }
128        }
129        (false, false) => {
130            let a_err = unsafe { (def.vtable.get_err)(a_ptr)? };
131            let b_err = unsafe { (def.vtable.get_err)(b_ptr)? };
132            unsafe { def.e.call_cmp(a_err, b_err)? }
133        }
134        // Ok is greater than Err (following std::cmp::Ord for Result)
135        (true, false) => Ordering::Greater,
136        (false, true) => Ordering::Less,
137    })
138}
139
140/// Drop for Result<T, E>
141unsafe fn result_drop(ox: OxPtrMut) {
142    let shape = ox.shape();
143    let Some(def) = get_result_def(shape) else {
144        return;
145    };
146    let ptr = ox.ptr();
147
148    if unsafe { (def.vtable.is_ok)(ptr.as_const()) } {
149        let Some(ok_ptr) = (unsafe { (def.vtable.get_ok)(ptr.as_const()) }) else {
150            return;
151        };
152        let ok_ptr_mut = PtrMut::new(ok_ptr.as_byte_ptr() as *mut u8);
153        unsafe { def.t.call_drop_in_place(ok_ptr_mut) };
154    } else {
155        let Some(err_ptr) = (unsafe { (def.vtable.get_err)(ptr.as_const()) }) else {
156            return;
157        };
158        let err_ptr_mut = PtrMut::new(err_ptr.as_byte_ptr() as *mut u8);
159        unsafe { def.e.call_drop_in_place(err_ptr_mut) };
160    }
161}
162
163// Shared vtable for all Result<T, E>
164const RESULT_VTABLE: VTableIndirect = VTableIndirect {
165    display: None,
166    debug: Some(result_debug),
167    hash: Some(result_hash),
168    invariants: None,
169    parse: None,
170    parse_bytes: None,
171    try_from: None,
172    try_into_inner: None,
173    try_borrow_inner: None,
174    partial_eq: Some(result_partial_eq),
175    partial_cmp: Some(result_partial_cmp),
176    cmp: Some(result_cmp),
177};
178
179// Type operations for all Result<T, E>
180static RESULT_TYPE_OPS: TypeOpsIndirect = TypeOpsIndirect {
181    drop_in_place: result_drop,
182    default_in_place: None,
183    clone_into: None,
184    is_truthy: None,
185};
186
187/// Check if Result<T, E> is Ok
188unsafe fn result_is_ok<T, E>(result: PtrConst) -> bool {
189    unsafe { result.get::<Result<T, E>>().is_ok() }
190}
191
192/// Get the Ok value from Result<T, E> if present
193unsafe fn result_get_ok<T, E>(result: PtrConst) -> Option<PtrConst> {
194    unsafe {
195        result
196            .get::<Result<T, E>>()
197            .as_ref()
198            .ok()
199            .map(|t| PtrConst::new(t as *const T))
200    }
201}
202
203/// Get the Err value from Result<T, E> if present
204unsafe fn result_get_err<T, E>(result: PtrConst) -> Option<PtrConst> {
205    unsafe {
206        result
207            .get::<Result<T, E>>()
208            .as_ref()
209            .err()
210            .map(|e| PtrConst::new(e as *const E))
211    }
212}
213
214/// Initialize Result<T, E> with Ok(value)
215unsafe fn result_init_ok<T, E>(result: crate::PtrUninit, value: PtrConst) -> PtrMut {
216    unsafe { result.put(Result::<T, E>::Ok(value.read::<T>())) }
217}
218
219/// Initialize Result<T, E> with Err(value)
220unsafe fn result_init_err<T, E>(result: crate::PtrUninit, value: PtrConst) -> PtrMut {
221    unsafe { result.put(Result::<T, E>::Err(value.read::<E>())) }
222}
223
224unsafe impl<'a, T: Facet<'a>, E: Facet<'a>> Facet<'a> for Result<T, E> {
225    const SHAPE: &'static Shape = &const {
226        const fn build_result_vtable<T, E>() -> ResultVTable {
227            ResultVTable::builder()
228                .is_ok(result_is_ok::<T, E>)
229                .get_ok(result_get_ok::<T, E>)
230                .get_err(result_get_err::<T, E>)
231                .init_ok(result_init_ok::<T, E>)
232                .init_err(result_init_err::<T, E>)
233                .build()
234        }
235
236        ShapeBuilder::for_sized::<Result<T, E>>("Result")
237            .ty(Type::User(UserType::Opaque))
238            .def(Def::Result(ResultDef::new(
239                &const { build_result_vtable::<T, E>() },
240                T::SHAPE,
241                E::SHAPE,
242            )))
243            .type_params(&[
244                TypeParam {
245                    name: "T",
246                    shape: T::SHAPE,
247                },
248                TypeParam {
249                    name: "E",
250                    shape: E::SHAPE,
251                },
252            ])
253            .vtable_indirect(&RESULT_VTABLE)
254            .type_ops_indirect(&RESULT_TYPE_OPS)
255            .build()
256    };
257}