1use facet_core::{EnumRepr, EnumType, Field, Shape, UserType, Variant};
2
3use crate::{Peek, trace};
4
5use super::HasFields;
6
7#[derive(Clone, Copy)]
9pub struct PeekEnum<'mem, 'facet_lifetime> {
10 pub(crate) value: Peek<'mem, 'facet_lifetime>,
15
16 pub(crate) ty: EnumType,
18}
19
20impl core::fmt::Debug for PeekEnum<'_, '_> {
21 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22 if let Some(debug_fn) = self.vtable().debug {
23 unsafe { debug_fn(self.data, f) }
24 } else {
25 write!(f, "⟨{}⟩", self.shape)
26 }
27 }
28}
29
30pub fn peek_enum(shape: &'static Shape) -> Option<EnumType> {
32 match shape.ty {
33 facet_core::Type::User(UserType::Enum(enum_ty)) => Some(enum_ty),
34 _ => None,
35 }
36}
37
38pub fn peek_enum_repr(shape: &'static Shape) -> Option<EnumRepr> {
40 peek_enum(shape).map(|enum_def| enum_def.enum_repr)
41}
42
43pub fn peek_enum_variants(shape: &'static Shape) -> Option<&'static [Variant]> {
45 peek_enum(shape).map(|enum_def| enum_def.variants)
46}
47
48impl<'mem, 'facet_lifetime> core::ops::Deref for PeekEnum<'mem, 'facet_lifetime> {
49 type Target = Peek<'mem, 'facet_lifetime>;
50
51 #[inline(always)]
52 fn deref(&self) -> &Self::Target {
53 &self.value
54 }
55}
56
57impl<'mem, 'facet_lifetime> PeekEnum<'mem, 'facet_lifetime> {
58 #[inline(always)]
60 pub fn ty(self) -> EnumType {
61 self.ty
62 }
63
64 #[inline(always)]
66 pub fn enum_repr(self) -> EnumRepr {
67 self.ty.enum_repr
68 }
69
70 #[inline(always)]
72 pub fn variants(self) -> &'static [Variant] {
73 self.ty.variants
74 }
75
76 #[inline(always)]
78 pub fn variant_count(self) -> usize {
79 self.ty.variants.len()
80 }
81
82 #[inline(always)]
84 pub fn variant_name(self, index: usize) -> Option<&'static str> {
85 self.ty.variants.get(index).map(|variant| variant.name)
86 }
87
88 #[inline]
90 pub fn discriminant(self) -> i64 {
91 unsafe {
93 let data = self.value.data();
94 match self.ty.enum_repr {
95 EnumRepr::U8 => data.read::<u8>() as i64,
96 EnumRepr::U16 => data.read::<u16>() as i64,
97 EnumRepr::U32 => data.read::<u32>() as i64,
98 EnumRepr::U64 => data.read::<u64>() as i64,
99 EnumRepr::USize => data.read::<usize>() as i64,
100 EnumRepr::I8 => data.read::<i8>() as i64,
101 EnumRepr::I16 => data.read::<i16>() as i64,
102 EnumRepr::I32 => data.read::<i32>() as i64,
103 EnumRepr::I64 => data.read::<i64>(),
104 EnumRepr::ISize => data.read::<isize>() as i64,
105 _ => {
106 data.read::<u32>() as i64
108 }
109 }
110 }
111 }
112
113 #[inline]
115 pub fn variant_index(self) -> Result<usize, VariantError> {
116 if self.ty.enum_repr == EnumRepr::RustNPO {
117 let layout = self
119 .value
120 .shape
121 .layout
122 .sized_layout()
123 .expect("Unsized enums in NPO repr are unsupported");
124
125 let data = self.value.data();
126 let slice = unsafe { core::slice::from_raw_parts(data.as_byte_ptr(), layout.size()) };
127 let all_zero = slice.iter().all(|v| *v == 0);
128
129 trace!(
130 "PeekEnum::variant_index (RustNPO): layout size = {}, all_zero = {} (slice is actually {:?})",
131 layout.size(),
132 all_zero,
133 slice
134 );
135
136 Ok(self
137 .ty
138 .variants
139 .iter()
140 .enumerate()
141 .position(|#[allow(unused)] (variant_idx, variant)| {
142 let mut max_offset = 0;
144
145 for field in variant.data.fields {
146 let offset = field.offset
147 + field
148 .shape
149 .layout
150 .sized_layout()
151 .map(|v| v.size())
152 .unwrap_or(0);
153 max_offset = core::cmp::max(max_offset, offset);
154 }
155
156 trace!(
157 " variant[{}] = '{}', max_offset = {}",
158 variant_idx, variant.name, max_offset
159 );
160
161 if all_zero {
164 max_offset == 0
165 } else {
166 max_offset != 0
167 }
168 })
169 .expect("No variant found with matching discriminant"))
170 } else {
171 let discriminant = self.discriminant();
172
173 trace!(
174 "PeekEnum::variant_index: discriminant = {} (repr = {:?})",
175 discriminant, self.ty.enum_repr
176 );
177
178 Ok(self
180 .ty
181 .variants
182 .iter()
183 .enumerate()
184 .position(|#[allow(unused)] (variant_idx, variant)| {
185 variant.discriminant == Some(discriminant)
186 })
187 .expect("No variant found with matching discriminant"))
188 }
189 }
190
191 #[inline]
193 pub fn active_variant(self) -> Result<&'static Variant, VariantError> {
194 let index = self.variant_index()?;
195 Ok(&self.ty.variants[index])
196 }
197
198 #[inline]
200 pub fn variant_name_active(self) -> Result<&'static str, VariantError> {
201 Ok(self.active_variant()?.name)
202 }
203
204 pub fn field(self, index: usize) -> Result<Option<Peek<'mem, 'facet_lifetime>>, VariantError> {
208 let variant = self.active_variant()?;
209 let fields = &variant.data.fields;
210
211 if index >= fields.len() {
212 return Ok(None);
213 }
214
215 let field = &fields[index];
216 let field_data = unsafe { self.value.data().field(field.offset) };
217 Ok(Some(unsafe {
218 Peek::unchecked_new(field_data, field.shape())
219 }))
220 }
221
222 pub fn field_index(self, field_name: &str) -> Result<Option<usize>, VariantError> {
224 let variant = self.active_variant()?;
225 Ok(variant
226 .data
227 .fields
228 .iter()
229 .position(|f| f.name == field_name))
230 }
231
232 pub fn field_by_name(
234 self,
235 field_name: &str,
236 ) -> Result<Option<Peek<'mem, 'facet_lifetime>>, VariantError> {
237 let index_opt = self.field_index(field_name)?;
238 match index_opt {
239 Some(index) => self.field(index),
240 None => Ok(None),
241 }
242 }
243}
244
245impl<'mem, 'facet_lifetime> HasFields<'mem, 'facet_lifetime> for PeekEnum<'mem, 'facet_lifetime> {
246 fn fields(&self) -> impl DoubleEndedIterator<Item = (Field, Peek<'mem, 'facet_lifetime>)> {
247 let variant = match self.active_variant() {
249 Ok(v) => v,
250 Err(e) => panic!("Cannot get active variant: {:?}", e),
251 };
252 let fields = &variant.data.fields;
253
254 (0..fields.len()).filter_map(move |i| {
256 let field = fields[i];
258 let field_value = match self.field(i) {
260 Ok(Some(v)) => v,
261 Ok(None) => return None,
262 Err(e) => panic!("Cannot get field: {:?}", e),
263 };
264 Some((field, field_value))
266 })
267 }
268}
269
270#[derive(Clone, Copy, PartialEq, Eq)]
272pub enum VariantError {
273 OpaqueInternals,
275}
276
277impl core::fmt::Display for VariantError {
278 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
279 write!(f, "enum layout is opaque, cannot determine variant")
280 }
281}
282
283impl core::fmt::Debug for VariantError {
284 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
285 write!(
286 f,
287 "VariantError::OpaqueInternals: enum layout is opaque, cannot determine variant"
288 )
289 }
290}
291
292impl core::error::Error for VariantError {}