1use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum Error {
13 Parse { message: String, pos: usize },
15 Unknown { name: String },
17 Missing { name: String },
19 OutOfRange { index: i64, len: usize },
22 Type {
24 operation: &'static str,
26 got: &'static str,
29 },
30 Overflow { operation: &'static str },
32 DivideByZero { operation: &'static str },
34 Function { name: String, message: String },
36 UnknownMethod { name: String },
39 Convert { message: String },
41}
42
43pub type Result<T, E = Error> = std::result::Result<T, E>;
46
47impl Error {
48 pub fn position(&self) -> Option<usize> {
51 match self {
52 Error::Parse { pos, .. } => Some(*pos),
53 _ => None,
54 }
55 }
56
57 pub(crate) fn parse(message: impl Into<String>, pos: usize) -> Error {
58 Error::Parse {
59 message: message.into(),
60 pos,
61 }
62 }
63}
64
65impl fmt::Display for Error {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 match self {
68 Error::Parse { message, pos } => write!(f, "parse error at byte {pos}: {message}"),
69 Error::Unknown { name } => write!(f, "unknown variable '{name}'"),
70 Error::Missing { name } => write!(f, "no such member '{name}'"),
71 Error::OutOfRange { index, len } => {
72 write!(f, "index {index} is out of range for a list of {len}")
73 }
74 Error::Type { operation, got } => {
75 write!(f, "no {operation} for a {got} value")
76 }
77 Error::Overflow { operation } => write!(f, "'{operation}' overflowed an int"),
78 Error::DivideByZero { operation } => write!(f, "'{operation}' by zero"),
79 Error::Function { name, message } => {
80 write!(f, "'{name}' failed: {message}")
81 }
82 Error::UnknownMethod { name } => write!(f, "no such method '{name}'"),
83 Error::Convert { message } => write!(f, "{message}"),
84 }
85 }
86}
87
88impl std::error::Error for Error {}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn position_is_only_present_for_parse_errors() {
96 let parse = Error::parse("expected a value", 7);
97 assert_eq!(parse.position(), Some(7));
98 assert_eq!(
99 parse,
100 Error::Parse {
101 message: "expected a value".to_string(),
102 pos: 7
103 }
104 );
105
106 for error in [
107 Error::Unknown {
108 name: "x".to_string(),
109 },
110 Error::Missing {
111 name: "x".to_string(),
112 },
113 Error::OutOfRange { index: 1, len: 0 },
114 Error::Type {
115 operation: "+",
116 got: "int",
117 },
118 Error::Overflow { operation: "+" },
119 Error::DivideByZero { operation: "/" },
120 Error::Function {
121 name: "f".to_string(),
122 message: "failed".to_string(),
123 },
124 Error::UnknownMethod {
125 name: "m".to_string(),
126 },
127 Error::Convert {
128 message: "bad".to_string(),
129 },
130 ] {
131 assert_eq!(error.position(), None);
132 }
133 }
134
135 #[test]
136 fn display_names_the_problem() {
137 assert_eq!(
138 Error::parse("expected a value", 4).to_string(),
139 "parse error at byte 4: expected a value"
140 );
141 assert_eq!(
142 Error::Unknown {
143 name: "count".to_string()
144 }
145 .to_string(),
146 "unknown variable 'count'"
147 );
148 assert_eq!(
149 Error::Missing {
150 name: "retries".to_string()
151 }
152 .to_string(),
153 "no such member 'retries'"
154 );
155 assert_eq!(
156 Error::OutOfRange { index: 3, len: 2 }.to_string(),
157 "index 3 is out of range for a list of 2"
158 );
159 assert_eq!(
160 Error::Type {
161 operation: "+",
162 got: "string"
163 }
164 .to_string(),
165 "no + for a string value"
166 );
167 assert_eq!(
168 Error::Overflow { operation: "*" }.to_string(),
169 "'*' overflowed an int"
170 );
171 assert_eq!(
172 Error::DivideByZero { operation: "%" }.to_string(),
173 "'%' by zero"
174 );
175 assert_eq!(
176 Error::Function {
177 name: "upper".to_string(),
178 message: "takes 1 argument(s), got 0".to_string()
179 }
180 .to_string(),
181 "'upper' failed: takes 1 argument(s), got 0"
182 );
183 assert_eq!(
184 Error::UnknownMethod {
185 name: "upper".to_string()
186 }
187 .to_string(),
188 "no such method 'upper'"
189 );
190 assert_eq!(
191 Error::Convert {
192 message: "not json".to_string()
193 }
194 .to_string(),
195 "not json"
196 );
197 }
198
199 #[test]
200 fn it_is_a_std_error_with_no_source() {
201 let error = Error::Unknown {
202 name: "x".to_string(),
203 };
204 let std_error: &dyn std::error::Error = &error;
205 assert!(std_error.source().is_none());
206 assert_eq!(format!("{std_error}"), "unknown variable 'x'");
207 }
208}