logicaffeine_base/error.rs
1//! Error types with source location tracking.
2//!
3//! All errors in logicaffeine carry a [`Span`] indicating where in the source
4//! text the error occurred. This enables precise, contextual error messages.
5//!
6//! # Example
7//!
8//! ```
9//! use logicaffeine_base::{SpannedError, Span, Result};
10//!
11//! fn parse_number(s: &str) -> Result<i32> {
12//! s.parse().map_err(|_| SpannedError::new(
13//! format!("invalid number: '{}'", s),
14//! Span::new(0, s.len()),
15//! ))
16//! }
17//!
18//! let err = parse_number("abc").unwrap_err();
19//! assert!(err.to_string().contains("invalid number"));
20//! ```
21
22use crate::span::Span;
23use std::fmt;
24
25/// An error annotated with its source location.
26///
27/// Implements [`std::error::Error`] and [`fmt::Display`]. The display format is:
28/// `{message} at {start}..{end}`.
29#[derive(Debug, Clone)]
30pub struct SpannedError {
31 /// Human-readable error description.
32 pub message: String,
33 /// Location in source where the error occurred.
34 pub span: Span,
35}
36
37impl SpannedError {
38 /// Creates an error with the given message and source location.
39 pub fn new(message: impl Into<String>, span: Span) -> Self {
40 Self {
41 message: message.into(),
42 span,
43 }
44 }
45}
46
47impl fmt::Display for SpannedError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 write!(f, "{} at {}..{}", self.message, self.span.start, self.span.end)
50 }
51}
52
53impl std::error::Error for SpannedError {}
54
55/// Alias for `std::result::Result<T, SpannedError>`.
56///
57/// Use this as the return type for fallible operations in logicaffeine.
58pub type Result<T> = std::result::Result<T, SpannedError>;
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn spanned_error_display() {
66 let err = SpannedError::new("test error", Span::new(5, 10));
67 let display = format!("{}", err);
68 assert!(display.contains("test error"));
69 assert!(display.contains("5..10"));
70 }
71}