Skip to main content

facet_reflect/poke/
option.rs

1use core::mem::ManuallyDrop;
2
3use facet_core::{Facet, OptionDef, OptionVTable};
4
5use crate::{HeapValue, ReflectError, ReflectErrorKind};
6
7use super::Poke;
8
9/// Lets you mutate an option (implements mutable option operations)
10pub struct PokeOption<'mem, 'facet> {
11    value: Poke<'mem, 'facet>,
12    def: OptionDef,
13}
14
15impl<'mem, 'facet> core::fmt::Debug for PokeOption<'mem, 'facet> {
16    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17        f.debug_struct("PokeOption").finish_non_exhaustive()
18    }
19}
20
21impl<'mem, 'facet> PokeOption<'mem, 'facet> {
22    /// Creates a new poke option
23    ///
24    /// # Safety
25    ///
26    /// The caller must ensure that `def` contains valid vtable function pointers that
27    /// correctly implement the option operations for the actual type, and that the
28    /// inner type matches `def.t()`.
29    #[inline]
30    pub const unsafe fn new(value: Poke<'mem, 'facet>, def: OptionDef) -> Self {
31        Self { value, def }
32    }
33
34    fn err(&self, kind: ReflectErrorKind) -> ReflectError {
35        self.value.err(kind)
36    }
37
38    /// Returns the option definition
39    #[inline(always)]
40    pub const fn def(&self) -> OptionDef {
41        self.def
42    }
43
44    /// Returns the option vtable
45    #[inline(always)]
46    pub const fn vtable(&self) -> &'static OptionVTable {
47        self.def.vtable
48    }
49
50    /// Returns whether the option is Some
51    #[inline]
52    pub fn is_some(&self) -> bool {
53        unsafe { (self.vtable().is_some)(self.value.data()) }
54    }
55
56    /// Returns whether the option is None
57    #[inline]
58    pub fn is_none(&self) -> bool {
59        !self.is_some()
60    }
61
62    /// Returns the inner value as a read-only `Peek` if the option is Some, `None` otherwise.
63    #[inline]
64    pub fn value(&self) -> Option<crate::Peek<'_, 'facet>> {
65        unsafe {
66            let inner_data = (self.vtable().get_value)(self.value.data());
67            if inner_data.is_null() {
68                None
69            } else {
70                Some(crate::Peek::unchecked_new(
71                    facet_core::PtrConst::new_sized(inner_data),
72                    self.def.t(),
73                ))
74            }
75        }
76    }
77
78    /// Returns the inner value as a mutable `Poke` if the option is Some, `None` otherwise.
79    #[inline]
80    pub fn value_mut(&mut self) -> Option<Poke<'_, 'facet>> {
81        unsafe {
82            let inner_data = (self.vtable().get_value)(self.value.data());
83            if inner_data.is_null() {
84                None
85            } else {
86                // The option is Some — compute the offset from the option's base to
87                // the inner value and construct a PtrMut from the option's mutable data.
88                let offset = inner_data.offset_from(self.value.data().as_byte_ptr()) as usize;
89                let inner_data = self.value.data_mut().field(offset);
90                Some(Poke::from_raw_parts(inner_data, self.def.t()))
91            }
92        }
93    }
94
95    /// Sets the option to `Some(value)`, dropping the previous value.
96    pub fn set_some<T: Facet<'facet>>(&mut self, value: T) -> Result<(), ReflectError> {
97        if self.def.t() != T::SHAPE {
98            return Err(self.err(ReflectErrorKind::WrongShape {
99                expected: self.def.t(),
100                actual: T::SHAPE,
101            }));
102        }
103
104        let mut value = ManuallyDrop::new(value);
105        unsafe {
106            (self.vtable().replace_with)(
107                self.value.data_mut(),
108                &mut value as *mut ManuallyDrop<T> as *mut u8,
109            );
110        }
111        Ok(())
112    }
113
114    /// Type-erased [`set_some`](Self::set_some).
115    ///
116    /// Accepts a [`HeapValue`] whose shape must match the option's inner type. The value is
117    /// moved out of the HeapValue into the option; the HeapValue's backing memory is freed
118    /// without running drop (the vtable has already consumed the value via `ptr::read`).
119    ///
120    /// Use this when you hold a reflection-built value and can't produce a concrete `T`.
121    pub fn set_some_from_heap<const BORROW: bool>(
122        &mut self,
123        value: HeapValue<'facet, BORROW>,
124    ) -> Result<(), ReflectError> {
125        if self.def.t() != value.shape() {
126            return Err(self.err(ReflectErrorKind::WrongShape {
127                expected: self.def.t(),
128                actual: value.shape(),
129            }));
130        }
131
132        let mut value = value;
133        let guard = value
134            .guard
135            .take()
136            .expect("HeapValue guard was already taken");
137        unsafe {
138            (self.vtable().replace_with)(self.value.data_mut(), guard.ptr.as_ptr());
139        }
140        drop(guard);
141        Ok(())
142    }
143
144    /// Sets the option to `None`, dropping the previous value.
145    pub fn set_none(&mut self) {
146        unsafe {
147            (self.vtable().replace_with)(self.value.data_mut(), core::ptr::null_mut());
148        }
149    }
150
151    /// Converts this `PokeOption` back into a `Poke`
152    #[inline]
153    pub const fn into_inner(self) -> Poke<'mem, 'facet> {
154        self.value
155    }
156
157    /// Returns a read-only `PeekOption` view
158    #[inline]
159    pub fn as_peek_option(&self) -> crate::PeekOption<'_, 'facet> {
160        crate::PeekOption {
161            value: self.value.as_peek(),
162            def: self.def,
163        }
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[test]
172    fn poke_option_is_some_is_none() {
173        let mut x: Option<i32> = Some(42);
174        let poke = Poke::new(&mut x);
175        let opt = poke.into_option().unwrap();
176        assert!(opt.is_some());
177        assert!(!opt.is_none());
178
179        let mut y: Option<i32> = None;
180        let poke = Poke::new(&mut y);
181        let opt = poke.into_option().unwrap();
182        assert!(opt.is_none());
183    }
184
185    #[test]
186    fn poke_option_set_some_then_none() {
187        let mut x: Option<i32> = None;
188        let poke = Poke::new(&mut x);
189        let mut opt = poke.into_option().unwrap();
190
191        opt.set_some(42i32).unwrap();
192        assert_eq!(x, Some(42));
193
194        let poke = Poke::new(&mut x);
195        let mut opt = poke.into_option().unwrap();
196        opt.set_none();
197        assert_eq!(x, None);
198    }
199
200    #[test]
201    fn poke_option_value_mut() {
202        let mut x: Option<i32> = Some(1);
203        let poke = Poke::new(&mut x);
204        let mut opt = poke.into_option().unwrap();
205
206        {
207            let mut inner = opt.value_mut().unwrap();
208            inner.set(100i32).unwrap();
209        }
210        assert_eq!(x, Some(100));
211    }
212
213    #[test]
214    fn poke_option_set_some_from_heap() {
215        let mut x: Option<i32> = None;
216        let poke = Poke::new(&mut x);
217        let mut opt = poke.into_option().unwrap();
218
219        let hv = crate::Partial::alloc::<i32>()
220            .unwrap()
221            .set(7i32)
222            .unwrap()
223            .build()
224            .unwrap();
225        opt.set_some_from_heap(hv).unwrap();
226        assert_eq!(x, Some(7));
227    }
228
229    #[test]
230    fn poke_option_set_some_from_heap_wrong_shape_fails() {
231        let mut x: Option<i32> = None;
232        let poke = Poke::new(&mut x);
233        let mut opt = poke.into_option().unwrap();
234
235        let hv = crate::Partial::alloc::<u32>()
236            .unwrap()
237            .set(7u32)
238            .unwrap()
239            .build()
240            .unwrap();
241        let res = opt.set_some_from_heap(hv);
242        assert!(matches!(
243            res,
244            Err(ref err) if matches!(err.kind, ReflectErrorKind::WrongShape { .. })
245        ));
246    }
247}