tui_markup/parser/
error.rs1use std::fmt::Display;
2
3use winnow::{
4 error::{AddContext, ParserError},
5 stream::{LocatingSlice, Offset},
6};
7
8use crate::{error::LocatedError, parser::LSpan};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum ErrorKind {
13 UnescapedChar,
15 UnescapableChar,
17 ElementNotClose,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct Error<'a> {
24 kind: Option<ErrorKind>,
25 pub(crate) input: LSpan<'a>,
27 pub(crate) line: usize,
29}
30
31impl Display for Error<'_> {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 f.write_str(match self.kind {
34 Some(kind) => match kind {
35 ErrorKind::UnescapedChar => "unescaped character",
36 ErrorKind::UnescapableChar => "unescapable character",
37 ErrorKind::ElementNotClose => "expect '>' to close element for element starter",
38 },
39 None => "unknown error",
40 })?;
41
42 if let Some(c) = self.input.chars().next() {
43 f.write_fmt(format_args!(" \'{}\'", c))?;
44 }
45
46 let (line, offset) = self.location();
47 f.write_fmt(format_args!(" near {}:{}", line, offset))
48 }
49}
50
51impl std::error::Error for Error<'_> {}
52
53impl<'a> Error<'a> {
54 fn new(input: &LocatingSlice<&'a str>) -> Self {
55 Self {
56 kind: None,
57 input: *input,
58 line: 0,
59 }
60 }
61
62 pub(crate) fn with_input(mut self, input: &LocatingSlice<&'a str>) -> Self {
63 self.input = *input;
64 self
65 }
66
67 pub(crate) fn with_line(mut self, line: usize) -> Self {
69 self.line = line;
70 self
71 }
72
73 pub(crate) fn with_kind(mut self, kind: ErrorKind) -> Self {
75 self.kind = Some(kind);
76 self
77 }
78
79 pub fn kind(&self) -> Option<ErrorKind> {
81 self.kind
82 }
83}
84
85impl LocatedError for Error<'_> {
86 fn location(&self) -> (usize, usize) {
87 let mut start = self.input;
88 start.reset_to_start();
89 (self.line + 1, self.input.offset_from(&start) + 1)
90 }
91}
92
93impl<'a> ParserError<LSpan<'a>> for Error<'a> {
94 type Inner = Self;
95
96 fn from_input(input: &LocatingSlice<&'a str>) -> Self {
97 Self::new(input)
98 }
99
100 fn into_inner(self) -> Result<Self::Inner, Self> {
101 Ok(self)
102 }
103}
104
105impl<'a> AddContext<LSpan<'a>, ErrorKind> for Error<'a> {
106 fn add_context(
107 self, _input: &LSpan<'a>, _token_start: &<LSpan<'a> as winnow::stream::Stream>::Checkpoint,
108 context: ErrorKind,
109 ) -> Self {
110 self.with_kind(context)
111 }
112}