facet_core/impls_core/
option.rs

1use core::mem::MaybeUninit;
2
3use crate::{
4    Def, EnumRepr, EnumType, Facet, Field, FieldFlags, OptionDef, OptionVTable, PtrConst, PtrMut,
5    PtrUninit, Repr, Shape, StructKind, StructType, TryBorrowInnerError, TryFromError,
6    TryIntoInnerError, Type, TypedPtrUninit, UserType, VTableView, ValueVTable, Variant,
7    value_vtable,
8};
9unsafe impl<'a, T: Facet<'a>> Facet<'a> for Option<T> {
10    const VTABLE: &'static ValueVTable = &const {
11        // Define the functions for transparent conversion between Option<T> and T
12        unsafe fn try_from<'a, 'src, 'dst, T: Facet<'a>>(
13            src_ptr: PtrConst<'src>,
14            src_shape: &'static Shape,
15            dst: PtrUninit<'dst>,
16        ) -> Result<PtrMut<'dst>, TryFromError> {
17            if src_shape.id != T::SHAPE.id {
18                return Err(TryFromError::UnsupportedSourceShape {
19                    src_shape,
20                    expected: &[T::SHAPE],
21                });
22            }
23            let t = unsafe { src_ptr.read::<T>() };
24            let option = Some(t);
25            Ok(unsafe { dst.put(option) })
26        }
27
28        unsafe fn try_into_inner<'a, 'src, 'dst, T: Facet<'a>>(
29            src_ptr: PtrMut<'src>,
30            dst: PtrUninit<'dst>,
31        ) -> Result<PtrMut<'dst>, TryIntoInnerError> {
32            let option = unsafe { src_ptr.read::<Option<T>>() };
33            match option {
34                Some(t) => Ok(unsafe { dst.put(t) }),
35                None => Err(TryIntoInnerError::Unavailable),
36            }
37        }
38
39        unsafe fn try_borrow_inner<'a, 'src, T: Facet<'a>>(
40            src_ptr: PtrConst<'src>,
41        ) -> Result<PtrConst<'src>, TryBorrowInnerError> {
42            let option = unsafe { src_ptr.get::<Option<T>>() };
43            match option {
44                Some(t) => Ok(PtrConst::new(t)),
45                None => Err(TryBorrowInnerError::Unavailable),
46            }
47        }
48
49        let mut vtable = value_vtable!(core::option::Option<T>, |f, opts| {
50            write!(f, "{}", Self::SHAPE.type_identifier)?;
51            if let Some(opts) = opts.for_children() {
52                write!(f, "<")?;
53                (T::SHAPE.vtable.type_name())(f, opts)?;
54                write!(f, ">")?;
55            } else {
56                write!(f, "<…>")?;
57            }
58            Ok(())
59        });
60
61        {
62            let vtable_sized = vtable.sized_mut().unwrap();
63            vtable_sized.debug = || {
64                if T::SHAPE.is_debug() {
65                    Some(|this, f| {
66                        let this = unsafe { this.get::<Self>() };
67                        if let Some(value) = &this {
68                            write!(f, "Some(")?;
69                            (<VTableView<T>>::of().debug().unwrap())(value, f)?;
70                            write!(f, ")")?;
71                        } else {
72                            write!(f, "None")?;
73                        }
74                        Ok(())
75                    })
76                } else {
77                    None
78                }
79            };
80
81            vtable_sized.parse = || {
82                if T::SHAPE.is_from_str() {
83                    Some(|str, target| {
84                        let mut t = MaybeUninit::<T>::uninit();
85                        let parse = <VTableView<T>>::of().parse().unwrap();
86                        let _res = (parse)(str, TypedPtrUninit::new(t.as_mut_ptr()))?;
87                        // res points to t so we can't drop it yet. the option is not initialized though
88                        unsafe {
89                            target.put(Some(t.assume_init()));
90                            Ok(target.assume_init())
91                        }
92                    })
93                } else {
94                    None
95                }
96            };
97
98            vtable_sized.try_from = || Some(try_from::<T>);
99            vtable_sized.try_into_inner = || Some(try_into_inner::<T>);
100            vtable_sized.try_borrow_inner = || Some(try_borrow_inner::<T>);
101        }
102
103        vtable
104    };
105
106    const SHAPE: &'static Shape = &const {
107        // Function to return inner type's shape
108        fn inner_shape<'a, T: Facet<'a>>() -> &'static Shape {
109            T::SHAPE
110        }
111
112        Shape::builder_for_sized::<Self>()
113            .type_identifier("Option")
114            .type_params(&[crate::TypeParam {
115                name: "T",
116                shape: || T::SHAPE,
117            }])
118            .ty(Type::User(
119                // Null-Pointer-Optimization - we verify that this Option variant has no
120                // discriminant.
121                //
122                // See: https://doc.rust-lang.org/std/option/index.html#representation
123                if core::mem::size_of::<T>() == core::mem::size_of::<Option<T>>()
124                    && core::mem::size_of::<T>() <= core::mem::size_of::<usize>()
125                {
126                    UserType::Enum(EnumType {
127                        repr: Repr::default(),
128                        enum_repr: EnumRepr::RustNPO,
129                        variants: &const {
130                            [
131                                Variant::builder()
132                                    .name("None")
133                                    .discriminant(0)
134                                    .data(
135                                        StructType::builder()
136                                            .repr(Repr::default())
137                                            .kind(StructKind::Unit)
138                                            .build(),
139                                    )
140                                    .build(),
141                                Variant::builder()
142                                    .name("Some")
143                                    .discriminant(0)
144                                    .data(
145                                        StructType::builder()
146                                            .repr(Repr::default())
147                                            .kind(StructKind::TupleStruct)
148                                            .fields(
149                                                &const {
150                                                    [Field::builder()
151                                                        .name("0")
152                                                        .shape(T::SHAPE)
153                                                        .offset(0)
154                                                        .flags(FieldFlags::EMPTY)
155                                                        .build()]
156                                                },
157                                            )
158                                            .build(),
159                                    )
160                                    .build(),
161                            ]
162                        },
163                    })
164                } else {
165                    UserType::Opaque
166                },
167            ))
168            .def(Def::Option(
169                OptionDef::builder()
170                    .t(T::SHAPE)
171                    .vtable(
172                        const {
173                            &OptionVTable::builder()
174                                .is_some(|option| unsafe { option.get::<Option<T>>().is_some() })
175                                .get_value(|option| unsafe {
176                                    option
177                                        .get::<Option<T>>()
178                                        .as_ref()
179                                        .map(|t| PtrConst::new(t as *const T))
180                                })
181                                .init_some(|option, value| unsafe {
182                                    option.put(Option::Some(value.read::<T>()))
183                                })
184                                .init_none(|option| unsafe { option.put(<Option<T>>::None) })
185                                .replace_with(|option, value| unsafe {
186                                    let option = option.as_mut::<Option<T>>();
187                                    match value {
188                                        Some(value) => option.replace(value.read::<T>()),
189                                        None => option.take(),
190                                    };
191                                })
192                                .build()
193                        },
194                    )
195                    .build(),
196            ))
197            .inner(inner_shape::<T>)
198            .build()
199    };
200}