geop_core_math/
geop_error.rs1use std::{backtrace::Backtrace, sync::Arc};
2
3#[derive(Clone)]
8pub enum GeopError {
9 Root {
10 message: String,
11 backtrace: Arc<Backtrace>,
12 },
13 Context {
16 inner: Box<GeopError>,
17 message: String,
18 },
19 Argument { inner: Box<GeopError>, key: String },
23}
24
25impl GeopError {
26 pub fn new(message: impl Into<String>) -> Self {
28 let backtrace = Arc::new(Backtrace::capture());
29 GeopError::Root {
30 message: message.into(),
31 backtrace,
32 }
33 }
34
35 pub fn with_context(self, message: impl Into<String>) -> Self {
37 GeopError::Context {
38 inner: Box::new(self),
39 message: message.into(),
40 }
41 }
42
43 pub fn concerning(self, key: impl Into<String>) -> Self {
47 GeopError::Argument {
48 inner: Box::new(self),
49 key: key.into(),
50 }
51 }
52
53 pub fn argument(&self) -> Option<&str> {
57 match self {
58 GeopError::Root { .. } => None,
59 GeopError::Context { inner, .. } => inner.argument(),
60 GeopError::Argument { key, .. } => Some(key),
61 }
62 }
63
64 pub fn root_message(&self) -> &str {
67 match self {
68 GeopError::Root { message, .. } => message,
69 GeopError::Context { inner, .. } | GeopError::Argument { inner, .. } => {
70 inner.root_message()
71 }
72 }
73 }
74}
75
76impl std::fmt::Display for GeopError {
77 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
78 match self {
79 GeopError::Root { message, backtrace } => {
80 writeln!(f, "GeopError")?;
81 writeln!(f, "Backtrace: {}", backtrace)?;
82 writeln!(f, "RootError: {}", message)
83 }
84 GeopError::Context { inner, message } => {
85 write!(f, "{}", inner)?;
86 writeln!(f, "Context:\n{}", message)
87 }
88 GeopError::Argument { inner, key } => {
89 write!(f, "{}", inner)?;
90 writeln!(f, "Argument: {}", key)
91 }
92 }
93 }
94}
95
96impl PartialEq for GeopError {
99 fn eq(&self, other: &Self) -> bool {
100 match (self, other) {
101 (GeopError::Root { message: a, .. }, GeopError::Root { message: b, .. }) => a == b,
102 (
103 GeopError::Context {
104 inner: a,
105 message: m,
106 },
107 GeopError::Context {
108 inner: b,
109 message: n,
110 },
111 ) => m == n && a == b,
112 _ => false,
113 }
114 }
115}
116
117impl std::fmt::Debug for GeopError {
118 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
119 write!(f, "{}", self)
120 }
121}
122
123impl std::error::Error for GeopError {}
124
125impl From<&str> for GeopError {
126 fn from(message: &str) -> Self {
127 GeopError::new(message)
128 }
129}
130
131impl From<String> for GeopError {
132 fn from(message: String) -> Self {
133 GeopError::new(message)
134 }
135}
136
137pub type GeopResult<T> = Result<T, GeopError>;
138
139pub trait ContextSource {
145 fn apply(&self, err: GeopError) -> GeopError;
146}
147
148impl ContextSource for str {
149 fn apply(&self, err: GeopError) -> GeopError {
150 err.with_context(self)
151 }
152}
153
154impl ContextSource for String {
155 fn apply(&self, err: GeopError) -> GeopError {
156 err.with_context(self.as_str())
157 }
158}
159
160impl<F: Fn(GeopError) -> GeopError + ?Sized> ContextSource for F {
161 fn apply(&self, err: GeopError) -> GeopError {
162 self(err)
163 }
164}
165
166pub trait WithContext<T> {
167 fn with_context(self, ctx: &(impl ContextSource + ?Sized)) -> GeopResult<T>;
168
169 fn concerning(self, key: &str) -> GeopResult<T>;
172}
173
174impl<T> WithContext<T> for GeopResult<T> {
175 fn with_context(self, ctx: &(impl ContextSource + ?Sized)) -> GeopResult<T> {
176 match self {
177 Ok(v) => Ok(v),
178 Err(err) => Err(ctx.apply(err)),
179 }
180 }
181
182 fn concerning(self, key: &str) -> GeopResult<T> {
183 self.map_err(|e| e.concerning(key))
184 }
185}
186
187#[macro_export]
193macro_rules! with_context {
194 ($($arg:tt)*) => {
195 &|e: $crate::geop_error::GeopError| e.with_context(format!($($arg)*))
196 };
197}