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