facet_reflect/peek/
enum_.rs1use facet_core::{Def, EnumRepr, EnumType, Shape, UserType, Variant};
2
3use crate::{Peek, trace};
4
5use super::{FieldIter, HasFields};
6
7#[derive(Clone, Copy)]
9pub struct PeekEnum<'mem, 'facet> {
10 pub(crate) value: Peek<'mem, 'facet>,
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 write!(f, "{:?}", self.value)
23 }
24}
25
26#[inline]
28pub fn peek_enum(shape: &'static Shape) -> Option<EnumType> {
29 match shape.ty {
30 facet_core::Type::User(UserType::Enum(enum_ty)) => Some(enum_ty),
31 _ => None,
32 }
33}
34
35#[inline]
37pub fn peek_enum_repr(shape: &'static Shape) -> Option<EnumRepr> {
38 peek_enum(shape).map(|enum_def| enum_def.enum_repr)
39}
40
41#[inline]
43pub fn peek_enum_variants(shape: &'static Shape) -> Option<&'static [Variant]> {
44 peek_enum(shape).map(|enum_def| enum_def.variants)
45}
46
47impl<'mem, 'facet> core::ops::Deref for PeekEnum<'mem, 'facet> {
48 type Target = Peek<'mem, 'facet>;
49
50 #[inline(always)]
51 fn deref(&self) -> &Self::Target {
52 &self.value
53 }
54}
55
56impl<'mem, 'facet> PeekEnum<'mem, 'facet> {
57 #[inline(always)]
59 pub fn ty(self) -> EnumType {
60 self.ty
61 }
62
63 #[inline(always)]
65 pub fn enum_repr(self) -> EnumRepr {
66 self.ty.enum_repr
67 }
68
69 #[inline(always)]
71 pub fn variants(self) -> &'static [Variant] {
72 self.ty.variants
73 }
74
75 #[inline(always)]
77 pub fn variant_count(self) -> usize {
78 self.ty.variants.len()
79 }
80
81 #[inline(always)]
83 pub fn variant_name(self, index: usize) -> Option<&'static str> {
84 self.ty.variants.get(index).map(|variant| variant.name)
85 }
86
87 #[inline]
93 pub fn discriminant(self) -> i64 {
94 match self.ty.enum_repr {
96 EnumRepr::RustNPO => 0,
101 EnumRepr::U8 => unsafe { self.value.data().read::<u8>() as i64 },
102 EnumRepr::U16 => unsafe { self.value.data().read::<u16>() as i64 },
103 EnumRepr::U32 => unsafe { self.value.data().read::<u32>() as i64 },
104 EnumRepr::U64 => unsafe { self.value.data().read::<u64>() as i64 },
105 EnumRepr::USize => unsafe { self.value.data().read::<usize>() as i64 },
106 EnumRepr::I8 => unsafe { self.value.data().read::<i8>() as i64 },
107 EnumRepr::I16 => unsafe { self.value.data().read::<i16>() as i64 },
108 EnumRepr::I32 => unsafe { self.value.data().read::<i32>() as i64 },
109 EnumRepr::I64 => unsafe { self.value.data().read::<i64>() },
110 EnumRepr::ISize => unsafe { self.value.data().read::<isize>() as i64 },
111 }
112 }
113
114 #[inline]
116 pub fn variant_index(self) -> Result<usize, VariantError> {
117 if self.ty.enum_repr == EnumRepr::RustNPO {
118 if let Def::Option(option_def) = self.value.shape.def {
123 let is_some = unsafe { (option_def.vtable.is_some)(self.value.data()) };
124 trace!("PeekEnum::variant_index (RustNPO Option): is_some = {is_some}");
125 return Ok(self
127 .ty
128 .variants
129 .iter()
130 .position(|variant| {
131 let has_fields = !variant.data.fields.is_empty();
132 has_fields == is_some
133 })
134 .expect("No variant found matching Option state"));
135 }
136
137 let layout = self
139 .value
140 .shape
141 .layout
142 .sized_layout()
143 .expect("Unsized enums in NPO repr are unsupported");
144
145 let data = self.value.data();
146 let slice = unsafe { core::slice::from_raw_parts(data.as_byte_ptr(), layout.size()) };
147 let all_zero = slice.iter().all(|v| *v == 0);
148
149 trace!(
150 "PeekEnum::variant_index (RustNPO): layout size = {}, all_zero = {} (slice is actually {:?})",
151 layout.size(),
152 all_zero,
153 slice
154 );
155
156 Ok(self
157 .ty
158 .variants
159 .iter()
160 .enumerate()
161 .position(|#[allow(unused)] (variant_idx, variant)| {
162 let mut max_offset = 0;
164
165 for field in variant.data.fields {
166 let offset = field.offset
167 + field
168 .shape()
169 .layout
170 .sized_layout()
171 .map(|v| v.size())
172 .unwrap_or(0);
173 max_offset = core::cmp::max(max_offset, offset);
174 }
175
176 trace!(
177 " variant[{}] = '{}', max_offset = {}",
178 variant_idx, variant.name, max_offset
179 );
180
181 if all_zero {
184 max_offset == 0
185 } else {
186 max_offset != 0
187 }
188 })
189 .expect("No variant found with matching discriminant"))
190 } else {
191 let discriminant = self.discriminant();
192
193 trace!(
194 "PeekEnum::variant_index: discriminant = {} (repr = {:?})",
195 discriminant, self.ty.enum_repr
196 );
197
198 Ok(self
200 .ty
201 .variants
202 .iter()
203 .enumerate()
204 .position(|#[allow(unused)] (variant_idx, variant)| {
205 variant.discriminant == Some(discriminant)
206 })
207 .expect("No variant found with matching discriminant"))
208 }
209 }
210
211 #[inline]
213 pub fn active_variant(self) -> Result<&'static Variant, VariantError> {
214 let index = self.variant_index()?;
215 Ok(&self.ty.variants[index])
216 }
217
218 #[inline]
220 pub fn variant_name_active(self) -> Result<&'static str, VariantError> {
221 Ok(self.active_variant()?.name)
222 }
223
224 pub fn field(self, index: usize) -> Result<Option<Peek<'mem, 'facet>>, VariantError> {
228 let variant = self.active_variant()?;
229 let fields = &variant.data.fields;
230
231 if index >= fields.len() {
232 return Ok(None);
233 }
234
235 let field = &fields[index];
236 let field_data = unsafe { self.value.data().field(field.offset) };
237 Ok(Some(unsafe {
238 Peek::unchecked_new(field_data, field.shape())
239 }))
240 }
241
242 pub fn field_index(self, field_name: &str) -> Result<Option<usize>, VariantError> {
244 let variant = self.active_variant()?;
245 Ok(variant
246 .data
247 .fields
248 .iter()
249 .position(|f| f.name == field_name))
250 }
251
252 pub fn field_by_name(
254 self,
255 field_name: &str,
256 ) -> Result<Option<Peek<'mem, 'facet>>, VariantError> {
257 let index_opt = self.field_index(field_name)?;
258 match index_opt {
259 Some(index) => self.field(index),
260 None => Ok(None),
261 }
262 }
263}
264
265impl<'mem, 'facet> HasFields<'mem, 'facet> for PeekEnum<'mem, 'facet> {
266 #[inline]
267 fn fields(&self) -> FieldIter<'mem, 'facet> {
268 FieldIter::new_enum(*self)
269 }
270}
271
272#[derive(Clone, Copy, PartialEq, Eq)]
274pub enum VariantError {
275 OpaqueInternals,
277
278 Unsized,
280}
281
282impl core::fmt::Display for VariantError {
283 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
284 match self {
285 VariantError::OpaqueInternals => {
286 write!(f, "enum layout is opaque, cannot determine variant")
287 }
288 VariantError::Unsized => {
289 write!(
290 f,
291 "enum value is unsized and cannot be accessed by field offset"
292 )
293 }
294 }
295 }
296}
297
298impl core::fmt::Debug for VariantError {
299 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
300 match self {
301 VariantError::OpaqueInternals => {
302 write!(
303 f,
304 "VariantError::OpaqueInternals: enum layout is opaque, cannot determine variant"
305 )
306 }
307 VariantError::Unsized => {
308 write!(
309 f,
310 "VariantError::Unsized: enum value is unsized and cannot be accessed by field offset"
311 )
312 }
313 }
314 }
315}
316
317impl core::error::Error for VariantError {}