Skip to main content

facet_reflect/poke/
tuple.rs

1use facet_core::{Facet, FieldError};
2
3use crate::{ReflectError, ReflectErrorKind, peek::TupleType};
4
5use super::Poke;
6
7/// Lets you mutate a tuple's fields (by index).
8///
9/// Tuples are just tuple-struct types without names, so this is a thin wrapper that
10/// exposes ordered field access.
11pub struct PokeTuple<'mem, 'facet> {
12    pub(crate) value: Poke<'mem, 'facet>,
13    pub(crate) ty: TupleType,
14}
15
16impl<'mem, 'facet> core::fmt::Debug for PokeTuple<'mem, 'facet> {
17    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
18        f.debug_struct("PokeTuple")
19            .field("type", &self.ty)
20            .finish_non_exhaustive()
21    }
22}
23
24impl<'mem, 'facet> PokeTuple<'mem, 'facet> {
25    fn err(&self, kind: ReflectErrorKind) -> ReflectError {
26        self.value.err(kind)
27    }
28
29    /// Returns the number of fields in this tuple.
30    #[inline]
31    pub const fn len(&self) -> usize {
32        self.ty.fields.len()
33    }
34
35    /// Returns true if this tuple has no fields.
36    #[inline]
37    pub const fn is_empty(&self) -> bool {
38        self.len() == 0
39    }
40
41    /// Tuple type information.
42    #[inline]
43    pub const fn ty(&self) -> TupleType {
44        self.ty
45    }
46
47    /// Returns a read-only `Peek` for the field at the given index.
48    pub fn field(&self, index: usize) -> Option<crate::Peek<'_, 'facet>> {
49        let field = self.ty.fields.get(index)?;
50        let field_ptr = unsafe { self.value.data().field(field.offset) };
51        Some(unsafe { crate::Peek::unchecked_new(field_ptr, field.shape()) })
52    }
53
54    /// Returns a mutable `Poke` for the field at the given index.
55    pub fn field_mut(&mut self, index: usize) -> Result<Poke<'_, 'facet>, ReflectError> {
56        let field = self.ty.fields.get(index).ok_or_else(|| {
57            self.err(ReflectErrorKind::FieldError {
58                shape: self.value.shape,
59                field_error: FieldError::IndexOutOfBounds {
60                    index,
61                    bound: self.ty.fields.len(),
62                },
63            })
64        })?;
65        let field_data = unsafe { self.value.data_mut().field(field.offset) };
66        Ok(unsafe { Poke::from_raw_parts(field_data, field.shape()) })
67    }
68
69    /// Sets the value of a field by index.
70    ///
71    /// The value type must match the field's type.
72    ///
73    /// Returns an error if the parent tuple is not POD. Field mutation could violate
74    /// tuple-level invariants, so the tuple must be marked with `#[facet(pod)]` to allow this.
75    pub fn set_field<T: Facet<'facet>>(
76        &mut self,
77        index: usize,
78        value: T,
79    ) -> Result<(), ReflectError> {
80        if !self.value.shape.is_pod() {
81            return Err(self.err(ReflectErrorKind::NotPod {
82                shape: self.value.shape,
83            }));
84        }
85
86        let field = self.ty.fields.get(index).ok_or_else(|| {
87            self.err(ReflectErrorKind::FieldError {
88                shape: self.value.shape,
89                field_error: FieldError::IndexOutOfBounds {
90                    index,
91                    bound: self.ty.fields.len(),
92                },
93            })
94        })?;
95
96        let field_shape = field.shape();
97        if field_shape != T::SHAPE {
98            return Err(self.err(ReflectErrorKind::WrongShape {
99                expected: field_shape,
100                actual: T::SHAPE,
101            }));
102        }
103
104        unsafe {
105            let field_ptr = self.value.data_mut().field(field.offset);
106            field_shape.call_drop_in_place(field_ptr);
107            core::ptr::write(field_ptr.as_mut_byte_ptr() as *mut T, value);
108        }
109        Ok(())
110    }
111
112    /// Converts this back into the underlying `Poke`.
113    #[inline]
114    pub const fn into_inner(self) -> Poke<'mem, 'facet> {
115        self.value
116    }
117
118    /// Returns a read-only `PeekTuple` view.
119    #[inline]
120    pub fn as_peek_tuple(&self) -> crate::PeekTuple<'_, 'facet> {
121        crate::PeekTuple {
122            value: self.value.as_peek(),
123            ty: self.ty,
124        }
125    }
126}