mech_core/
error.rs

1use crate::*;
2use std::sync::Arc;
3use std::any::Any;
4
5// Errors
6// ----------------------------------------------------------------------------
7
8// Defines a struct for errors and an enum which enumerates the error types
9
10type Rows = usize;
11type Cols = usize;
12
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct CompilerSourceRange {
16  pub file: &'static str,
17  pub line: u32,
18}
19
20impl CompilerSourceRange {
21  #[track_caller]
22  pub fn here() -> Self {
23    let loc = std::panic::Location::caller();
24    Self {
25      file: loc.file(),
26      line: loc.line(),
27    }
28  }
29}
30
31#[macro_export]
32macro_rules! compiler_loc {
33  () => {
34    $crate::CompilerSourceRange {
35      file: file!(),
36      line: line!(),
37    }
38  };
39}
40
41trait ErrorKindCallbacks: Send + Sync {
42  fn name(&self, data: &dyn Any) -> String;
43  fn message(&self, data: &dyn Any) -> String;
44}
45
46struct CallbacksImpl<K> {
47  // zero-sized; all behavior encoded in trait impl below
48  _marker: std::marker::PhantomData<K>,
49}
50
51impl<K> CallbacksImpl<K> {
52  fn new() -> Self {
53    Self { _marker: std::marker::PhantomData }
54  }
55}
56
57impl<K> ErrorKindCallbacks for CallbacksImpl<K>
58where
59  K: MechErrorKind2 + 'static,
60{
61  fn name(&self, data: &dyn Any) -> String {
62    // downcast and call name()
63    let k = data.downcast_ref::<K>().expect("wrong kind type in vtable");
64    k.name().to_string()
65  }
66
67  fn message(&self, data: &dyn Any) -> String {
68    let k = data.downcast_ref::<K>().expect("wrong kind type in vtable");
69    k.message()
70  }
71}
72
73pub trait MechErrorKind2: std::fmt::Debug + Send + Sync + Clone {
74  fn name(&self) -> &str;
75  fn message(&self) -> String;
76}
77
78//#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
79//#[derive(Clone, Debug, Eq, PartialEq, Hash)]
80#[derive(Clone)]
81pub struct MechError2 {
82  kind_data: Arc<dyn Any + Send + Sync>,
83  kind_callbacks: Arc<dyn ErrorKindCallbacks>, // object-safe vtable
84  pub program_range: Option<SourceRange>,
85  pub annotations: Vec<SourceRange>,
86  pub tokens: Vec<Token>,
87  pub compiler_location: Option<CompilerSourceRange>,
88  pub source: Option<Box<MechError2>>, // for propagation
89  pub message: Option<String>,
90}
91
92impl std::fmt::Debug for MechError2 {
93  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94    f.debug_struct("MechError2")
95      .field("kind name", &self.kind_name())
96      .field("kind message", &self.kind_message())
97      .field("message", &self.message)
98      .field("program_range", &self.program_range)
99      .field("annotations", &self.annotations)
100      .field("tokens", &self.tokens)
101      .field("compiler_location", &self.compiler_location)
102      .field("source", &self.source)
103      .finish()
104  }
105}
106
107impl MechError2 {
108  pub fn new<K: MechErrorKind2 + 'static>(
109    kind: K,
110    message: Option<String>
111  ) -> Self {
112    MechError2 {
113      kind_data: Arc::new(kind),
114      kind_callbacks: Arc::new(CallbacksImpl::<K>::new()),
115      program_range: None,
116      annotations: Vec::new(),
117      tokens: Vec::new(),
118      compiler_location: None,
119      source: None,
120      message,
121    }
122  }
123
124  /// Get the kind as a specific type, if it matches
125  pub fn kind_as<K: MechErrorKind2 + 'static>(&self) -> Option<&K> {
126    self.kind_data.downcast_ref::<K>()
127  }
128
129  /// Get the runtime name (delegates to the underlying kind)
130  pub fn kind_name(&self) -> String {
131    self.kind_callbacks.name(self.kind_data.as_ref())
132  }
133
134  /// Get the runtime message (delegates to the underlying kind)
135  pub fn kind_message(&self) -> String {
136    self.kind_callbacks.message(self.kind_data.as_ref())
137  }
138
139  /// Optional helper that returns the message or the explicit `message` override
140  pub fn display_message(&self) -> String {
141    if let Some(ref m) = self.message { m.clone() } else { self.kind_message() }
142  }
143
144  /// If you ever need downcast access to the concrete kind:
145  pub fn kind_downcast_ref<K: 'static>(&self) -> Option<&K> {
146    self.kind_data.downcast_ref::<K>()
147  }
148
149  /// Add a compiler location annotation with the current location
150  #[track_caller]
151  pub fn with_compiler_loc(mut self) -> Self {
152    self.compiler_location = Some(CompilerSourceRange::here());
153    self
154  }
155
156  /// Specify a particular compiler location
157  pub fn with_specific_compiler_loc(mut self, loc: CompilerSourceRange) -> Self {
158    self.compiler_location = Some(loc);
159    self
160  }
161
162  /// Add a source range annotation
163  pub fn with_annotation(mut self, range: SourceRange) -> Self {
164    self.annotations.push(range);
165    self
166  }
167
168  /// Add multiple source range annotations
169  pub fn with_annotations<I>(mut self, iter: I) -> Self
170  where
171    I: IntoIterator<Item = SourceRange>,
172  {
173    self.annotations.extend(iter);
174    self
175  }
176
177  /// Add tokens related to this error
178  pub fn with_tokens<I>(mut self, iter: I) -> Self
179  where
180    I: IntoIterator<Item = Token>,
181  {
182    self.tokens.extend(iter);
183    self
184  }
185
186  /// Set the source error that caused this one
187  pub fn with_source(mut self, src: MechError2) -> Self {
188    self.source = Some(Box::new(src));
189    self
190  }
191
192  /// Get the primary source range associated with this error
193  pub fn primary_range(&self) -> Option<SourceRange> {
194    self.program_range.clone()
195  }
196
197  /// Get a simple message describing the error
198  pub fn simple_message(&self) -> String {
199    format!("{}: {}", self.kind_name(), self.kind_message())
200  }
201
202  /// Get a full chain message including all source errors
203  pub fn full_chain_message(&self) -> String {
204    let mut out = self.simple_message();
205    let mut current = &self.source;
206
207    while let Some(err) = current {
208      out.push_str("\nCaused by: ");
209      out.push_str(&err.simple_message());
210      current = &err.source;
211    }
212
213    out
214  }
215
216  pub fn boxed(self) -> Box<Self> {
217    Box::new(self)
218  }
219}
220
221#[derive(Debug, Clone)]
222pub struct UndefinedKindError {
223  pub kind_id: u64,
224}
225impl MechErrorKind2 for UndefinedKindError {
226  fn name(&self) -> &str {
227    "UndefinedKind"
228  }
229  fn message(&self) -> String {
230    format!("Kind `{}` is not defined.", self.kind_id)
231  }
232}
233
234impl From<std::io::Error> for MechError2 {
235  fn from(err: std::io::Error) -> Self {
236    MechError2::new(
237      IoErrorWrapper { msg: err.to_string() },
238      None
239    )
240    .with_compiler_loc()
241  }
242}
243
244#[derive(Debug, Clone)]
245pub struct DimensionMismatch {
246  pub dims: Vec<usize>,
247}
248impl MechErrorKind2 for DimensionMismatch {
249  fn name(&self) -> &str { "DimensionMismatch" }
250  fn message(&self) -> String { format!("Matrix dimension mismatch: {:?}", self.dims) }
251}
252
253#[derive(Debug, Clone)]
254pub struct GenericError {
255  pub msg: String,
256}
257impl MechErrorKind2 for GenericError {
258  fn name(&self) -> &str { "GenericError" }
259
260  fn message(&self) -> String {
261    format!("Error: {}", self.msg)
262  }
263}
264
265#[derive(Debug, Clone)]
266pub struct FeatureNotEnabledError;
267impl MechErrorKind2 for FeatureNotEnabledError {
268  fn name(&self) -> &str { "FeatureNotEnabled" }
269
270  fn message(&self) -> String {
271    format!("Feature not enabled")
272  }
273}
274
275#[derive(Debug, Clone)]
276pub struct NotExecutableError {}
277impl MechErrorKind2 for NotExecutableError {
278  fn name(&self) -> &str { "NotExecutable" }
279
280  fn message(&self) -> String {
281    format!("Not executable")
282  }
283}
284
285#[derive(Debug, Clone)]
286pub struct IoErrorWrapper {
287  pub msg: String,
288}
289impl MechErrorKind2 for IoErrorWrapper {
290  fn name(&self) -> &str { "IoError" }
291
292  fn message(&self) -> String {
293    format!("IO error: {}", self.msg)
294  }
295}
296
297/*
298impl fmt::Debug for MechErrorKind {
299  #[inline]
300  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
301    match self {
302      _ => write!(f,"No Format")?;
303    }
304    Ok(())
305  }
306}*/