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