1#![allow(missing_docs, dead_code)] use crate::compat::{boxed::Box, error::Error as ErrorTrait};
4#[cfg(feature = "std")]
5use crate::error::inner::Location;
6use serde::{Deserialize, Serialize};
7
8use self::code::ErrorCode;
9
10mod code;
11mod inner;
12
13pub mod errcode {
15 pub use super::code::*;
16}
17
18#[cfg(feature = "alloc")]
21type ErrorData = Box<inner::ErrorData>;
22#[cfg(not(feature = "alloc"))]
25type ErrorData = Inner;
26
27pub type Result<T, E = Error> = core::result::Result<T, E>;
28
29#[derive(Serialize, Deserialize)]
40pub struct Error(ErrorData);
41
42impl Error {
43 #[cold]
45 #[track_caller]
46 #[cfg(feature = "std")]
47 pub fn new<E>(origin: code::Origin, kind: code::Kind, cause: E) -> Self
48 where
49 E: Into<Box<dyn std::error::Error + Send + Sync>>,
50 {
51 Self(inner::ErrorData::new(ErrorCode::new(origin, kind), cause).into())
52 }
53
54 #[cold]
56 #[track_caller]
57 #[cfg(not(feature = "std"))]
58 pub fn new<E>(origin: code::Origin, kind: code::Kind, cause: E) -> Self
59 where
60 E: core::fmt::Display,
61 {
62 Self(inner::ErrorData::new(ErrorCode::new(origin, kind), cause).into())
63 }
64
65 #[cold]
69 #[cfg(feature = "std")]
70 #[track_caller]
71 pub fn new_unknown<E>(origin: code::Origin, cause: E) -> Self
72 where
73 E: Into<Box<dyn crate::compat::error::Error + Send + Sync>>,
74 {
75 Self::new(origin, code::Kind::Unknown, cause)
76 }
77
78 #[cold]
83 #[track_caller]
84 pub fn new_without_cause(origin: code::Origin, kind: code::Kind) -> Self {
85 Self(inner::ErrorData::new_without_cause(origin, kind).into())
86 }
87
88 pub fn code(&self) -> ErrorCode {
90 self.0.code
91 }
92
93 #[cfg(feature = "std")]
95 pub(super) fn source_location(&self) -> Location {
96 self.0.source_loc.clone()
97 }
98
99 #[must_use]
101 pub fn context(mut self, key: &str, val: impl core::fmt::Display) -> Self {
102 self.0.add_context(key, &val);
103 self
104 }
105}
106
107impl core::fmt::Debug for Error {
108 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
109 self.0.fmt(f)
110 }
111}
112
113impl core::fmt::Display for Error {
114 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115 #[cfg(feature = "std")]
116 match self.source() {
117 None => write!(
118 f,
119 "{}, source location: {}",
120 self.code(),
121 self.source_location()
122 )?,
123 Some(e) => write!(
124 f,
125 "{} ({}, source location: {})",
126 e,
127 self.code(),
128 self.source_location()
129 )?,
130 }
131 #[cfg(not(feature = "std"))]
132 write!(f, "{}", self.code())?;
133 Ok(())
134 }
135}
136
137impl ErrorTrait for Error {
138 #[cfg(feature = "std")]
139 fn source(&self) -> Option<&(dyn ErrorTrait + 'static)> {
140 if let Some(e) = self.0.cause() {
141 let force_coercion: &(dyn ErrorTrait + 'static) = e;
142 Some(force_coercion)
143 } else {
144 None
145 }
146 }
147}
148
149#[cfg(feature = "std")]
150impl miette::Diagnostic for Error {}
151
152impl From<core::fmt::Error> for Error {
153 #[cfg(feature = "std")]
154 #[track_caller]
155 fn from(e: core::fmt::Error) -> Self {
156 Error::new(code::Origin::Application, code::Kind::Invalid, e)
157 }
158 #[cfg(not(feature = "std"))]
159 #[track_caller]
160 fn from(_: core::fmt::Error) -> Self {
161 Error::new_without_cause(code::Origin::Application, code::Kind::Invalid)
162 }
163}
164
165impl From<strum::ParseError> for Error {
166 #[cfg(feature = "std")]
167 #[track_caller]
168 fn from(e: strum::ParseError) -> Self {
169 Error::new(code::Origin::Application, code::Kind::Invalid, e)
170 }
171 #[cfg(not(feature = "std"))]
172 #[track_caller]
173 fn from(_: strum::ParseError) -> Self {
174 Error::new_without_cause(code::Origin::Application, code::Kind::Invalid)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use crate::errcode::{Kind, Origin};
182
183 #[test]
184 fn test_error_display() {
185 let e = Error::new(Origin::Node, Kind::NotFound, "address not found");
186 assert!(e.to_string().contains("address not found (origin: Node, kind: NotFound, source location: implementations/rust/ockam/ockam_core/src/error/mod.rs"))
187 }
188
189 #[test]
190 fn test_error_without_cause_display() {
191 let e = Error::new_without_cause(Origin::Node, Kind::NotFound);
192 assert!(e.to_string().contains("origin: Node, kind: NotFound, source location: implementations/rust/ockam/ockam_core/src/error/mod.rs"))
193 }
194}