1use facet_core::{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, 'shape> {
10 pub(crate) value: Peek<'mem, 'facet, 'shape>,
15
16 pub(crate) ty: EnumType<'shape>,
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>(shape: &'shape Shape) -> Option<EnumType<'shape>> {
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: &Shape) -> Option<EnumRepr> {
38 peek_enum(shape).map(|enum_def| enum_def.enum_repr)
39}
40
41#[inline]
43pub fn peek_enum_variants<'shape>(shape: &'shape Shape) -> Option<&'shape [Variant<'shape>]> {
44 peek_enum(shape).map(|enum_def| enum_def.variants)
45}
46
47impl<'mem, 'facet, 'shape> core::ops::Deref for PeekEnum<'mem, 'facet, 'shape> {
48 type Target = Peek<'mem, 'facet, 'shape>;
49
50 #[inline(always)]
51 fn deref(&self) -> &Self::Target {
52 &self.value
53 }
54}
55
56impl<'mem, 'facet, 'shape> PeekEnum<'mem, 'facet, 'shape> {
57 #[inline(always)]
59 pub fn ty(self) -> EnumType<'shape> {
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) -> &'shape [Variant<'shape>] {
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<&'shape str> {
84 self.ty.variants.get(index).map(|variant| variant.name)
85 }
86
87 #[inline]
89 pub fn discriminant(self) -> i64 {
90 unsafe {
92 let data = self
93 .value
94 .data()
95 .thin()
96 .expect("discriminant must be Sized");
97 match self.ty.enum_repr {
98 EnumRepr::U8 => data.read::<u8>() as i64,
99 EnumRepr::U16 => data.read::<u16>() as i64,
100 EnumRepr::U32 => data.read::<u32>() as i64,
101 EnumRepr::U64 => data.read::<u64>() as i64,
102 EnumRepr::USize => data.read::<usize>() as i64,
103 EnumRepr::I8 => data.read::<i8>() as i64,
104 EnumRepr::I16 => data.read::<i16>() as i64,
105 EnumRepr::I32 => data.read::<i32>() as i64,
106 EnumRepr::I64 => data.read::<i64>(),
107 EnumRepr::ISize => data.read::<isize>() as i64,
108 _ => {
109 data.read::<u32>() as i64
111 }
112 }
113 }
114 }
115
116 #[inline]
118 pub fn variant_index(self) -> Result<usize, VariantError> {
119 if self.ty.enum_repr == EnumRepr::RustNPO {
120 let layout = self
122 .value
123 .shape
124 .layout
125 .sized_layout()
126 .expect("Unsized enums in NPO repr are unsupported");
127
128 let data = self.value.data().thin().unwrap();
129 let slice = unsafe { core::slice::from_raw_parts(data.as_byte_ptr(), layout.size()) };
130 let all_zero = slice.iter().all(|v| *v == 0);
131
132 trace!(
133 "PeekEnum::variant_index (RustNPO): layout size = {}, all_zero = {} (slice is actually {:?})",
134 layout.size(),
135 all_zero,
136 slice
137 );
138
139 Ok(self
140 .ty
141 .variants
142 .iter()
143 .enumerate()
144 .position(|#[allow(unused)] (variant_idx, variant)| {
145 let mut max_offset = 0;
147
148 for field in variant.data.fields {
149 let offset = field.offset
150 + field
151 .shape
152 .layout
153 .sized_layout()
154 .map(|v| v.size())
155 .unwrap_or(0);
156 max_offset = core::cmp::max(max_offset, offset);
157 }
158
159 trace!(
160 " variant[{}] = '{}', max_offset = {}",
161 variant_idx, variant.name, max_offset
162 );
163
164 if all_zero {
167 max_offset == 0
168 } else {
169 max_offset != 0
170 }
171 })
172 .expect("No variant found with matching discriminant"))
173 } else {
174 let discriminant = self.discriminant();
175
176 trace!(
177 "PeekEnum::variant_index: discriminant = {} (repr = {:?})",
178 discriminant, self.ty.enum_repr
179 );
180
181 Ok(self
183 .ty
184 .variants
185 .iter()
186 .enumerate()
187 .position(|#[allow(unused)] (variant_idx, variant)| {
188 variant.discriminant == Some(discriminant)
189 })
190 .expect("No variant found with matching discriminant"))
191 }
192 }
193
194 #[inline]
196 pub fn active_variant(self) -> Result<&'shape Variant<'shape>, VariantError> {
197 let index = self.variant_index()?;
198 Ok(&self.ty.variants[index])
199 }
200
201 #[inline]
203 pub fn variant_name_active(self) -> Result<&'shape str, VariantError> {
204 Ok(self.active_variant()?.name)
205 }
206
207 pub fn field(self, index: usize) -> Result<Option<Peek<'mem, 'facet, 'shape>>, VariantError> {
211 let variant = self.active_variant()?;
212 let fields = &variant.data.fields;
213
214 if index >= fields.len() {
215 return Ok(None);
216 }
217
218 let field = &fields[index];
219 let field_data = unsafe {
220 self.value
221 .data()
222 .thin()
223 .ok_or(VariantError::Unsized)?
224 .field(field.offset)
225 };
226 Ok(Some(unsafe {
227 Peek::unchecked_new(field_data, field.shape())
228 }))
229 }
230
231 pub fn field_index(self, field_name: &str) -> Result<Option<usize>, VariantError> {
233 let variant = self.active_variant()?;
234 Ok(variant
235 .data
236 .fields
237 .iter()
238 .position(|f| f.name == field_name))
239 }
240
241 pub fn field_by_name(
243 self,
244 field_name: &str,
245 ) -> Result<Option<Peek<'mem, 'facet, 'shape>>, VariantError> {
246 let index_opt = self.field_index(field_name)?;
247 match index_opt {
248 Some(index) => self.field(index),
249 None => Ok(None),
250 }
251 }
252}
253
254impl<'mem, 'facet, 'shape> HasFields<'mem, 'facet, 'shape> for PeekEnum<'mem, 'facet, 'shape> {
255 #[inline]
256 fn fields(&self) -> FieldIter<'mem, 'facet, 'shape> {
257 FieldIter::new_enum(*self)
258 }
259}
260
261#[derive(Clone, Copy, PartialEq, Eq)]
263pub enum VariantError {
264 OpaqueInternals,
266
267 Unsized,
269}
270
271impl core::fmt::Display for VariantError {
272 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
273 match self {
274 VariantError::OpaqueInternals => {
275 write!(f, "enum layout is opaque, cannot determine variant")
276 }
277 VariantError::Unsized => {
278 write!(
279 f,
280 "enum value is unsized and cannot be accessed by field offset"
281 )
282 }
283 }
284 }
285}
286
287impl core::fmt::Debug for VariantError {
288 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
289 match self {
290 VariantError::OpaqueInternals => {
291 write!(
292 f,
293 "VariantError::OpaqueInternals: enum layout is opaque, cannot determine variant"
294 )
295 }
296 VariantError::Unsized => {
297 write!(
298 f,
299 "VariantError::Unsized: enum value is unsized and cannot be accessed by field offset"
300 )
301 }
302 }
303 }
304}
305
306impl core::error::Error for VariantError {}