facet_reflect/peek/
list_like.rs

1use facet_core::{PtrConst, Shape, ShapeLayout};
2
3use super::Peek;
4use core::fmt::Debug;
5
6/// Fields for types which act like lists
7#[derive(Clone, Copy)]
8pub enum ListLikeDef {
9    /// Ordered list of heterogenous values, variable size
10    ///
11    /// e.g. `Vec<T>`
12    List(facet_core::ListDef),
13
14    /// Fixed-size array of heterogenous values
15    ///
16    /// e.g. `[T; 32]`
17    Array(facet_core::ArrayDef),
18
19    /// Slice — a reference to a contiguous sequence of elements
20    ///
21    /// e.g. `&[T]`
22    Slice(facet_core::SliceDef),
23}
24
25impl ListLikeDef {
26    /// Returns the shape of the items in the list
27    pub fn t(&self) -> &'static Shape {
28        match self {
29            ListLikeDef::List(v) => v.t(),
30            ListLikeDef::Array(v) => v.t(),
31            ListLikeDef::Slice(v) => v.t(),
32        }
33    }
34}
35
36/// Iterator over a `PeekListLike`
37pub struct PeekListLikeIter<'mem, 'facet_lifetime> {
38    list: PeekListLike<'mem, 'facet_lifetime>,
39    index: usize,
40    len: usize,
41}
42
43impl<'mem, 'facet_lifetime> Iterator for PeekListLikeIter<'mem, 'facet_lifetime> {
44    type Item = Peek<'mem, 'facet_lifetime>;
45
46    fn next(&mut self) -> Option<Self::Item> {
47        if self.index >= self.len {
48            return None;
49        }
50        let item = self.list.get(self.index);
51        self.index += 1;
52        item
53    }
54
55    fn size_hint(&self) -> (usize, Option<usize>) {
56        let remaining = self.len.saturating_sub(self.index);
57        (remaining, Some(remaining))
58    }
59}
60
61impl ExactSizeIterator for PeekListLikeIter<'_, '_> {}
62
63impl<'mem, 'facet_lifetime> IntoIterator for &'mem PeekListLike<'mem, 'facet_lifetime> {
64    type Item = Peek<'mem, 'facet_lifetime>;
65    type IntoIter = PeekListLikeIter<'mem, 'facet_lifetime>;
66
67    fn into_iter(self) -> Self::IntoIter {
68        self.iter()
69    }
70}
71
72/// Lets you read from a list, array or slice
73#[derive(Clone, Copy)]
74pub struct PeekListLike<'mem, 'facet_lifetime> {
75    pub(crate) value: Peek<'mem, 'facet_lifetime>,
76    pub(crate) def: ListLikeDef,
77    len: usize,
78    as_ptr: unsafe fn(this: PtrConst) -> PtrConst,
79}
80
81impl Debug for PeekListLike<'_, '_> {
82    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83        f.debug_struct("PeekListLike").finish_non_exhaustive()
84    }
85}
86
87impl<'mem, 'facet_lifetime> PeekListLike<'mem, 'facet_lifetime> {
88    /// Creates a new peek list
89    pub fn new(value: Peek<'mem, 'facet_lifetime>, def: ListLikeDef) -> Self {
90        let (len, as_ptr_fn) = match def {
91            ListLikeDef::List(v) => (unsafe { (v.vtable.len)(value.data()) }, v.vtable.as_ptr),
92            ListLikeDef::Slice(v) => (unsafe { (v.vtable.len)(value.data()) }, v.vtable.as_ptr),
93            ListLikeDef::Array(v) => (v.n, v.vtable.as_ptr),
94        };
95        Self {
96            value,
97            def,
98            len,
99            as_ptr: as_ptr_fn,
100        }
101    }
102
103    /// Get the length of the list
104    pub fn len(&self) -> usize {
105        self.len
106    }
107
108    /// Returns true if the list is empty
109    pub fn is_empty(&self) -> bool {
110        self.len() == 0
111    }
112
113    /// Get an item from the list at the specified index
114    ///
115    /// # Panics
116    ///
117    /// Panics if the index is out of bounds
118    pub fn get(&self, index: usize) -> Option<Peek<'mem, 'facet_lifetime>> {
119        if index >= self.len() {
120            return None;
121        }
122
123        // Get the base pointer of the array
124        let base_ptr = unsafe { (self.as_ptr)(self.value.data()) };
125
126        // Get the layout of the element type
127        let elem_layout = match self.def.t().layout {
128            ShapeLayout::Sized(layout) => layout,
129            ShapeLayout::Unsized => return None, // Cannot handle unsized elements
130        };
131
132        // Calculate the offset based on element size
133        let offset = index * elem_layout.size();
134
135        // Apply the offset to get the item's pointer
136        let item_ptr = unsafe { base_ptr.field(offset) };
137
138        Some(unsafe { Peek::unchecked_new(item_ptr, self.def.t()) })
139    }
140
141    /// Returns an iterator over the list
142    pub fn iter(self) -> PeekListLikeIter<'mem, 'facet_lifetime> {
143        PeekListLikeIter {
144            list: self,
145            index: 0,
146            len: self.len(),
147        }
148    }
149
150    /// Def getter
151    pub fn def(&self) -> ListLikeDef {
152        self.def
153    }
154}