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
26pub fn peek_enum<'shape>(shape: &'shape Shape) -> Option<EnumType<'shape>> {
28 match shape.ty {
29 facet_core::Type::User(UserType::Enum(enum_ty)) => Some(enum_ty),
30 _ => None,
31 }
32}
33
34pub fn peek_enum_repr(shape: &Shape) -> Option<EnumRepr> {
36 peek_enum(shape).map(|enum_def| enum_def.enum_repr)
37}
38
39pub fn peek_enum_variants<'shape>(shape: &'shape Shape) -> Option<&'shape [Variant<'shape>]> {
41 peek_enum(shape).map(|enum_def| enum_def.variants)
42}
43
44impl<'mem, 'facet, 'shape> core::ops::Deref for PeekEnum<'mem, 'facet, 'shape> {
45 type Target = Peek<'mem, 'facet, 'shape>;
46
47 #[inline(always)]
48 fn deref(&self) -> &Self::Target {
49 &self.value
50 }
51}
52
53impl<'mem, 'facet, 'shape> PeekEnum<'mem, 'facet, 'shape> {
54 #[inline(always)]
56 pub fn ty(self) -> EnumType<'shape> {
57 self.ty
58 }
59
60 #[inline(always)]
62 pub fn enum_repr(self) -> EnumRepr {
63 self.ty.enum_repr
64 }
65
66 #[inline(always)]
68 pub fn variants(self) -> &'shape [Variant<'shape>] {
69 self.ty.variants
70 }
71
72 #[inline(always)]
74 pub fn variant_count(self) -> usize {
75 self.ty.variants.len()
76 }
77
78 #[inline(always)]
80 pub fn variant_name(self, index: usize) -> Option<&'shape str> {
81 self.ty.variants.get(index).map(|variant| variant.name)
82 }
83
84 #[inline]
86 pub fn discriminant(self) -> i64 {
87 unsafe {
89 let data = self
90 .value
91 .data()
92 .thin()
93 .expect("discriminant must be Sized");
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().thin().unwrap();
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<&'shape Variant<'shape>, 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<&'shape str, VariantError> {
201 Ok(self.active_variant()?.name)
202 }
203
204 pub fn field(self, index: usize) -> Result<Option<Peek<'mem, 'facet, 'shape>>, 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 {
217 self.value
218 .data()
219 .thin()
220 .ok_or(VariantError::Unsized)?
221 .field(field.offset)
222 };
223 Ok(Some(unsafe {
224 Peek::unchecked_new(field_data, field.shape())
225 }))
226 }
227
228 pub fn field_index(self, field_name: &str) -> Result<Option<usize>, VariantError> {
230 let variant = self.active_variant()?;
231 Ok(variant
232 .data
233 .fields
234 .iter()
235 .position(|f| f.name == field_name))
236 }
237
238 pub fn field_by_name(
240 self,
241 field_name: &str,
242 ) -> Result<Option<Peek<'mem, 'facet, 'shape>>, VariantError> {
243 let index_opt = self.field_index(field_name)?;
244 match index_opt {
245 Some(index) => self.field(index),
246 None => Ok(None),
247 }
248 }
249}
250
251impl<'mem, 'facet, 'shape> HasFields<'mem, 'facet, 'shape> for PeekEnum<'mem, 'facet, 'shape> {
252 fn fields(&self) -> FieldIter<'mem, 'facet, 'shape> {
253 FieldIter::new_enum(*self)
254 }
255}
256
257#[derive(Clone, Copy, PartialEq, Eq)]
259pub enum VariantError {
260 OpaqueInternals,
262
263 Unsized,
265}
266
267impl core::fmt::Display for VariantError {
268 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
269 match self {
270 VariantError::OpaqueInternals => {
271 write!(f, "enum layout is opaque, cannot determine variant")
272 }
273 VariantError::Unsized => {
274 write!(
275 f,
276 "enum value is unsized and cannot be accessed by field offset"
277 )
278 }
279 }
280 }
281}
282
283impl core::fmt::Debug for VariantError {
284 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
285 match self {
286 VariantError::OpaqueInternals => {
287 write!(
288 f,
289 "VariantError::OpaqueInternals: enum layout is opaque, cannot determine variant"
290 )
291 }
292 VariantError::Unsized => {
293 write!(
294 f,
295 "VariantError::Unsized: enum value is unsized and cannot be accessed by field offset"
296 )
297 }
298 }
299 }
300}
301
302impl core::error::Error for VariantError {}