fory_core/
error.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! # PERFORMANCE CRITICAL MODULE
19//!
20//! **WARNING**: This module is highly performance-sensitive. Changes to error
21//! constructor attributes (`#[inline]`, `#[cold]`, `#[track_caller]`) can
22//! impact serialization/deserialization performance throughout the entire codebase.
23//!
24//! ## Why This Module Is Performance Critical
25//!
26//! Error constructors are called in **every** buffer read/write operation and type check.
27//! Even though these functions are rarely executed (error paths), their mere presence and
28//! inlining behavior affects how LLVM optimizes the **hot paths** (successful operations).
29
30use std::borrow::Cow;
31
32use thiserror::Error;
33
34/// Global flag to check if FORY_PANIC_ON_ERROR environment variable is set at compile time.
35/// Set FORY_PANIC_ON_ERROR=1 at compile time to enable panic on error.
36pub const PANIC_ON_ERROR: bool = option_env!("FORY_PANIC_ON_ERROR").is_some();
37
38/// Check if FORY_PANIC_ON_ERROR environment variable is set.
39#[inline(always)]
40pub const fn should_panic_on_error() -> bool {
41    PANIC_ON_ERROR
42}
43
44/// Error type for Fory serialization and deserialization operations.
45///
46/// # IMPORTANT: Always Use Static Constructor Functions
47///
48/// **DO NOT** construct error variants directly using the enum syntax.
49/// **ALWAYS** use the provided static constructor functions instead.
50///
51/// ## Why Use Static Functions?
52///
53/// The static constructor functions provide:
54/// - Automatic type conversion via `Into<Cow<'static, str>>`
55/// - Consistent error creation across the codebase
56/// - Better ergonomics (no need for manual `.into()` calls)
57/// - Future-proof API if error construction logic needs to change
58///
59/// ## Examples
60///
61/// ```rust
62/// use fory_core::error::Error;
63///
64/// // ✅ CORRECT: Use static functions
65/// let err = Error::type_error("Expected string type");
66/// let err = Error::invalid_data(format!("Invalid value: {}", 42));
67/// let err = Error::type_mismatch(1, 2);
68///
69/// // ❌ WRONG: Do not construct directly
70/// // let err = Error::TypeError("Expected string type".into());
71/// // let err = Error::InvalidData(format!("Invalid value: {}", 42).into());
72/// ```
73///
74/// ## Available Constructor Functions
75///
76/// - [`Error::type_mismatch`] - For type ID mismatches
77/// - [`Error::buffer_out_of_bound`] - For buffer boundary violations
78/// - [`Error::encode_error`] - For encoding failures
79/// - [`Error::invalid_data`] - For invalid or corrupted data
80/// - [`Error::invalid_ref`] - For invalid reference IDs
81/// - [`Error::unknown_enum`] - For unknown enum variants
82/// - [`Error::type_error`] - For general type errors
83/// - [`Error::encoding_error`] - For encoding format errors
84/// - [`Error::depth_exceed`] - For exceeding maximum nesting depth
85/// - [`Error::unsupported`] - For unsupported operations
86/// - [`Error::not_allowed`] - For disallowed operations
87/// - [`Error::unknown`] - For generic errors
88///
89/// ## Debug Mode: FORY_PANIC_ON_ERROR
90///
91/// For easier debugging, you can set the `FORY_PANIC_ON_ERROR` environment variable to make
92/// the program panic at the exact location where an error is created. This helps identify
93/// the error source with a full stack trace.
94///
95/// ```bash
96/// RUST_BACKTRACE=1 FORY_PANIC_ON_ERROR=1 cargo run
97/// # or
98/// RUST_BACKTRACE=1 FORY_PANIC_ON_ERROR=true cargo test
99/// ```
100///
101/// When enabled, any error created via the static constructor functions will panic immediately
102/// with the error message, allowing you to see the exact call stack in your debugger or
103/// panic output. Use `RUST_BACKTRACE=1` together with `FORY_PANIC_ON_ERROR` to get a full
104/// stack trace showing exactly where the error was created.
105#[derive(Error, Debug)]
106#[non_exhaustive]
107pub enum Error {
108    /// Type mismatch between local and remote type IDs.
109    ///
110    /// Do not construct this variant directly; use [`Error::type_mismatch`] instead.
111    #[error("Type mismatch: type_a = {0}, type_b = {1}")]
112    TypeMismatch(u32, u32),
113
114    /// Buffer boundary violation during read/write operations.
115    ///
116    /// Do not construct this variant directly; use [`Error::buffer_out_of_bound`] instead.
117    #[error("Buffer out of bound: {0} + {1} > {2}")]
118    BufferOutOfBound(usize, usize, usize),
119
120    /// Error during data encoding.
121    ///
122    /// Do not construct this variant directly; use [`Error::encode_error`] instead.
123    #[error("{0}")]
124    EncodeError(Cow<'static, str>),
125
126    /// Invalid or corrupted data encountered.
127    ///
128    /// Do not construct this variant directly; use [`Error::invalid_data`] instead.
129    #[error("{0}")]
130    InvalidData(Cow<'static, str>),
131
132    /// Invalid reference ID encountered.
133    ///
134    /// Do not construct this variant directly; use [`Error::invalid_ref`] instead.
135    #[error("{0}")]
136    InvalidRef(Cow<'static, str>),
137
138    /// Unknown enum variant encountered.
139    ///
140    /// Do not construct this variant directly; use [`Error::unknown_enum`] instead.
141    #[error("{0}")]
142    UnknownEnum(Cow<'static, str>),
143
144    /// General type-related error.
145    ///
146    /// Do not construct this variant directly; use [`Error::type_error`] instead.
147    #[error("{0}")]
148    TypeError(Cow<'static, str>),
149
150    /// Error in encoding format or conversion.
151    ///
152    /// Do not construct this variant directly; use [`Error::encoding_error`] instead.
153    #[error("{0}")]
154    EncodingError(Cow<'static, str>),
155
156    /// Maximum nesting depth exceeded.
157    ///
158    /// Do not construct this variant directly; use [`Error::depth_exceed`] instead.
159    #[error("{0}")]
160    DepthExceed(Cow<'static, str>),
161
162    /// Unsupported operation or feature.
163    ///
164    /// Do not construct this variant directly; use [`Error::unsupported`] instead.
165    #[error("{0}")]
166    Unsupported(Cow<'static, str>),
167
168    /// Operation not allowed in current context.
169    ///
170    /// Do not construct this variant directly; use [`Error::not_allowed`] instead.
171    #[error("{0}")]
172    NotAllowed(Cow<'static, str>),
173
174    /// Generic unknown error.
175    ///
176    /// Do not construct this variant directly; use [`Error::unknown`] instead.
177    #[error("{0}")]
178    Unknown(Cow<'static, str>),
179
180    /// Struct version mismatch between local and remote schemas.
181    ///
182    /// Do not construct this variant directly; use [`Error::struct_version_mismatch`] instead.
183    #[error("{0}")]
184    StructVersionMismatch(Cow<'static, str>),
185}
186
187impl Error {
188    /// Creates a new [`Error::TypeMismatch`] with the given type IDs.
189    ///
190    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
191    ///
192    /// # Example
193    /// ```
194    /// use fory_core::error::Error;
195    ///
196    /// let err = Error::type_mismatch(1, 2);
197    /// ```
198    #[inline(always)]
199    #[cold]
200    #[track_caller]
201    pub fn type_mismatch(type_a: u32, type_b: u32) -> Self {
202        let err = Error::TypeMismatch(type_a, type_b);
203        if PANIC_ON_ERROR {
204            panic!("FORY_PANIC_ON_ERROR: {}", err);
205        }
206        err
207    }
208
209    /// Creates a new [`Error::BufferOutOfBound`] with the given bounds.
210    ///
211    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
212    ///
213    /// # Example
214    /// ```
215    /// use fory_core::error::Error;
216    ///
217    /// let err = Error::buffer_out_of_bound(10, 20, 25);
218    /// ```
219    #[inline(always)]
220    #[cold]
221    #[track_caller]
222    pub fn buffer_out_of_bound(offset: usize, length: usize, capacity: usize) -> Self {
223        let err = Error::BufferOutOfBound(offset, length, capacity);
224        if PANIC_ON_ERROR {
225            panic!("FORY_PANIC_ON_ERROR: {}", err);
226        }
227        err
228    }
229
230    /// Creates a new [`Error::EncodeError`] from a string or static message.
231    ///
232    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
233    ///
234    /// # Example
235    /// ```
236    /// use fory_core::error::Error;
237    ///
238    /// let err = Error::encode_error("Failed to encode");
239    /// let err = Error::encode_error(format!("Failed to encode field {}", "name"));
240    /// ```
241    #[inline(always)]
242    #[cold]
243    #[track_caller]
244    pub fn encode_error<S: Into<Cow<'static, str>>>(s: S) -> Self {
245        let err = Error::EncodeError(s.into());
246        if PANIC_ON_ERROR {
247            panic!("FORY_PANIC_ON_ERROR: {}", err);
248        }
249        err
250    }
251
252    /// Creates a new [`Error::InvalidData`] from a string or static message.
253    ///
254    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
255    ///
256    /// # Example
257    /// ```
258    /// use fory_core::error::Error;
259    ///
260    /// let err = Error::invalid_data("Invalid data format");
261    /// let err = Error::invalid_data(format!("Invalid data at position {}", 42));
262    /// ```
263    #[inline(always)]
264    #[cold]
265    #[track_caller]
266    pub fn invalid_data<S: Into<Cow<'static, str>>>(s: S) -> Self {
267        let err = Error::InvalidData(s.into());
268        if PANIC_ON_ERROR {
269            panic!("FORY_PANIC_ON_ERROR: {}", err);
270        }
271        err
272    }
273
274    /// Creates a new [`Error::InvalidRef`] from a string or static message.
275    ///
276    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
277    ///
278    /// # Example
279    /// ```
280    /// use fory_core::error::Error;
281    ///
282    /// let err = Error::invalid_ref("Invalid reference");
283    /// let err = Error::invalid_ref(format!("Invalid ref id {}", 123));
284    /// ```
285    #[inline(always)]
286    #[cold]
287    #[track_caller]
288    pub fn invalid_ref<S: Into<Cow<'static, str>>>(s: S) -> Self {
289        let err = Error::InvalidRef(s.into());
290        if PANIC_ON_ERROR {
291            panic!("FORY_PANIC_ON_ERROR: {}", err);
292        }
293        err
294    }
295
296    /// Creates a new [`Error::UnknownEnum`] from a string or static message.
297    ///
298    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
299    ///
300    /// # Example
301    /// ```
302    /// use fory_core::error::Error;
303    ///
304    /// let err = Error::unknown_enum("Unknown enum variant");
305    /// let err = Error::unknown_enum(format!("Unknown variant {}", 5));
306    /// ```
307    #[inline(always)]
308    #[cold]
309    #[track_caller]
310    pub fn unknown_enum<S: Into<Cow<'static, str>>>(s: S) -> Self {
311        let err = Error::UnknownEnum(s.into());
312        if PANIC_ON_ERROR {
313            panic!("FORY_PANIC_ON_ERROR: {}", err);
314        }
315        err
316    }
317
318    /// Creates a new [`Error::TypeError`] from a string or static message.
319    ///
320    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
321    ///
322    /// # Example
323    /// ```
324    /// use fory_core::error::Error;
325    ///
326    /// let err = Error::type_error("Type error");
327    /// let err = Error::type_error(format!("Expected type {}", "String"));
328    /// ```
329    #[inline(always)]
330    #[cold]
331    #[track_caller]
332    pub fn type_error<S: Into<Cow<'static, str>>>(s: S) -> Self {
333        let err = Error::TypeError(s.into());
334        if PANIC_ON_ERROR {
335            panic!("FORY_PANIC_ON_ERROR: {}", err);
336        }
337        err
338    }
339
340    /// Creates a new [`Error::EncodingError`] from a string or static message.
341    ///
342    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
343    ///
344    /// # Example
345    /// ```
346    /// use fory_core::error::Error;
347    ///
348    /// let err = Error::encoding_error("Encoding failed");
349    /// let err = Error::encoding_error(format!("Failed to encode as {}", "UTF-8"));
350    /// ```
351    #[inline(always)]
352    #[cold]
353    #[track_caller]
354    pub fn encoding_error<S: Into<Cow<'static, str>>>(s: S) -> Self {
355        let err = Error::EncodingError(s.into());
356        if PANIC_ON_ERROR {
357            panic!("FORY_PANIC_ON_ERROR: {}", err);
358        }
359        err
360    }
361
362    /// Creates a new [`Error::DepthExceed`] from a string or static message.
363    ///
364    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
365    ///
366    /// # Example
367    /// ```
368    /// use fory_core::error::Error;
369    ///
370    /// let err = Error::depth_exceed("Max depth exceeded");
371    /// let err = Error::depth_exceed(format!("Depth {} exceeds max {}", 100, 64));
372    /// ```
373    #[inline(always)]
374    #[cold]
375    #[track_caller]
376    pub fn depth_exceed<S: Into<Cow<'static, str>>>(s: S) -> Self {
377        let err = Error::DepthExceed(s.into());
378        if PANIC_ON_ERROR {
379            panic!("FORY_PANIC_ON_ERROR: {}", err);
380        }
381        err
382    }
383
384    /// Creates a new [`Error::Unsupported`] from a string or static message.
385    ///
386    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
387    ///
388    /// # Example
389    /// ```
390    /// use fory_core::error::Error;
391    ///
392    /// let err = Error::unsupported("Unsupported operation");
393    /// let err = Error::unsupported(format!("Type {} not supported", "MyType"));
394    /// ```
395    #[inline(always)]
396    #[cold]
397    #[track_caller]
398    pub fn unsupported<S: Into<Cow<'static, str>>>(s: S) -> Self {
399        let err = Error::Unsupported(s.into());
400        if PANIC_ON_ERROR {
401            panic!("FORY_PANIC_ON_ERROR: {}", err);
402        }
403        err
404    }
405
406    /// Creates a new [`Error::NotAllowed`] from a string or static message.
407    ///
408    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
409    ///
410    /// # Example
411    /// ```
412    /// use fory_core::error::Error;
413    ///
414    /// let err = Error::not_allowed("Operation not allowed");
415    /// let err = Error::not_allowed(format!("Cannot perform {}", "delete"));
416    /// ```
417    #[inline(always)]
418    #[cold]
419    #[track_caller]
420    pub fn not_allowed<S: Into<Cow<'static, str>>>(s: S) -> Self {
421        let err = Error::NotAllowed(s.into());
422        if PANIC_ON_ERROR {
423            panic!("FORY_PANIC_ON_ERROR: {}", err);
424        }
425        err
426    }
427
428    /// Creates a new [`Error::StructVersionMismatch`] from a string or static message.
429    ///
430    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
431    ///
432    /// # Example
433    /// ```
434    /// use fory_core::error::Error;
435    ///
436    /// let err = Error::struct_version_mismatch("Version mismatch");
437    /// let err = Error::struct_version_mismatch(format!("Class {} version mismatch", "Foo"));
438    /// ```
439    #[inline(always)]
440    #[cold]
441    #[track_caller]
442    pub fn struct_version_mismatch<S: Into<Cow<'static, str>>>(s: S) -> Self {
443        let err = Error::StructVersionMismatch(s.into());
444        if PANIC_ON_ERROR {
445            panic!("FORY_PANIC_ON_ERROR: {}", err);
446        }
447        err
448    }
449
450    /// Creates a new [`Error::Unknown`] from a string or static message.
451    ///
452    /// This function is a convenient way to produce an error message
453    /// from a literal, `String`, or any type that can be converted into
454    /// a [`Cow<'static, str>`].
455    ///
456    /// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic with the error message.
457    ///
458    /// # Example
459    /// ```
460    /// use fory_core::error::Error;
461    ///
462    /// let err = Error::unknown("Something went wrong");
463    /// let err = Error::unknown(format!("ID:{} not found", 1));
464    /// ```
465    #[inline(always)]
466    #[cold]
467    #[track_caller]
468    pub fn unknown<S: Into<Cow<'static, str>>>(s: S) -> Self {
469        let err = Error::Unknown(s.into());
470        if PANIC_ON_ERROR {
471            panic!("FORY_PANIC_ON_ERROR: {}", err);
472        }
473        err
474    }
475
476    /// Enhances a [`Error::TypeError`] with additional type name information.
477    ///
478    /// If the error is a `TypeError`, appends the type name to the message.
479    /// Otherwise, returns the error unchanged.
480    ///
481    /// # Example
482    /// ```
483    /// use fory_core::error::Error;
484    ///
485    /// let err = Error::type_error("Type not registered");
486    /// let enhanced = Error::enhance_type_error::<String>(err);
487    /// // Result: "Type not registered (type: alloc::string::String)"
488    /// ```
489    #[inline(never)]
490    pub fn enhance_type_error<T: ?Sized + 'static>(err: Error) -> Error {
491        if let Error::TypeError(s) = err {
492            let mut msg = s.to_string();
493            msg.push_str(" (type: ");
494            msg.push_str(std::any::type_name::<T>());
495            msg.push(')');
496            Error::type_error(msg)
497        } else {
498            err
499        }
500    }
501}
502
503/// Ensures a condition is true; otherwise returns an [`enum@Error`].
504///
505/// # Examples
506/// ```
507/// use fory_core::ensure;
508/// use fory_core::error::Error;
509///
510/// fn check_value(n: i32) -> Result<(), Error> {
511///     ensure!(n > 0, "value must be positive");
512///     ensure!(n < 10, "value {} too large", n);
513///     Ok(())
514/// }
515/// ```
516#[macro_export]
517macro_rules! ensure {
518    ($cond:expr, $msg:literal) => {
519        if !$cond {
520            return Err($crate::error::Error::unknown($msg));
521        }
522    };
523    ($cond:expr, $err:expr) => {
524        if !$cond {
525            return Err($err);
526        }
527    };
528    ($cond:expr, $fmt:expr, $($arg:tt)*) => {
529        if !$cond {
530            return Err($crate::error::Error::unknown(format!($fmt, $($arg)*)));
531        }
532    };
533}
534
535/// Returns early with an [`enum@Error`].
536///
537/// # Examples
538/// ```
539/// use fory_core::bail;
540/// use fory_core::error::Error;
541///
542/// fn fail_fast() -> Result<(), Error> {
543///     bail!("something went wrong");
544/// }
545/// ```
546#[macro_export]
547macro_rules! bail {
548    ($err:expr) => {
549        return Err($crate::error::Error::unknown($err))
550    };
551    ($fmt:expr, $($arg:tt)*) => {
552        return Err($crate::error::Error::unknown(format!($fmt, $($arg)*)))
553    };
554}
555
556/// Returns early with a [`Error::NotAllowed`].
557///
558/// # Examples
559/// ```
560/// use fory_core::not_allowed;
561/// use fory_core::error::Error;
562///
563/// fn check_operation() -> Result<(), Error> {
564///     not_allowed!("operation not allowed");
565/// }
566///
567/// fn check_operation_with_context(op: &str) -> Result<(), Error> {
568///     not_allowed!("operation {} not allowed", op);
569/// }
570/// ```
571#[macro_export]
572macro_rules! not_allowed {
573    ($err:expr) => {
574        return Err($crate::error::Error::not_allowed($err))
575    };
576    ($fmt:expr, $($arg:tt)*) => {
577        return Err($crate::error::Error::not_allowed(format!($fmt, $($arg)*)))
578    };
579}