mate_rs/
errors.rs

1//
2// Copyright 2022-present theiskaa. All rights reserved.
3// Use of this source code is governed by MIT license
4// that can be found in the LICENSE file.
5//
6
7use crate::token::Token;
8
9// Main structure model for errors of lexer.
10#[derive(Clone, Debug, PartialEq)]
11pub struct Error {
12    msg: String,
13}
14
15impl Error {
16    pub fn new(msg: String) -> Self {
17        Self { msg }
18    }
19
20    // The error template used to generate cool error messages by input, invalid token, title of
21    // error and explanation of error.
22    // Generated error would be like:
23    //
24    // ```
25    // <your {err} title here>
26    //
27    //      "<your input here>"
28    //         |
29    //         | > Your detailed error
30    //         | > explanation here.
31    // ```
32    fn indexed_error(input: String, point: i32, err: String, expl: Vec<&str>) -> Self {
33        let mut message = err.clone();
34
35        let tab: String = String::from("     ");
36        let mut space: String = String::from("");
37        for _ in 0..point - 1 {
38            space.push_str(" ");
39        }
40
41        message.push_str(format!("{}\"{}\" \n", tab.clone(), input.trim_end()).as_str());
42        for exp in expl.iter() {
43            message.push_str(format!(" {}{}{}\n", tab.clone(), space.clone(), exp).as_str());
44        }
45
46        Self { msg: message }
47    }
48
49    // A custom early made error for empty input cases.
50    pub fn empty_input() -> Self {
51        Self {
52            msg: String::from("error: cannot parse an empty input"),
53        }
54    }
55
56    // A custom early made error for empty tokens cases.
57    pub fn empty_tokens() -> Self {
58        Self {
59            msg: String::from("error: cannot calculate result from an empty token list"),
60        }
61    }
62
63    // A custom early made error for invalid tokens cases.
64    // Looks like:
65    //
66    // ```
67    // [!] error: missing some tokens to calculate result
68    //
69    //      "<your input here [X]>"
70    //                         |
71    //                         | > Cannot convert the character
72    //                         | > that represented as number,
73    //                         | > to the actual number representation.
74    // ```
75    //
76    pub fn missing_some_tokens(input: String, point: i32) -> Self {
77        let message = format!("error: missing some tokens to calculate result\n\n");
78
79        let mut inpt: String = input.clone().trim_end().to_string();
80        let pointer: String = String::from(" {X} ");
81
82        for i in 1..pointer.len() {
83            let p: usize = (point as usize) + i;
84            let pch: char = pointer.chars().nth(i - 1).unwrap();
85
86            let back_ch: char = match inpt.chars().nth(p - 1) {
87                Some(v) => v,
88                None => '0',
89            };
90            let next_ch: char = match inpt.chars().nth(p + 1) {
91                Some(v) => v,
92                None => '0',
93            };
94
95            if back_ch == ' ' && pch == ' ' || next_ch == ' ' && pch == ' ' {
96                continue;
97            }
98
99            inpt.insert(p, pch);
100        }
101
102        // A split list of error explanation.
103        let explanation: Vec<&str> = Vec::from([
104            "|",
105            "| > Expected a token character.",
106            "| > hint: `42`, `+`, `-`, `/`, `*`, `%`, `^`.",
107        ]);
108
109        Error::indexed_error(inpt, point + 4, message, explanation)
110    }
111
112    // A custom [indexed_error] implementation for rust string -> to -> number parsing error.
113    // Looks like:
114    //
115    // ```
116    // error: cannot parse token literal: `<token-literal>` to a number
117    //
118    //      "<your input here>"
119    //         |
120    //         | > Cannot convert the character (that represented
121    //         | > as number) to the actual number representation.
122    // ```
123    pub fn cannot_parse_to_number(input: String, token: Token) -> Self {
124        let message = format!(
125            "error: cannot parse token literal: `{}` to a number\n\n",
126            token.clone().literal.clone()
127        );
128
129        // A split list of error explanation.
130        let explanation: Vec<&str> = Vec::from([
131            "|",
132            "| > Cannot convert the character (that represented",
133            "| > as number) to the actual number representation.",
134        ]);
135
136        Error::indexed_error(input, token.index.1 + 1, message, explanation)
137    }
138
139    // A custom early made error for invalid order case of token characters.
140    pub fn invalid_order() -> Self {
141        let space = "      ";
142        let mut msg = String::from("error: invalid order of token characters\n");
143
144        msg.push_str(format!("{}A valid token/character order is:", space).as_str());
145        msg.push_str(format!("{}[Numerable], [Operation], [Numerable]", space).as_str());
146
147        Self { msg }
148    }
149
150    // A custom [indexed_error] implementation for illegal token error.
151    // Looks like:
152    //
153    // ```
154    // error: found an illegal character `<token-literal>`
155    //
156    //      "<your input here>"
157    //         |
158    //         | > We do not know how to parse this character
159    //         | > If you think this is a bug or a practical feature
160    //         | > that we do not have yet, please open an issue:
161    //         | >   -> https://github.com/theiskaa/mate/issues/new
162    // ```
163    pub fn illeagal_token(input: String, token: Token) -> Self {
164        let message = format!(
165            "error: found an illegal character: `{}` \n\n",
166            token.clone().literal
167        );
168
169        // A split list of error explanation.
170        let explanation: Vec<&str> = Vec::from([
171            "|",
172            "| > We do not know how to parse this character",
173            "| > If you think this is a bug or a practical feature",
174            "| > that we do not have yet, please open an issue:",
175            "| >   -> https://github.com/theiskaa/mate/issues/new",
176        ]);
177
178        Error::indexed_error(input, token.index.1 + 1, message, explanation)
179    }
180
181    pub fn to_string(&self) -> String {
182        self.msg.clone()
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn new() {
192        let result: Error = Error::new(String::from("test message"));
193        assert_eq!(result.msg, String::from("test message"));
194    }
195
196    #[test]
197    fn empty_input() {
198        let result: Error = Error::empty_input();
199        assert_eq!(
200            result.msg,
201            String::from("error: cannot parse an empty input")
202        );
203    }
204
205    #[test]
206    fn empty_tokens() {
207        let result: Error = Error::empty_tokens();
208        assert_eq!(
209            result.msg,
210            String::from("error: cannot calculate result from an empty token list")
211        );
212    }
213
214    #[test]
215    fn to_string() {
216        let error: Error = Error::new(String::from("A new message"));
217        assert_eq!(error.to_string(), error.msg)
218    }
219}