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, 'shape, 'src, 'dst, T: Facet<'a>>(
13            src_ptr: PtrConst<'src>,
14            src_shape: &'shape Shape<'shape>,
15            dst: PtrUninit<'dst>,
16        ) -> Result<PtrMut<'dst>, TryFromError<'shape>> {
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, "Option")?;
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        if T::SHAPE.is_debug() {
62            vtable.debug = Some(|this, f| {
63                let this = unsafe { this.get::<Self>() };
64                if let Some(value) = &this {
65                    write!(f, "Some(")?;
66                    (<VTableView<T>>::of().debug().unwrap())(value, f)?;
67                    write!(f, ")")?;
68                } else {
69                    write!(f, "None")?;
70                }
71                Ok(())
72            });
73        }
74
75        if T::SHAPE.is_from_str() {
76            vtable.parse = Some(|str, target| {
77                let mut t = MaybeUninit::<T>::uninit();
78                let parse = <VTableView<T>>::of().parse().unwrap();
79                let _res = (parse)(str, TypedPtrUninit::new(t.as_mut_ptr()))?;
80                // res points to t so we can't drop it yet. the option is not initialized though
81                unsafe {
82                    target.put(Some(t.assume_init()));
83                    Ok(target.assume_init())
84                }
85            });
86        }
87
88        vtable.try_from = Some(try_from::<T>);
89        vtable.try_into_inner = Some(try_into_inner::<T>);
90        vtable.try_borrow_inner = Some(try_borrow_inner::<T>);
91
92        vtable
93    };
94
95    const SHAPE: &'static Shape<'static> = &const {
96        // Function to return inner type's shape
97        fn inner_shape<'a, T: Facet<'a>>() -> &'static Shape<'static> {
98            T::SHAPE
99        }
100
101        Shape::builder_for_sized::<Self>()
102            .type_params(&[crate::TypeParam {
103                name: "T",
104                shape: || T::SHAPE,
105            }])
106            .ty(Type::User(
107                // Null-Pointer-Optimization - we verify that this Option variant has no
108                // discriminant.
109                //
110                // See: https://doc.rust-lang.org/std/option/index.html#representation
111                if core::mem::size_of::<T>() == core::mem::size_of::<Option<T>>()
112                    && core::mem::size_of::<T>() <= core::mem::size_of::<usize>()
113                {
114                    UserType::Enum(EnumType {
115                        repr: Repr::default(),
116                        enum_repr: EnumRepr::RustNPO,
117                        variants: &const {
118                            [
119                                Variant::builder()
120                                    .name("None")
121                                    .discriminant(0)
122                                    .data(
123                                        StructType::builder()
124                                            .repr(Repr::default())
125                                            .kind(StructKind::Unit)
126                                            .build(),
127                                    )
128                                    .build(),
129                                Variant::builder()
130                                    .name("Some")
131                                    .discriminant(0)
132                                    .data(
133                                        StructType::builder()
134                                            .repr(Repr::default())
135                                            .kind(StructKind::TupleStruct)
136                                            .fields(
137                                                &const {
138                                                    [Field::builder()
139                                                        .name("0")
140                                                        .shape(T::SHAPE)
141                                                        .offset(0)
142                                                        .flags(FieldFlags::EMPTY)
143                                                        .build()]
144                                                },
145                                            )
146                                            .build(),
147                                    )
148                                    .build(),
149                            ]
150                        },
151                    })
152                } else {
153                    UserType::Opaque
154                },
155            ))
156            .def(Def::Option(
157                OptionDef::builder()
158                    .t(T::SHAPE)
159                    .vtable(
160                        const {
161                            &OptionVTable::builder()
162                                .is_some(|option| unsafe { option.get::<Option<T>>().is_some() })
163                                .get_value(|option| unsafe {
164                                    option
165                                        .get::<Option<T>>()
166                                        .as_ref()
167                                        .map(|t| PtrConst::new(t as *const T))
168                                })
169                                .init_some(|option, value| unsafe {
170                                    option.put(Option::Some(value.read::<T>()))
171                                })
172                                .init_none(|option| unsafe { option.put(<Option<T>>::None) })
173                                .replace_with(|option, value| unsafe {
174                                    let option = option.as_mut::<Option<T>>();
175                                    match value {
176                                        Some(value) => option.replace(value.read::<T>()),
177                                        None => option.take(),
178                                    };
179                                })
180                                .build()
181                        },
182                    )
183                    .build(),
184            ))
185            .inner(inner_shape::<T>)
186            .build()
187    };
188}