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    parse_bytes: None,
168    try_from: None,
169    try_into_inner: None,
170    try_borrow_inner: None,
171    partial_eq: Some(result_partial_eq),
172    partial_cmp: Some(result_partial_cmp),
173    cmp: Some(result_cmp),
174};
175
176// Type operations for all Result<T, E>
177static RESULT_TYPE_OPS: TypeOpsIndirect = TypeOpsIndirect {
178    drop_in_place: result_drop,
179    default_in_place: None,
180    clone_into: None,
181    is_truthy: None,
182};
183
184/// Check if Result<T, E> is Ok
185unsafe fn result_is_ok<T, E>(result: PtrConst) -> bool {
186    unsafe { result.get::<Result<T, E>>().is_ok() }
187}
188
189/// Get the Ok value from Result<T, E> if present
190unsafe fn result_get_ok<T, E>(result: PtrConst) -> Option<PtrConst> {
191    unsafe {
192        result
193            .get::<Result<T, E>>()
194            .as_ref()
195            .ok()
196            .map(|t| PtrConst::new(t as *const T))
197    }
198}
199
200/// Get the Err value from Result<T, E> if present
201unsafe fn result_get_err<T, E>(result: PtrConst) -> Option<PtrConst> {
202    unsafe {
203        result
204            .get::<Result<T, E>>()
205            .as_ref()
206            .err()
207            .map(|e| PtrConst::new(e as *const E))
208    }
209}
210
211/// Initialize Result<T, E> with Ok(value)
212unsafe fn result_init_ok<T, E>(result: crate::PtrUninit, value: PtrConst) -> PtrMut {
213    unsafe { result.put(Result::<T, E>::Ok(value.read::<T>())) }
214}
215
216/// Initialize Result<T, E> with Err(value)
217unsafe fn result_init_err<T, E>(result: crate::PtrUninit, value: PtrConst) -> PtrMut {
218    unsafe { result.put(Result::<T, E>::Err(value.read::<E>())) }
219}
220
221unsafe impl<'a, T: Facet<'a>, E: Facet<'a>> Facet<'a> for Result<T, E> {
222    const SHAPE: &'static Shape = &const {
223        const fn build_result_vtable<T, E>() -> ResultVTable {
224            ResultVTable::builder()
225                .is_ok(result_is_ok::<T, E>)
226                .get_ok(result_get_ok::<T, E>)
227                .get_err(result_get_err::<T, E>)
228                .init_ok(result_init_ok::<T, E>)
229                .init_err(result_init_err::<T, E>)
230                .build()
231        }
232
233        ShapeBuilder::for_sized::<Result<T, E>>("Result")
234            .ty(Type::User(UserType::Opaque))
235            .def(Def::Result(ResultDef::new(
236                &const { build_result_vtable::<T, E>() },
237                T::SHAPE,
238                E::SHAPE,
239            )))
240            .type_params(&[
241                TypeParam {
242                    name: "T",
243                    shape: T::SHAPE,
244                },
245                TypeParam {
246                    name: "E",
247                    shape: E::SHAPE,
248                },
249            ])
250            .vtable_indirect(&RESULT_VTABLE)
251            .type_ops_indirect(&RESULT_TYPE_OPS)
252            .build()
253    };
254}