use std::{cmp, fmt}; use std::fmt::{Formatter, Debug}; /// The representation of an s-expression parse error. pub struct Error { /// The error message. pub message: &'static str, /// Position in the source string where the error was detected pub pos: SourcePosition, } /// Position in the input string #[derive(PartialEq, Clone, Default)] pub struct SourcePosition { /// The line number on which the error occurred. pub line: u32, /// The column number on which the error occurred. pub column: u32, /// The index in the given string which caused the error. pub index: u32, } impl fmt::Display for SourcePosition { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{}:{}", self.line, self.column) } } impl Debug for SourcePosition { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if f.alternate() { f.debug_struct("SourcePosition") .field("line", &self.line) .field("column", &self.column) .field("index", &self.index) .finish() } else { // shorter version write!(f, "Pos({}:{})", self.line, self.column) } } } /// Since errors are the uncommon case, they're boxed. This keeps the size of /// structs down, which helps performance in the common case. /// /// For example, an `ERes<()>` becomes 8 bytes, instead of the 24 bytes it would /// be if `Err` were unboxed. type Err = Box; /// Helps clean up type signatures, but shouldn't be exposed to the outside /// world. pub(crate) type ERes = Result; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{}:{}: {}", self.pos.line, self.pos.column, self.message) } } impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{}", self) } } impl std::error::Error for Error {} pub(crate) fn get_line_and_column(s: &str, pos: usize) -> SourcePosition { let mut line: usize = 1; let mut col: isize = -1; for c in s.chars().take(pos + 1) { if c == '\n' { line += 1; col = -1; } else { col += 1; } } SourcePosition { line: line as u32, column: cmp::max(col, 0) as u32, index: pos as u32, } } #[cold] fn err_impl(message: &'static str, s: &str, pos: usize) -> Err { Box::new(Error { message, pos: get_line_and_column(s, pos), }) } /// Build an error with span information pub(crate) fn err(message: &'static str, s: &str, pos: usize) -> ERes { Err(err_impl(message, s, pos)) } /// Build a span pub(crate) fn spos(s: &str, pos: usize) -> SourcePosition { if pos >= s.len() { Default::default() } else { get_line_and_column(s, pos) } }