facet_core/impls_core/
option.rs

1use core::{alloc::Layout, mem::MaybeUninit};
2
3use crate::{
4    ConstTypeId, Def, Facet, OptionDef, OptionVTable, PtrConst, PtrMut, PtrUninit, Shape,
5    TryBorrowInnerError, TryFromInnerError, TryIntoInnerError, value_vtable_inner,
6};
7
8unsafe impl<'a, T: Facet<'a>> Facet<'a> for Option<T> {
9    const SHAPE: &'static Shape = &const {
10        // Define the functions for transparent conversion between Option<T> and T
11        unsafe fn try_from_inner<'a, 'src, 'dst, T: Facet<'a>>(
12            src_ptr: PtrConst<'src>,
13            src_shape: &'static Shape,
14            dst: PtrUninit<'dst>,
15        ) -> Result<PtrMut<'dst>, TryFromInnerError> {
16            if src_shape.id != T::SHAPE.id {
17                return Err(TryFromInnerError::UnsupportedSourceShape {
18                    src_shape,
19                    expected: &[T::SHAPE],
20                });
21            }
22            let t = unsafe { src_ptr.read::<T>() };
23            let option = Some(t);
24            Ok(unsafe { dst.put(option) })
25        }
26
27        unsafe fn try_into_inner<'a, 'src, 'dst, T: Facet<'a>>(
28            src_ptr: PtrConst<'src>,
29            dst: PtrUninit<'dst>,
30        ) -> Result<PtrMut<'dst>, TryIntoInnerError> {
31            let option = unsafe { src_ptr.read::<Option<T>>() };
32            match option {
33                Some(t) => Ok(unsafe { dst.put(t) }),
34                None => Err(TryIntoInnerError::Unavailable),
35            }
36        }
37
38        unsafe fn try_borrow_inner<'a, 'src, T: Facet<'a>>(
39            src_ptr: PtrConst<'src>,
40        ) -> Result<PtrConst<'src>, TryBorrowInnerError> {
41            let option = unsafe { src_ptr.get::<Option<T>>() };
42            match option {
43                Some(t) => Ok(PtrConst::new(t)),
44                None => Err(TryBorrowInnerError::Unavailable),
45            }
46        }
47
48        // Function to return inner type's shape
49        fn inner_shape<'a, T: Facet<'a>>() -> &'static Shape {
50            T::SHAPE
51        }
52
53        Shape::builder()
54            .id(ConstTypeId::of::<Self>())
55            .layout(Layout::new::<Self>())
56            .type_params(&[crate::TypeParam {
57                name: "T",
58                shape: || T::SHAPE,
59            }])
60            .def(Def::Option(
61                OptionDef::builder()
62                    .t(|| T::SHAPE)
63                    .vtable(
64                        const {
65                            &OptionVTable::builder()
66                                .is_some(|option| unsafe { option.get::<Option<T>>().is_some() })
67                                .get_value(|option| unsafe {
68                                    option
69                                        .get::<Option<T>>()
70                                        .as_ref()
71                                        .map(|t| PtrConst::new(t as *const T))
72                                })
73                                .init_some(|option, value| unsafe {
74                                    option.put(Option::Some(value.read::<T>()))
75                                })
76                                .init_none(|option| unsafe { option.put(<Option<T>>::None) })
77                                .replace_with(|option, value| unsafe {
78                                    let option = option.as_mut::<Option<T>>();
79                                    match value {
80                                        Some(value) => option.replace(value.read::<T>()),
81                                        None => option.take(),
82                                    };
83                                })
84                                .build()
85                        },
86                    )
87                    .build(),
88            ))
89            .vtable(
90                &const {
91                    let mut vtable = value_vtable_inner!(core::option::Option<T>, |f, opts| {
92                        write!(f, "Option")?;
93                        if let Some(opts) = opts.for_children() {
94                            write!(f, "<")?;
95                            (T::SHAPE.vtable.type_name)(f, opts)?;
96                            write!(f, ">")?;
97                        } else {
98                            write!(f, "<…>")?;
99                        }
100                        Ok(())
101                    });
102
103                    if T::SHAPE.is_debug() {
104                        vtable.debug = Some(|this, f| {
105                            let this = unsafe { this.get::<Self>() };
106                            if let Some(value) = &this {
107                                write!(f, "Some(")?;
108                                unsafe {
109                                    (T::SHAPE.vtable.debug.unwrap_unchecked())(
110                                        PtrConst::new(value),
111                                        f,
112                                    )?;
113                                }
114                                write!(f, ")")?;
115                            } else {
116                                write!(f, "None")?;
117                            }
118                            Ok(())
119                        });
120                    }
121
122                    if T::SHAPE.is_from_str() {
123                        vtable.parse = Some(|str, target| {
124                            let mut t = MaybeUninit::<T>::uninit();
125                            let parse = unsafe { T::SHAPE.vtable.parse.unwrap_unchecked() };
126                            let _res = unsafe { (parse)(str, PtrUninit::new(t.as_mut_ptr()))? };
127                            // res points to t so we can't drop it yet. the option is not initialized though
128                            unsafe {
129                                target.put(Some(t.assume_init()));
130                                Ok(target.assume_init())
131                            }
132                        });
133                    }
134
135                    vtable.try_from_inner = Some(try_from_inner::<T>);
136                    vtable.try_into_inner = Some(try_into_inner::<T>);
137                    vtable.try_borrow_inner = Some(try_borrow_inner::<T>);
138
139                    vtable
140                },
141            )
142            .inner(inner_shape::<T>)
143            .build()
144    };
145}