facet_reflect/peek/
enum_.rs1use 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> {
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]
89 pub fn discriminant(self) -> i64 {
90 unsafe {
92 let data = self.value.data();
93 match self.ty.enum_repr {
94 EnumRepr::U8 => data.read::<u8>() as i64,
95 EnumRepr::U16 => data.read::<u16>() as i64,
96 EnumRepr::U32 => data.read::<u32>() as i64,
97 EnumRepr::U64 => data.read::<u64>() as i64,
98 EnumRepr::USize => data.read::<usize>() as i64,
99 EnumRepr::I8 => data.read::<i8>() as i64,
100 EnumRepr::I16 => data.read::<i16>() as i64,
101 EnumRepr::I32 => data.read::<i32>() as i64,
102 EnumRepr::I64 => data.read::<i64>(),
103 EnumRepr::ISize => data.read::<isize>() as i64,
104 _ => {
105 data.read::<u32>() as i64
107 }
108 }
109 }
110 }
111
112 #[inline]
114 pub fn variant_index(self) -> Result<usize, VariantError> {
115 if self.ty.enum_repr == EnumRepr::RustNPO {
116 let layout = self
118 .value
119 .shape
120 .layout
121 .sized_layout()
122 .expect("Unsized enums in NPO repr are unsupported");
123
124 let data = self.value.data();
125 let slice = unsafe { core::slice::from_raw_parts(data.as_byte_ptr(), layout.size()) };
126 let all_zero = slice.iter().all(|v| *v == 0);
127
128 trace!(
129 "PeekEnum::variant_index (RustNPO): layout size = {}, all_zero = {} (slice is actually {:?})",
130 layout.size(),
131 all_zero,
132 slice
133 );
134
135 Ok(self
136 .ty
137 .variants
138 .iter()
139 .enumerate()
140 .position(|#[allow(unused)] (variant_idx, variant)| {
141 let mut max_offset = 0;
143
144 for field in variant.data.fields {
145 let offset = field.offset
146 + field
147 .shape()
148 .layout
149 .sized_layout()
150 .map(|v| v.size())
151 .unwrap_or(0);
152 max_offset = core::cmp::max(max_offset, offset);
153 }
154
155 trace!(
156 " variant[{}] = '{}', max_offset = {}",
157 variant_idx, variant.name, max_offset
158 );
159
160 if all_zero {
163 max_offset == 0
164 } else {
165 max_offset != 0
166 }
167 })
168 .expect("No variant found with matching discriminant"))
169 } else {
170 let discriminant = self.discriminant();
171
172 trace!(
173 "PeekEnum::variant_index: discriminant = {} (repr = {:?})",
174 discriminant, self.ty.enum_repr
175 );
176
177 Ok(self
179 .ty
180 .variants
181 .iter()
182 .enumerate()
183 .position(|#[allow(unused)] (variant_idx, variant)| {
184 variant.discriminant == Some(discriminant)
185 })
186 .expect("No variant found with matching discriminant"))
187 }
188 }
189
190 #[inline]
192 pub fn active_variant(self) -> Result<&'static Variant, VariantError> {
193 let index = self.variant_index()?;
194 Ok(&self.ty.variants[index])
195 }
196
197 #[inline]
199 pub fn variant_name_active(self) -> Result<&'static str, VariantError> {
200 Ok(self.active_variant()?.name)
201 }
202
203 pub fn field(self, index: usize) -> Result<Option<Peek<'mem, 'facet>>, VariantError> {
207 let variant = self.active_variant()?;
208 let fields = &variant.data.fields;
209
210 if index >= fields.len() {
211 return Ok(None);
212 }
213
214 let field = &fields[index];
215 let field_data = unsafe { self.value.data().field(field.offset) };
216 Ok(Some(unsafe {
217 Peek::unchecked_new(field_data, field.shape())
218 }))
219 }
220
221 pub fn field_index(self, field_name: &str) -> Result<Option<usize>, VariantError> {
223 let variant = self.active_variant()?;
224 Ok(variant
225 .data
226 .fields
227 .iter()
228 .position(|f| f.name == field_name))
229 }
230
231 pub fn field_by_name(
233 self,
234 field_name: &str,
235 ) -> Result<Option<Peek<'mem, 'facet>>, VariantError> {
236 let index_opt = self.field_index(field_name)?;
237 match index_opt {
238 Some(index) => self.field(index),
239 None => Ok(None),
240 }
241 }
242}
243
244impl<'mem, 'facet> HasFields<'mem, 'facet> for PeekEnum<'mem, 'facet> {
245 #[inline]
246 fn fields(&self) -> FieldIter<'mem, 'facet> {
247 FieldIter::new_enum(*self)
248 }
249}
250
251#[derive(Clone, Copy, PartialEq, Eq)]
253pub enum VariantError {
254 OpaqueInternals,
256
257 Unsized,
259}
260
261impl core::fmt::Display for VariantError {
262 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
263 match self {
264 VariantError::OpaqueInternals => {
265 write!(f, "enum layout is opaque, cannot determine variant")
266 }
267 VariantError::Unsized => {
268 write!(
269 f,
270 "enum value is unsized and cannot be accessed by field offset"
271 )
272 }
273 }
274 }
275}
276
277impl core::fmt::Debug for VariantError {
278 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
279 match self {
280 VariantError::OpaqueInternals => {
281 write!(
282 f,
283 "VariantError::OpaqueInternals: enum layout is opaque, cannot determine variant"
284 )
285 }
286 VariantError::Unsized => {
287 write!(
288 f,
289 "VariantError::Unsized: enum value is unsized and cannot be accessed by field offset"
290 )
291 }
292 }
293 }
294}
295
296impl core::error::Error for VariantError {}