Skip to main content

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}