1use crate::token::Token;
8use std::fmt;
9
10#[derive(Clone, Debug, PartialEq)]
12pub struct Error {
13 msg: String,
14}
15
16impl Error {
17 pub fn new(msg: String) -> Self {
18 Self { msg }
19 }
20
21 fn indexed_error(input: String, point: i32, err: String, expl: Vec<&str>) -> Self {
34 let mut message = err;
35
36 let tab = " ";
37 let space_count = if point > 1 { (point - 1) as usize } else { 0 };
38 let space: String = " ".repeat(space_count);
39
40 message.push_str(&format!("{tab}\"{}\" \n", input.trim_end()));
41 for exp in expl.iter() {
42 message.push_str(&format!(" {tab}{space}{exp}\n"));
43 }
44
45 Self { msg: message }
46 }
47
48 pub fn empty_input() -> Self {
50 Self {
51 msg: String::from("error: cannot parse an empty input"),
52 }
53 }
54
55 pub fn empty_tokens() -> Self {
57 Self {
58 msg: String::from("error: cannot calculate result from an empty token list"),
59 }
60 }
61
62 pub fn missing_some_tokens(input: String, point: i32) -> Self {
76 let message = "error: missing some tokens to calculate result\n\n".to_string();
77
78 let mut inpt: String = input.trim_end().to_string();
79 let pointer = " {X} ";
80
81 for (i, pch) in pointer.chars().enumerate() {
82 let p: i32 = point + (i as i32) + 1;
83
84 let backid: usize = if p < 1 { 0 } else { (p - 1) as usize };
85
86 let back_ch = inpt.chars().nth(backid).unwrap_or('0');
87 let next_ch = inpt.chars().nth((p + 1) as usize).unwrap_or('0');
88
89 if (back_ch == ' ' || next_ch == ' ') && pch == ' ' {
90 continue;
91 }
92
93 inpt.insert(p as usize, pch);
94 }
95
96 let explanation: Vec<&str> = Vec::from([
98 "|",
99 "| > Expected a token character.",
100 "| > hint: `42`, `+`, `-`, `/`, `*`, `%`, `^`.",
101 ]);
102
103 Error::indexed_error(inpt, point + 4, message, explanation)
104 }
105
106 pub fn cannot_parse_to_number(input: String, token: Token) -> Self {
118 let message = format!(
119 "error: cannot parse token literal: `{}` to a number\n\n",
120 token.literal
121 );
122
123 let explanation: Vec<&str> = Vec::from([
125 "|",
126 "| > Cannot convert the character (that represented",
127 "| > as number) to the actual number representation.",
128 ]);
129
130 Error::indexed_error(input, token.index.1 + 1, message, explanation)
131 }
132
133 pub fn invalid_order() -> Self {
135 let space = " ";
136 let mut msg = String::from("error: invalid order of token characters\n");
137
138 msg.push_str(&format!("{space}A valid token/character order is:"));
139 msg.push_str(&format!("{space}[Numerable], [Operation], [Numerable]"));
140
141 Self { msg }
142 }
143
144 pub fn illegal_token(input: String, token: Token) -> Self {
145 let message = format!(
146 "error: found an illegal character: `{}` \n\n",
147 token.literal
148 );
149
150 let explanation: Vec<&str> = Vec::from([
152 "|",
153 "| > We do not know how to parse this character",
154 "| > If you think this is a bug or a practical feature",
155 "| > that we do not have yet, please open an issue:",
156 "| > -> https://github.com/theiskaa/mate/issues/new",
157 ]);
158
159 Error::indexed_error(input, token.index.1 + 1, message, explanation)
160 }
161
162 pub fn division_by_zero(input: String, point: i32) -> Self {
164 let message = String::from("error: division by zero\n\n");
165
166 let explanation: Vec<&str> = Vec::from([
167 "|",
168 "| > Cannot divide by zero.",
169 "| > hint: ensure the divisor is not zero.",
170 ]);
171
172 Error::indexed_error(input, point, message, explanation)
173 }
174
175 pub fn mismatched_parentheses(input: String, point: i32) -> Self {
176 let message = String::from("error: mismatched parentheses or brackets\n\n");
177
178 let explanation: Vec<&str> = Vec::from([
179 "|",
180 "| > Found a closing bracket without a matching opening bracket,",
181 "| > or brackets are mismatched (e.g., '(' closed with ']').",
182 "| > hint: ensure all brackets are properly paired.",
183 ]);
184
185 Error::indexed_error(input, point, message, explanation)
186 }
187}
188
189impl fmt::Display for Error {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 write!(f, "{}", self.msg)
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 fn new() {
201 let result: Error = Error::new(String::from("test message"));
202 assert_eq!(result.msg, String::from("test message"));
203 }
204
205 #[test]
206 fn empty_input() {
207 let result: Error = Error::empty_input();
208 assert_eq!(
209 result.msg,
210 String::from("error: cannot parse an empty input")
211 );
212 }
213
214 #[test]
215 fn empty_tokens() {
216 let result: Error = Error::empty_tokens();
217 assert_eq!(
218 result.msg,
219 String::from("error: cannot calculate result from an empty token list")
220 );
221 }
222
223 #[test]
224 fn display() {
225 let error: Error = Error::new(String::from("A new message"));
226 assert_eq!(format!("{}", error), error.msg)
227 }
228}