error2/root_error.rs
1use crate::{Error2, Location, transform::SourceToTarget};
2
3/// Convenience methods for creating root errors.
4///
5/// `RootError` provides `.build()` and `.fail()` for creating errors that
6/// represent new error origins (not wrapping other errors).
7///
8/// # Root Error Pattern
9///
10/// A root error only has a `backtrace` field (no `source`):
11///
12/// ```
13/// use error2::prelude::*;
14///
15/// #[derive(Debug, Error2)]
16/// pub enum AppError {
17/// #[error2(display("invalid ID: {id}"))]
18/// InvalidId { id: i64, backtrace: Backtrace },
19///
20/// #[error2(display("channel closed"))]
21/// ChannelClosed { backtrace: Backtrace },
22/// }
23/// ```
24///
25/// # Using .build()
26///
27/// Create an error instance:
28///
29/// ```
30/// # use error2::prelude::*;
31/// # #[derive(Debug, Error2)]
32/// # pub enum AppError {
33/// # #[error2(display("invalid ID: {id}"))]
34/// # InvalidId { id: i64, backtrace: Backtrace },
35/// # }
36/// # fn validate(id: i64) -> Result<(), AppError> {
37/// if id < 0 {
38/// let error = InvalidId2 { id }.build();
39/// // Use error...
40/// }
41/// # Ok(())
42/// # }
43/// ```
44///
45/// # Using .fail()
46///
47/// Create and return an error in one step:
48///
49/// ```
50/// # use error2::prelude::*;
51/// # #[derive(Debug, Error2)]
52/// # pub enum AppError {
53/// # #[error2(display("invalid ID: {id}"))]
54/// # InvalidId { id: i64, backtrace: Backtrace },
55/// # }
56/// fn validate(id: i64) -> Result<(), AppError> {
57/// if id < 0 {
58/// return InvalidId2 { id }.fail();
59/// }
60/// Ok(())
61/// }
62/// ```
63///
64/// # Automatic Location Tracking
65///
66/// Both `.build()` and `.fail()` automatically capture the caller's
67/// location using `#[track_caller]`, which is included in the backtrace.
68///
69/// ```
70/// # use error2::prelude::*;
71/// # #[derive(Debug, Error2)]
72/// # pub enum AppError {
73/// # #[error2(display("invalid ID: {id}"))]
74/// # InvalidId { id: i64, backtrace: Backtrace },
75/// # }
76/// use regex::Regex;
77///
78/// let err = InvalidId2 { id: -1 }.build();
79/// let msg = err.backtrace().error_message();
80///
81/// // Full error format with location tracking:
82/// // AppError: invalid ID: -1
83/// // at /path/to/file.rs:683:33
84///
85/// let re = Regex::new(concat!(
86/// r"(?s)^.+AppError: invalid ID: -1",
87/// r"\n at .+\.rs:\d+:\d+$",
88/// ))
89/// .unwrap();
90/// assert!(re.is_match(msg.as_ref()));
91/// ```
92pub trait RootError<M, Target: Error2>: SourceToTarget<M, (), (), Target> + Sized {
93 /// Creates a root error instance.
94 ///
95 /// Automatically captures the caller's location.
96 #[inline]
97 #[must_use]
98 #[track_caller]
99 fn build(self) -> Target {
100 self.build_with_location(Location::caller())
101 }
102
103 /// Creates a root error with explicit location.
104 #[inline]
105 #[must_use]
106 fn build_with_location(self, location: Location) -> Target {
107 <Self as SourceToTarget<M, (), (), Target>>::source_to_target(self, (), location)
108 }
109
110 /// Creates and returns a root error as `Err(...)`.
111 ///
112 /// Convenient for returning errors directly.
113 #[inline]
114 #[track_caller]
115 fn fail<T>(self) -> Result<T, Target> {
116 Err(self.build())
117 }
118
119 /// Creates and returns error with explicit location.
120 #[inline]
121 fn fail_with_location<T>(self, location: Location) -> Result<T, Target> {
122 Err(self.build_with_location(location))
123 }
124}
125
126impl<M, Target, C> RootError<M, Target> for C
127where
128 Target: Error2,
129 C: SourceToTarget<M, (), (), Target>,
130{
131}