brush_parser/
error.rs

1use crate::Token;
2use crate::tokenizer;
3
4/// Represents an error that occurred while parsing tokens.
5#[derive(thiserror::Error, Debug)]
6pub enum ParseError {
7    /// A parsing error occurred near the given token.
8    #[error("parse error near token: {}", .0.location().start)]
9    ParsingNearToken(Token),
10
11    /// A parsing error occurred at the end of the input.
12    #[error("parse error at end of input")]
13    ParsingAtEndOfInput,
14
15    /// An error occurred while tokenizing the input stream.
16    #[error("failed to tokenize input")]
17    Tokenizing {
18        /// The inner error.
19        inner: tokenizer::TokenizerError,
20        /// Optionally provides the position of the error.
21        position: Option<tokenizer::SourcePosition>,
22    },
23}
24
25#[cfg(feature = "diagnostics")]
26#[allow(clippy::cast_sign_loss)]
27pub mod miette {
28    use super::ParseError;
29    use miette::SourceOffset;
30
31    impl ParseError {
32        /// Convert the original error to one miette can pretty print
33        pub fn to_pretty_error(self, input: impl Into<String>) -> PrettyError {
34            let input = input.into();
35            let location = match self {
36                Self::ParsingNearToken(ref token) => Some(SourceOffset::from_location(
37                    &input,
38                    token.location().start.line as usize,
39                    token.location().start.column as usize,
40                )),
41                Self::Tokenizing { ref position, .. } => position.as_ref().map(|p| {
42                    SourceOffset::from_location(&input, p.line as usize, p.column as usize)
43                }),
44                Self::ParsingAtEndOfInput => {
45                    Some(SourceOffset::from_location(&input, usize::MAX, usize::MAX))
46                }
47            };
48
49            PrettyError {
50                cause: self,
51                input,
52                location,
53            }
54        }
55    }
56
57    /// Represents an error that occurred while parsing tokens.
58    #[derive(thiserror::Error, Debug, miette::Diagnostic)]
59    #[error("Cannot parse the input script")]
60    pub struct PrettyError {
61        cause: ParseError,
62        #[source_code]
63        input: String,
64        #[label("{cause}")]
65        location: Option<SourceOffset>,
66    }
67}
68
69/// Represents a parsing error with its location information
70#[derive(Debug, thiserror::Error)]
71#[error(transparent)]
72pub struct ParseErrorLocation {
73    #[from]
74    inner: peg::error::ParseError<peg::str::LineCol>,
75}
76
77/// Represents an error that occurred while parsing a word.
78#[derive(Debug, thiserror::Error)]
79pub enum WordParseError {
80    /// An error occurred while parsing an arithmetic expression.
81    #[error("failed to parse arithmetic expression")]
82    ArithmeticExpression(ParseErrorLocation),
83
84    /// An error occurred while parsing a shell pattern.
85    #[error("failed to parse pattern")]
86    Pattern(ParseErrorLocation),
87
88    /// An error occurred while parsing a prompt string.
89    #[error("failed to parse prompt string")]
90    Prompt(ParseErrorLocation),
91
92    /// An error occurred while parsing a parameter.
93    #[error("failed to parse parameter '{0}'")]
94    Parameter(String, ParseErrorLocation),
95
96    /// An error occurred while parsing for brace expansion.
97    #[error("failed to parse for brace expansion: '{0}'")]
98    BraceExpansion(String, ParseErrorLocation),
99
100    /// An error occurred while parsing a word.
101    #[error("failed to parse word '{0}'")]
102    Word(String, ParseErrorLocation),
103}
104
105/// Represents an error that occurred while parsing a (non-extended) test command.
106#[derive(Debug, thiserror::Error)]
107#[error(transparent)]
108pub struct TestCommandParseError(#[from] peg::error::ParseError<usize>);
109
110/// Represents an error that occurred while parsing a key-binding specification.
111#[derive(Debug, thiserror::Error)]
112pub enum BindingParseError {
113    /// An unknown error occurred while parsing a key-binding specification.
114    #[error("unknown error while parsing key-binding: '{0}'")]
115    Unknown(String),
116
117    /// A key code was missing from the key-binding specification.
118    #[error("missing key code in key-binding")]
119    MissingKeyCode,
120}
121
122pub(crate) fn convert_peg_parse_error(
123    err: &peg::error::ParseError<usize>,
124    tokens: &[Token],
125) -> ParseError {
126    let approx_token_index = err.location;
127
128    if approx_token_index < tokens.len() {
129        ParseError::ParsingNearToken(tokens[approx_token_index].clone())
130    } else {
131        ParseError::ParsingAtEndOfInput
132    }
133}