facet_reflect/poke/
option.rs

1use facet_core::{Facet, Opaque, OpaqueConst, OpaqueUninit, OptionDef, OptionVTable, Shape};
2
3use crate::Guard;
4
5/// Allows initializing an uninitialized option
6pub struct PokeOptionUninit<'mem> {
7    data: OpaqueUninit<'mem>,
8    shape: &'static Shape,
9    def: OptionDef,
10}
11
12impl<'mem> PokeOptionUninit<'mem> {
13    /// Creates a new uninitialized option poke
14    ///
15    /// # Safety
16    ///
17    /// `data` must be properly aligned and sized for this shape.
18    pub(crate) unsafe fn new(
19        data: OpaqueUninit<'mem>,
20        shape: &'static Shape,
21        def: OptionDef,
22    ) -> Self {
23        Self { data, shape, def }
24    }
25
26    /// Returns the shape of this option
27    pub fn shape(&self) -> &'static Shape {
28        self.shape
29    }
30
31    /// Returns the option definition
32    pub fn def(&self) -> OptionDef {
33        self.def
34    }
35
36    /// Returns the option vtable
37    pub fn vtable(&self) -> &'static OptionVTable {
38        self.def.vtable
39    }
40
41    /// Get a reference to the underlying PokeValue
42    #[inline(always)]
43    pub fn into_value(self) -> crate::PokeValueUninit<'mem> {
44        unsafe { crate::PokeValueUninit::new(self.data, self.shape) }
45    }
46
47    /// Initialize the option as None
48    ///
49    /// # Safety
50    ///
51    /// Caller must ensure that all safety requirements for initializing this option are met.
52    pub unsafe fn init_none(self) -> PokeOption<'mem> {
53        unsafe {
54            let inited = (self.vtable().init_none_fn)(self.data);
55            PokeOption::new(inited, self.shape, self.def)
56        }
57    }
58
59    /// Initialize the option as Some, taking ownership of the given value
60    ///
61    /// # Safety
62    ///
63    /// Caller must ensure that all safety requirements for initializing this option are met
64    /// and that the value type matches what the option expects.
65    ///
66    /// Caller must free the memory pointed to by `value` after the option is initialized,
67    /// but must not drop it in place — it's been copied bitwise into the option.
68    pub unsafe fn write<'a>(self, value: facet_core::OpaqueConst<'a>) -> PokeOption<'mem> {
69        unsafe {
70            // Initialize the option as Some
71            let inited = (self.vtable().init_some_fn)(self.data, value);
72            PokeOption::new(inited, self.shape, self.def)
73        }
74    }
75
76    /// Initialize the option by providing a value of type `T`
77    ///
78    /// # Safety
79    ///
80    /// Caller must ensure that `T` matches the expected type of the option
81    /// and that all safety requirements for initializing this option are met.
82    pub unsafe fn put<T>(self, value: T) -> PokeOption<'mem> {
83        let value_opaque = facet_core::OpaqueConst::new(&raw const value);
84        let result = unsafe { self.write(value_opaque) };
85        core::mem::forget(value);
86        result
87    }
88}
89
90/// Allows poking an option (setting Some/None)
91pub struct PokeOption<'mem> {
92    data: Opaque<'mem>,
93    shape: &'static Shape,
94    def: OptionDef,
95}
96
97impl<'mem> PokeOption<'mem> {
98    /// Creates a new option poke
99    ///
100    /// # Safety
101    ///
102    /// `data` must be properly aligned and sized for this shape.
103    pub(crate) unsafe fn new(data: Opaque<'mem>, shape: &'static Shape, def: OptionDef) -> Self {
104        Self { data, shape, def }
105    }
106
107    /// Returns the shape of this option
108    pub fn shape(&self) -> &'static Shape {
109        self.shape
110    }
111
112    /// Returns the option definition
113    pub fn def(&self) -> OptionDef {
114        self.def
115    }
116
117    /// Returns the option vtable
118    pub fn vtable(&self) -> &'static OptionVTable {
119        self.def.vtable
120    }
121
122    /// Replace the current value with None
123    pub fn replace_with_none(self) -> Self {
124        unsafe { (self.vtable().replace_with_fn)(self.data, None) };
125        self
126    }
127
128    /// Replace the current value with Some
129    pub fn replace_with_some<T>(self, value: T) -> Self {
130        let value_opaque = OpaqueConst::new(&raw const value);
131        unsafe { (self.vtable().replace_with_fn)(self.data, Some(value_opaque)) };
132        core::mem::forget(value);
133        self
134    }
135
136    /// Get a reference to the underlying value
137    #[inline(always)]
138    pub fn into_value(self) -> crate::PokeValueUninit<'mem> {
139        unsafe {
140            crate::PokeValueUninit::new(OpaqueUninit::new(self.data.as_mut_byte_ptr()), self.shape)
141        }
142    }
143
144    /// Builds an `Option<T>` from the PokeOption, then deallocates the memory
145    /// that this PokeOption was pointing to.
146    ///
147    /// # Panics
148    ///
149    /// This function will panic if:
150    /// - The generic type parameter T does not match the shape that this PokeOption is building.
151    pub fn build<T: Facet>(self, guard: Option<Guard>) -> Option<T> {
152        let mut guard = guard;
153        let this = self;
154        // this changes drop order: guard must be dropped _after_ this.
155
156        this.shape.assert_type::<Option<T>>();
157        if let Some(guard) = &guard {
158            guard.shape.assert_type::<Option<T>>();
159        }
160
161        let result = unsafe {
162            let ptr = this.data.as_ref::<Option<T>>();
163            core::ptr::read(ptr)
164        };
165        guard.take(); // dealloc
166        result
167    }
168}