Skip to main content

hml_rs/
error.rs

1//a Imports
2use crate::{Posn, Span};
3use thiserror::Error;
4
5//a HmlResult, MarkupResult
6//tp MarkupResult
7/// The [MarkupResult] type is a result with an error type of
8/// [crate::MarkupError]; it is abstract in the sense that it does not
9/// refer to a specific point in a parser stream; rather it represents
10/// errors such as bad namespace references in tag names
11pub type MarkupResult<T> = std::result::Result<T, MarkupError>;
12
13//tp HmlResult
14/// The [HmlResult] type is a result with an error type of
15/// [crate::HmlError] for a given position within a parsing stream
16pub type HmlResult<T, P> = std::result::Result<T, HmlError<P>>;
17
18//a MarkupError
19//tp MarkupError
20/// An error representing a tag or name issue when building HML
21/// markup; this is not specifically related to a parsing position
22/// (indeed, these errors can be generated by using the HML name
23/// subsystem without any parsing).
24#[derive(Debug, Error)]
25pub enum MarkupError {
26    /// An empty name was provided, which
27    /// is illegal
28    #[error("empty name")]
29    EmptyName {},
30    /// Use of an unmapped prefix / namespace
31    #[error("unmapped_prefix {prefix}")]
32    UnmappedPrefix {
33        /// Prefix
34        prefix: String,
35    },
36    /// Indicates a bad name (such as a:b:c)
37    #[error("bad name {name}")]
38    BadName {
39        /// Name
40        name: String,
41    },
42}
43
44//ip MarkupError
45impl MarkupError {
46    //cp empty_name
47    /// An 'empty' name was provided; names must always contain some
48    /// characters; create the appropriate error
49    pub fn empty_name() -> Self {
50        Self::EmptyName {}
51    }
52
53    //cp unmapped_prefix
54    /// Create a [MarkupError] for a specific namespace prefix that
55    /// has not been mapped in the hierarchy of the document to this
56    /// point
57    pub fn unmapped_prefix(prefix: &str) -> Self {
58        Self::UnmappedPrefix {
59            prefix: prefix.to_string(),
60        }
61    }
62
63    //cp bad_name
64    /// Create a [MarkupError] for a malformed name (such as a:b:c)
65    pub fn bad_name(name: &str) -> Self {
66        Self::BadName {
67            name: name.to_string(),
68        }
69    }
70}
71
72//a HmlError
73//tp HmlError
74/// [HmlError] represents an error from the UTF-8 character reader,
75/// either an IO error from the reader or a malformed UTF-8 encoded
76/// set of bytes.
77#[derive(Debug, Error)]
78pub enum HmlError<P>
79where
80    P: Posn,
81{
82    /// An IO error
83    #[error("io error {source}")]
84    IoError {
85        /// Span of the error
86        span: Span<P>,
87        /// Error
88        source: std::io::Error,
89    },
90    /// A markup error
91    #[error("markup error {source}")]
92    MarkupError {
93        /// Span of the error
94        span: Span<P>,
95        /// Error
96        source: MarkupError,
97    },
98    /// Expected a tag name after hashes
99    #[error("Expected a tag name after hashes")]
100    ExpectedTagName {
101        /// Span of the error
102        span: Span<P>,
103    },
104    /// Expect whitespace after a tag
105    #[error("Expected whitespace after tag")]
106    ExpectedWhitespaceAfterTag {
107        /// Span of the error
108        span: Span<P>,
109    },
110    /// An unexpected character
111    #[error("Unexpected character '{ch}'")]
112    UnexpectedCharacter {
113        /// Span of the error
114        span: Span<P>,
115        /// Character
116        ch: char,
117    },
118    /// Expected a depth of N or N+1
119    #[error("Expected a tag indent of at most {depth}")]
120    UnexpectedTagIndent {
121        /// Span of the error
122        span: Span<P>,
123        /// Depth
124        depth: usize,
125    },
126    /// Iterated beyond the end of the reader stream
127    #[error("Attempt to parse beyond end of tokens, probably a bug")]
128    BeyondEndOfTokens,
129    /// Attribute provided where an attribute was not expected
130    #[error("Found attribute when not expected {attr}")]
131    UnexpectedAttribute {
132        /// Span of the error
133        span: Span<P>,
134        /// Attribute
135        attr: String,
136    },
137    /// Newline in a quoted string
138    #[error("Unexpected newline in quoted string")]
139    UnexpectedNewlineInQuotedString {
140        /// Span of the error
141        span: Span<P>,
142    },
143    /// Expected an '=' for an attribute but got something else
144    #[error("Expected equals, but found '{ch}'")]
145    ExpectedEquals {
146        /// Span of the error
147        span: Span<P>,
148        /// Character
149        ch: char,
150    },
151    /// EOF when it was not expected
152    #[error("Unexpected EOF")]
153    UnexpectedEOF {
154        /// Span of the error
155        span: Span<P>,
156    },
157}
158
159//ip HmlError
160impl<P> HmlError<P>
161where
162    P: Posn,
163{
164    //fp unexpected_eof
165    /// Return an unexpected_eof error at the specified positions
166    pub fn unexpected_eof<T>(start: &P, end: &P) -> HmlResult<T, P> {
167        let span = Span::new_at(start).end_at(end);
168        Err(Self::UnexpectedEOF { span })
169    }
170
171    //fp unexpected_character
172    /// Return an unexpected_character error at the specified positions
173    pub fn unexpected_character<T>(start: &P, end: &P, ch: char) -> HmlResult<T, P> {
174        let span = Span::new_at(start).end_at(end);
175        Err(Self::UnexpectedCharacter { span, ch })
176    }
177
178    //fp unexpected_newline_in_string
179    /// Return an unexpected newline error
180    pub fn unexpected_newline_in_string<T>(start: &P, end: &P) -> HmlResult<T, P> {
181        let span = Span::new_at(start).end_at(end);
182        Err(Self::UnexpectedNewlineInQuotedString { span })
183    }
184
185    //fp expected_equals
186    /// Return an error indicating an expected character, but got a different character
187    pub fn expected_equals<T>(start: &P, end: &P, ch: char) -> HmlResult<T, P> {
188        let span = Span::new_at(start).end_at(end);
189        Err(Self::ExpectedEquals { span, ch })
190    }
191
192    //fp no_more_events
193    /// Return an error indicating a read beyond the end of the stream
194    pub fn no_more_events<T>() -> HmlResult<T, P> {
195        Err(Self::BeyondEndOfTokens)
196    }
197
198    //fp unexpected_tag_indent
199    /// Return an unexpected_tag_indent error over the specified span
200    pub fn unexpected_tag_indent<T>(span: Span<P>, depth: usize) -> HmlResult<T, P> {
201        Err(Self::UnexpectedTagIndent { span, depth })
202    }
203
204    //fp unexpected_attribute
205    /// Return an unexpected_attribute eror over the given span
206    pub fn unexpected_attribute<T>(span: Span<P>, prefx: &str, name: &str) -> HmlResult<T, P> {
207        let attr = format!("{}:{}", prefx, name);
208        Err(Self::UnexpectedAttribute { span, attr })
209    }
210
211    //fp io_error
212    /// An IO error
213    pub fn io_error(span: Span<P>, source: std::io::Error) -> Self {
214        Self::IoError { span, source }
215    }
216
217    //fp of_io_result
218    /// Map a io result over a span to a HmlError result
219    pub fn of_io_result<T>(
220        span: Span<P>,
221        r: std::result::Result<T, std::io::Error>,
222    ) -> HmlResult<T, P> {
223        match r {
224            Ok(t) => Ok(t),
225            Err(e) => Err(Self::io_error(span, e)),
226        }
227    }
228}
229
230//ip HmlError<P>
231impl<P> HmlError<P>
232where
233    P: Posn,
234{
235    //mp span
236    /// Borrow a span if it has one
237    pub fn span(&self) -> Option<&Span<P>> {
238        match self {
239            Self::IoError { span, .. } => Some(span),
240            Self::UnexpectedCharacter { span, .. } => Some(span),
241            Self::UnexpectedTagIndent { span, .. } => Some(span),
242            Self::UnexpectedAttribute { span, .. } => Some(span),
243            Self::UnexpectedEOF { span, .. } => Some(span),
244            Self::UnexpectedNewlineInQuotedString { span, .. } => Some(span),
245            Self::ExpectedEquals { span, .. } => Some(span),
246            Self::BeyondEndOfTokens => None,
247            Self::MarkupError { span, .. } => Some(span),
248            Self::ExpectedTagName { span, .. } => Some(span),
249            Self::ExpectedWhitespaceAfterTag { span, .. } => Some(span),
250        }
251    }
252
253    //cp map_markup_error
254    /// Map a [MarkupResult] to an [HmlResult]; this passes an Ok
255    /// value through unchanged, but it maps an Err to an
256    /// HmlResult::MarkupError with the provided span
257    #[inline(always)]
258    pub fn map_markup_error<T>(result: MarkupResult<T>, span: &Span<P>) -> HmlResult<T, P> {
259        match result {
260            Err(source) => Err(Self::MarkupError {
261                source,
262                span: *span,
263            }),
264            Ok(t) => Ok(t),
265        }
266    }
267}
268
269//ip HmlError<P>
270impl<P> lexer_rs::LexerError<P> for HmlError<P>
271where
272    P: Posn,
273{
274    //cp failed_to_parse
275
276    // This creates a parse error for a particular location and
277    // character, as required by the Lexer
278    fn failed_to_parse(posn: P, ch: char) -> Self {
279        let span = Span::new_at(&posn);
280        Self::UnexpectedCharacter { span, ch }
281    }
282}