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 const 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 const fn ty(self) -> EnumType {
60 self.ty
61 }
62
63 #[inline(always)]
65 pub const fn enum_repr(self) -> EnumRepr {
66 self.ty.enum_repr
67 }
68
69 #[inline(always)]
71 pub const fn variants(self) -> &'static [Variant] {
72 self.ty.variants
73 }
74
75 #[inline(always)]
77 pub const 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::Rust => {
99 panic!("cannot read discriminant from Rust enum with unspecified layout")
100 }
101 EnumRepr::RustNPO => 0,
106 EnumRepr::U8 => unsafe { self.value.data().read::<u8>() as i64 },
107 EnumRepr::U16 => unsafe { self.value.data().read::<u16>() as i64 },
108 EnumRepr::U32 => unsafe { self.value.data().read::<u32>() as i64 },
109 EnumRepr::U64 => unsafe { self.value.data().read::<u64>() as i64 },
110 EnumRepr::USize => unsafe { self.value.data().read::<usize>() as i64 },
111 EnumRepr::I8 => unsafe { self.value.data().read::<i8>() as i64 },
112 EnumRepr::I16 => unsafe { self.value.data().read::<i16>() as i64 },
113 EnumRepr::I32 => unsafe { self.value.data().read::<i32>() as i64 },
114 EnumRepr::I64 => unsafe { self.value.data().read::<i64>() },
115 EnumRepr::ISize => unsafe { self.value.data().read::<isize>() as i64 },
116 }
117 }
118
119 #[inline]
121 pub fn variant_index(self) -> Result<usize, VariantError> {
122 if let Def::Option(option_def) = self.value.shape.def {
125 let is_some = unsafe { (option_def.vtable.is_some)(self.value.data()) };
126 trace!("PeekEnum::variant_index (Option): is_some = {is_some}");
127 return Ok(self
129 .ty
130 .variants
131 .iter()
132 .position(|variant| {
133 let has_fields = !variant.data.fields.is_empty();
134 has_fields == is_some
135 })
136 .expect("No variant found matching Option state"));
137 }
138
139 if self.ty.enum_repr == EnumRepr::RustNPO {
140 let layout = self
142 .value
143 .shape
144 .layout
145 .sized_layout()
146 .expect("Unsized enums in NPO repr are unsupported");
147
148 let data = self.value.data();
149 let slice = unsafe { core::slice::from_raw_parts(data.as_byte_ptr(), layout.size()) };
150 let all_zero = slice.iter().all(|v| *v == 0);
151
152 trace!(
153 "PeekEnum::variant_index (RustNPO): layout size = {}, all_zero = {} (slice is actually {:?})",
154 layout.size(),
155 all_zero,
156 slice
157 );
158
159 Ok(self
160 .ty
161 .variants
162 .iter()
163 .enumerate()
164 .position(|#[allow(unused)] (variant_idx, variant)| {
165 let mut max_offset = 0;
167
168 for field in variant.data.fields {
169 let offset = field.offset
170 + field
171 .shape()
172 .layout
173 .sized_layout()
174 .map(|v| v.size())
175 .unwrap_or(0);
176 max_offset = core::cmp::max(max_offset, offset);
177 }
178
179 trace!(
180 " variant[{}] = '{}', max_offset = {}",
181 variant_idx, variant.name, max_offset
182 );
183
184 if all_zero {
187 max_offset == 0
188 } else {
189 max_offset != 0
190 }
191 })
192 .expect("No variant found with matching discriminant"))
193 } else {
194 let discriminant = self.discriminant();
195
196 trace!(
197 "PeekEnum::variant_index: discriminant = {} (repr = {:?})",
198 discriminant, self.ty.enum_repr
199 );
200
201 Ok(self
203 .ty
204 .variants
205 .iter()
206 .enumerate()
207 .position(|#[allow(unused)] (variant_idx, variant)| {
208 variant.discriminant == Some(discriminant)
209 })
210 .expect("No variant found with matching discriminant"))
211 }
212 }
213
214 #[inline]
216 pub fn active_variant(self) -> Result<&'static Variant, VariantError> {
217 let index = self.variant_index()?;
218 Ok(&self.ty.variants[index])
219 }
220
221 #[inline]
223 pub fn variant_name_active(self) -> Result<&'static str, VariantError> {
224 Ok(self.active_variant()?.name)
225 }
226
227 pub fn field(self, index: usize) -> Result<Option<Peek<'mem, 'facet>>, VariantError> {
231 let variant = self.active_variant()?;
232 let fields = &variant.data.fields;
233
234 if index >= fields.len() {
235 return Ok(None);
236 }
237
238 let field = &fields[index];
239 let field_data = unsafe { self.value.data().field(field.offset) };
240 Ok(Some(unsafe {
241 Peek::unchecked_new(field_data, field.shape())
242 }))
243 }
244
245 pub fn field_index(self, field_name: &str) -> Result<Option<usize>, VariantError> {
247 let variant = self.active_variant()?;
248 Ok(variant
249 .data
250 .fields
251 .iter()
252 .position(|f| f.name == field_name))
253 }
254
255 pub fn field_by_name(
257 self,
258 field_name: &str,
259 ) -> Result<Option<Peek<'mem, 'facet>>, VariantError> {
260 let index_opt = self.field_index(field_name)?;
261 match index_opt {
262 Some(index) => self.field(index),
263 None => Ok(None),
264 }
265 }
266}
267
268impl<'mem, 'facet> HasFields<'mem, 'facet> for PeekEnum<'mem, 'facet> {
269 #[inline]
270 fn fields(&self) -> FieldIter<'mem, 'facet> {
271 FieldIter::new_enum(*self)
272 }
273}
274
275#[derive(Clone, Copy, PartialEq, Eq)]
277pub enum VariantError {
278 OpaqueInternals,
280
281 Unsized,
283}
284
285impl core::fmt::Display for VariantError {
286 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
287 match self {
288 VariantError::OpaqueInternals => {
289 write!(f, "enum layout is opaque, cannot determine variant")
290 }
291 VariantError::Unsized => {
292 write!(
293 f,
294 "enum value is unsized and cannot be accessed by field offset"
295 )
296 }
297 }
298 }
299}
300
301impl core::fmt::Debug for VariantError {
302 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
303 match self {
304 VariantError::OpaqueInternals => {
305 write!(
306 f,
307 "VariantError::OpaqueInternals: enum layout is opaque, cannot determine variant"
308 )
309 }
310 VariantError::Unsized => {
311 write!(
312 f,
313 "VariantError::Unsized: enum value is unsized and cannot be accessed by field offset"
314 )
315 }
316 }
317 }
318}
319
320impl core::error::Error for VariantError {}