geop_core_math/
geop_error.rs1use std::backtrace::Backtrace;
2
3pub trait DebugContext: Send + Sync + core::fmt::Debug {
6 fn label(&self) -> &str;
8}
9
10pub enum GeopError {
11 Root {
12 message: String,
13 backtrace: Backtrace,
14 },
15 Context {
16 inner: Box<GeopError>,
17 context: Option<Box<dyn DebugContext>>,
18 },
19}
20
21impl GeopError {
22 pub fn new(message: impl Into<String>) -> Self {
24 let backtrace = Backtrace::capture();
25 GeopError::Root {
26 message: message.into(),
27 backtrace,
28 }
29 }
30
31 pub fn with_context(self, message: impl Into<String>) -> Self {
33 let label = message.into();
34 GeopError::Context {
35 inner: Box::new(self),
36 context: Some(Box::new(StringContext(label))),
37 }
38 }
39
40 pub fn with_scene(self, scene: impl DebugContext + 'static) -> Self {
42 GeopError::Context {
43 inner: Box::new(self),
44 context: Some(Box::new(scene)),
45 }
46 }
47
48 pub fn scene_labels(&self) -> Vec<&str> {
50 match self {
51 GeopError::Root { .. } => vec![],
52 GeopError::Context { inner, context } => {
53 let mut labels = inner.scene_labels();
54 if let Some(ctx) = context {
55 labels.push(ctx.label());
56 }
57 labels
58 }
59 }
60 }
61}
62
63impl std::fmt::Display for GeopError {
64 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
65 match self {
66 GeopError::Root { message, backtrace } => {
67 writeln!(f, "GeopError")?;
68 writeln!(f, "Backtrace: {}", backtrace)?;
69 writeln!(f, "RootError: {}", message)
70 }
71 GeopError::Context { inner, context } => {
72 write!(f, "{}", inner)?;
73 match context {
74 Some(ctx) => writeln!(f, "Context:\n{}", ctx.label()),
75 None => writeln!(f, "Context: (no details)"),
76 }
77 }
78 }
79 }
80}
81
82impl std::fmt::Debug for GeopError {
83 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
84 write!(f, "{}", self)
85 }
86}
87
88impl std::error::Error for GeopError {}
89
90impl From<&str> for GeopError {
91 fn from(message: &str) -> Self {
92 GeopError::new(message)
93 }
94}
95
96impl From<String> for GeopError {
97 fn from(message: String) -> Self {
98 GeopError::new(message)
99 }
100}
101
102pub type GeopResult<T> = Result<T, GeopError>;
103
104pub trait ContextSource {
110 fn apply(&self, err: GeopError) -> GeopError;
111}
112
113impl ContextSource for str {
114 fn apply(&self, err: GeopError) -> GeopError {
115 err.with_context(self)
116 }
117}
118
119impl ContextSource for String {
120 fn apply(&self, err: GeopError) -> GeopError {
121 err.with_context(self.as_str())
122 }
123}
124
125impl<F: Fn(GeopError) -> GeopError + ?Sized> ContextSource for F {
126 fn apply(&self, err: GeopError) -> GeopError {
127 self(err)
128 }
129}
130
131pub trait WithContext<T> {
132 fn with_context(self, ctx: &(impl ContextSource + ?Sized)) -> GeopResult<T>;
133}
134
135impl<T> WithContext<T> for GeopResult<T> {
136 fn with_context(self, ctx: &(impl ContextSource + ?Sized)) -> GeopResult<T> {
137 match self {
138 Ok(v) => Ok(v),
139 Err(err) => Err(ctx.apply(err)),
140 }
141 }
142}
143
144#[macro_export]
150macro_rules! with_context {
151 ($($arg:tt)*) => {
152 &|e: $crate::geop_error::GeopError| e.with_context(format!($($arg)*))
153 };
154}
155
156#[derive(Debug)]
159struct StringContext(String);
160
161impl DebugContext for StringContext {
162 fn label(&self) -> &str {
163 &self.0
164 }
165}