Skip to main content

facet_reflect/
error.rs

1use facet_core::{Characteristic, EnumType, FieldError, Shape, TryFromError};
2
3/// A kind-only version of Tracker
4#[allow(missing_docs)]
5#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
6#[non_exhaustive]
7pub enum TrackerKind {
8    Scalar,
9    Array,
10    Struct,
11    SmartPointer,
12    SmartPointerSlice,
13    Enum,
14    List,
15    Map,
16    Set,
17    Option,
18    Result,
19    DynamicValue,
20}
21
22/// Errors that can occur when reflecting on types.
23#[derive(Clone)]
24pub enum ReflectError {
25    /// Tried to set an enum to a variant that does not exist
26    NoSuchVariant {
27        /// The enum definition containing all known variants.
28        enum_type: EnumType,
29    },
30
31    /// Tried to get the wrong shape out of a value — e.g. we were manipulating
32    /// a `String`, but `.get()` was called with a `u64` or something.
33    WrongShape {
34        /// The expected shape of the value.
35        expected: &'static Shape,
36        /// The actual shape of the value.
37        actual: &'static Shape,
38    },
39
40    /// Attempted to perform an operation that expected a struct or something
41    WasNotA {
42        /// The name of the expected type.
43        expected: &'static str,
44
45        /// The type we got instead
46        actual: &'static Shape,
47    },
48
49    /// A field was not initialized during build
50    UninitializedField {
51        /// The shape containing the field
52        shape: &'static Shape,
53        /// The name of the field that wasn't initialized
54        field_name: &'static str,
55    },
56
57    /// A field in an enum variant was not initialized during build
58    UninitializedEnumField {
59        /// The enum shape
60        shape: &'static Shape,
61        /// The name of the field that wasn't initialized
62        field_name: &'static str,
63        /// The name of the variant containing the field
64        variant_name: &'static str,
65    },
66
67    /// A scalar value was not initialized during build
68    UninitializedValue {
69        /// The scalar shape
70        shape: &'static Shape,
71    },
72
73    /// An invariant of the reflection system was violated.
74    InvariantViolation {
75        /// The invariant that was violated.
76        invariant: &'static str,
77    },
78
79    /// Attempted to set a value to its default, but the value doesn't implement `Default`.
80    MissingCharacteristic {
81        /// The shape of the value that doesn't implement `Default`.
82        shape: &'static Shape,
83        /// The characteristic that is missing.
84        characteristic: Characteristic,
85    },
86
87    /// An operation failed for a given shape
88    OperationFailed {
89        /// The shape of the value for which the operation failed.
90        shape: &'static Shape,
91        /// The name of the operation that failed.
92        operation: &'static str,
93    },
94
95    /// Failed to parse a string value into the target type
96    ParseFailed {
97        /// The shape we were trying to parse into.
98        shape: &'static Shape,
99        /// The input string that failed to parse.
100        input: alloc::string::String,
101    },
102
103    /// An error occurred when attempting to access or modify a field.
104    FieldError {
105        /// The shape of the value containing the field.
106        shape: &'static Shape,
107        /// The specific error that occurred with the field.
108        field_error: FieldError,
109    },
110
111    /// Attempted to mutate struct fields on a type that is not POD (Plain Old Data).
112    ///
113    /// Field mutation through reflection requires the parent struct to be POD
114    /// (have no invariants). Mark the struct with `#[facet(pod)]` to enable
115    /// field mutation. Wholesale replacement via `Poke::set()` is always allowed.
116    NotPod {
117        /// The shape of the struct that is not POD.
118        shape: &'static Shape,
119    },
120
121    /// Indicates that we try to access a field on an `Arc<T>`, for example, and the field might exist
122    /// on the T, but you need to do begin_smart_ptr first when using the WIP API.
123    MissingPushPointee {
124        /// The smart pointer (`Arc<T>`, `Box<T>` etc.) shape on which field was caleld
125        shape: &'static Shape,
126    },
127
128    /// An unknown error occurred.
129    Unknown,
130
131    /// An error occured while putting
132    TryFromError {
133        /// The shape of the value being converted from.
134        src_shape: &'static Shape,
135
136        /// The shape of the value being converted to.
137        dst_shape: &'static Shape,
138
139        /// The inner error
140        inner: TryFromError,
141    },
142
143    /// A shape has a `default` attribute, but no implementation of the `Default` trait.
144    DefaultAttrButNoDefaultImpl {
145        /// The shape of the value that has a `default` attribute but no default implementation.
146        shape: &'static Shape,
147    },
148
149    /// The type is unsized
150    Unsized {
151        /// The shape for the type that is unsized
152        shape: &'static Shape,
153        /// The operation we were trying to perform
154        operation: &'static str,
155    },
156
157    /// Array not fully initialized during build
158    ArrayNotFullyInitialized {
159        /// The shape of the array
160        shape: &'static Shape,
161        /// The number of elements pushed
162        pushed_count: usize,
163        /// The expected array size
164        expected_size: usize,
165    },
166
167    /// Array index out of bounds
168    ArrayIndexOutOfBounds {
169        /// The shape of the array
170        shape: &'static Shape,
171        /// The index that was out of bounds
172        index: usize,
173        /// The array size
174        size: usize,
175    },
176
177    /// Invalid operation for the current state
178    InvalidOperation {
179        /// The operation that was attempted
180        operation: &'static str,
181        /// The reason why it failed
182        reason: &'static str,
183    },
184
185    /// Unexpected tracker state when performing a reflection operation
186    UnexpectedTracker {
187        /// User-friendly message including operation that was being
188        /// attempted
189        message: &'static str,
190
191        /// The current tracker set for this frame
192        current_tracker: TrackerKind,
193    },
194
195    /// No active frame in Partial
196    NoActiveFrame,
197
198    #[cfg(feature = "alloc")]
199    /// Error during custom deserialization
200    CustomDeserializationError {
201        /// Error message provided by the deserialize_with method
202        message: alloc::string::String,
203        /// Shape that was passed to deserialize_with
204        src_shape: &'static Shape,
205        /// the shape of the target type
206        dst_shape: &'static Shape,
207    },
208
209    #[cfg(feature = "alloc")]
210    /// A user-defined invariant check failed during build
211    UserInvariantFailed {
212        /// The error message from the invariant check
213        message: alloc::string::String,
214        /// The shape of the value that failed the invariant check
215        shape: &'static Shape,
216    },
217
218    #[cfg(feature = "alloc")]
219    /// Error during custom serialization
220    CustomSerializationError {
221        /// Error message provided by the serialize_with method
222        message: alloc::string::String,
223        /// Shape that was passed to serialize_with
224        src_shape: &'static Shape,
225        /// the shape of the target
226        dst_shape: &'static Shape,
227    },
228}
229
230impl core::fmt::Display for ReflectError {
231    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
232        match self {
233            ReflectError::NoSuchVariant { enum_type } => {
234                write!(f, "No such variant in enum. Known variants: ")?;
235                for v in enum_type.variants {
236                    write!(f, ", {}", v.name)?;
237                }
238                write!(f, ", that's it.")
239            }
240            ReflectError::WrongShape { expected, actual } => {
241                write!(f, "Wrong shape: expected {expected}, but got {actual}")
242            }
243            ReflectError::WasNotA { expected, actual } => {
244                write!(f, "Wrong shape: expected {expected}, but got {actual}")
245            }
246            ReflectError::UninitializedField { shape, field_name } => {
247                write!(
248                    f,
249                    "Field '{shape}::{field_name}' was not initialized. \
250                    If you need to leave fields partially initialized and come back later, \
251                    use deferred mode (begin_deferred/finish_deferred)"
252                )
253            }
254            ReflectError::UninitializedEnumField {
255                shape,
256                field_name,
257                variant_name,
258            } => {
259                write!(
260                    f,
261                    "Field '{shape}::{field_name}' in variant '{variant_name}' was not initialized. \
262                    If you need to leave fields partially initialized and come back later, \
263                    use deferred mode (begin_deferred/finish_deferred)"
264                )
265            }
266            ReflectError::UninitializedValue { shape } => {
267                write!(
268                    f,
269                    "Value '{shape}' was not initialized. \
270                    If you need to leave values partially initialized and come back later, \
271                    use deferred mode (begin_deferred/finish_deferred)"
272                )
273            }
274            ReflectError::InvariantViolation { invariant } => {
275                write!(f, "Invariant violation: {invariant}")
276            }
277            ReflectError::MissingCharacteristic {
278                shape,
279                characteristic,
280            } => write!(
281                f,
282                "{shape} does not implement characteristic {characteristic:?}",
283            ),
284            ReflectError::OperationFailed { shape, operation } => {
285                write!(f, "Operation failed on shape {shape}: {operation}")
286            }
287            ReflectError::ParseFailed { shape, input } => {
288                write!(f, "failed to parse \"{input}\" as {shape}")
289            }
290            ReflectError::FieldError { shape, field_error } => {
291                write!(f, "Field error for shape {shape}: {field_error}")
292            }
293            ReflectError::NotPod { shape } => {
294                write!(
295                    f,
296                    "Cannot mutate fields of '{shape}' - it is not POD (Plain Old Data). \
297                     Add #[facet(pod)] to the struct to enable field mutation. \
298                     (Wholesale replacement via Poke::set() is always allowed.)"
299                )
300            }
301            ReflectError::MissingPushPointee { shape } => {
302                write!(
303                    f,
304                    "Tried to access a field on smart pointer '{shape}', but you need to call .begin_smart_ptr() first to work with the value it points to (and pop it with .pop() later)"
305                )
306            }
307            ReflectError::Unknown => write!(f, "Unknown error"),
308            ReflectError::TryFromError {
309                src_shape,
310                dst_shape,
311                inner,
312            } => {
313                write!(
314                    f,
315                    "While trying to put {src_shape} into a {dst_shape}: {inner}"
316                )
317            }
318            ReflectError::DefaultAttrButNoDefaultImpl { shape } => write!(
319                f,
320                "Shape '{shape}' has a `default` attribute but no default implementation"
321            ),
322            ReflectError::Unsized { shape, operation } => write!(
323                f,
324                "Shape '{shape}' is unsized, can't perform operation {operation}"
325            ),
326            ReflectError::ArrayNotFullyInitialized {
327                shape,
328                pushed_count,
329                expected_size,
330            } => {
331                write!(
332                    f,
333                    "Array '{shape}' not fully initialized: expected {expected_size} elements, but got {pushed_count}"
334                )
335            }
336            ReflectError::ArrayIndexOutOfBounds { shape, index, size } => {
337                write!(
338                    f,
339                    "Array index {index} out of bounds for '{shape}' (array length is {size})"
340                )
341            }
342            ReflectError::InvalidOperation { operation, reason } => {
343                write!(f, "Invalid operation '{operation}': {reason}")
344            }
345            ReflectError::UnexpectedTracker {
346                message,
347                current_tracker,
348            } => {
349                write!(f, "{message}: current tracker is {current_tracker:?}")
350            }
351            ReflectError::NoActiveFrame => {
352                write!(f, "No active frame in Partial")
353            }
354            #[cfg(feature = "alloc")]
355            ReflectError::CustomDeserializationError {
356                message,
357                src_shape,
358                dst_shape,
359            } => {
360                write!(
361                    f,
362                    "Custom deserialization of shape '{src_shape}' into '{dst_shape}' failed: {message}"
363                )
364            }
365            #[cfg(feature = "alloc")]
366            ReflectError::CustomSerializationError {
367                message,
368                src_shape,
369                dst_shape,
370            } => {
371                write!(
372                    f,
373                    "Custom serialization of shape '{src_shape}' into '{dst_shape}' failed: {message}"
374                )
375            }
376            #[cfg(feature = "alloc")]
377            ReflectError::UserInvariantFailed { message, shape } => {
378                write!(f, "Invariant check failed for '{shape}': {message}")
379            }
380        }
381    }
382}
383
384impl core::fmt::Debug for ReflectError {
385    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
386        // Use Display implementation for more readable output
387        write!(f, "ReflectError({self})")
388    }
389}
390
391impl core::error::Error for ReflectError {}