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 Uunsupported(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::Uunsupported`] 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::Uunsupported(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}