Skip to main content

pencil_puzzles_core/
error.rs

1//! The error type for reading puzzles, answers and parameters.
2
3use std::error::Error;
4use std::fmt;
5
6/// Where in the input a [`ParseError`] was found.
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
8#[non_exhaustive]
9pub enum Position {
10    /// No particular place.
11    None,
12    /// A line of text, counted from 1.
13    Line(usize),
14    /// A character of a one-line input such as a puzzle ID, counted from 1.
15    Char(usize),
16}
17
18/// An error from reading text, a puzzle ID or JSON, saying what is wrong and
19/// where (G7).
20#[derive(Clone, Debug, PartialEq, Eq, Hash)]
21pub struct ParseError {
22    position: Position,
23    message: String,
24}
25
26impl ParseError {
27    /// An error with no particular position.
28    pub fn new(message: impl Into<String>) -> Self {
29        Self {
30            position: Position::None,
31            message: message.into(),
32        }
33    }
34
35    /// An error on a line, counted from 1.
36    pub fn at_line(line: usize, message: impl Into<String>) -> Self {
37        Self {
38            position: Position::Line(line),
39            message: message.into(),
40        }
41    }
42
43    /// An error at a character, counted from 1.
44    pub fn at_char(position: usize, message: impl Into<String>) -> Self {
45        Self {
46            position: Position::Char(position),
47            message: message.into(),
48        }
49    }
50
51    /// Where the error is.
52    pub fn position(&self) -> Position {
53        self.position
54    }
55
56    /// What is wrong, without the position.
57    pub fn message(&self) -> &str {
58        &self.message
59    }
60
61    /// The same error, placed on `line` if it has no position yet.
62    pub fn or_at_line(self, line: usize) -> Self {
63        match self.position {
64            Position::None => Self::at_line(line, self.message),
65            _ => self,
66        }
67    }
68}
69
70impl fmt::Display for ParseError {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        match self.position {
73            Position::None => f.write_str(&self.message),
74            Position::Line(line) => write!(f, "line {line}: {}", self.message),
75            Position::Char(position) => write!(f, "character {position}: {}", self.message),
76        }
77    }
78}
79
80impl Error for ParseError {}