1use crate::Span;
2use std::fmt;
3
4#[derive(Debug)]
5pub struct Error {
6 pub kind: Box<ErrorKind>,
7}
8
9#[derive(Debug)]
10pub enum ErrorKind {
11 Io(std::io::Error),
12 Regex(regex::Error),
13 Dialoguer(dialoguer::Error),
14 Parse { message: String, span: Span },
15 UnexpectedChar { character: char, span: Span },
16 ExpectedChar { expected: char, found: char, span: Span },
17 ExpectedString { expected: String, found: String, span: Span },
18 ExpectedOneOf { expected: Vec<String>, found: String, span: Span },
19 ExpectedClosingTag { expected: String, found: String, span: Span },
20 InvalidI18n { format: String, span: Span },
21 UnsupportedI18nFormat { format: String, span: Span },
22 NotImplemented { feature: String, span: Span },
23 ParseFloatError { source: String, span: Span },
24 UnexpectedContent { message: String, span: Span },
25 TrailingContent { span: Span },
26 ExternalError { source: String, details: String, span: Span },
27 Codegen { message: String, span: Span },
28}
29
30impl Error {
31 pub fn new(kind: ErrorKind) -> Self {
32 Self { kind: Box::new(kind) }
33 }
34
35 pub fn io_error(err: std::io::Error) -> Self {
36 Self::new(ErrorKind::Io(err))
37 }
38
39 pub fn unexpected_char(character: char, span: Span) -> Self {
40 Self::new(ErrorKind::UnexpectedChar { character, span })
41 }
42
43 pub fn expected_char(expected: char, found: char, span: Span) -> Self {
44 Self::new(ErrorKind::ExpectedChar { expected, found, span })
45 }
46
47 pub fn expected_string(expected: String, found: String, span: Span) -> Self {
48 Self::new(ErrorKind::ExpectedString { expected, found, span })
49 }
50
51 pub fn expected_one_of(expected: Vec<String>, found: String, span: Span) -> Self {
52 Self::new(ErrorKind::ExpectedOneOf { expected, found, span })
53 }
54
55 pub fn expected_closing_tag(expected: String, found: String, span: Span) -> Self {
56 Self::new(ErrorKind::ExpectedClosingTag { expected, found, span })
57 }
58
59 pub fn invalid_i18n(format: String, span: Span) -> Self {
60 Self::new(ErrorKind::InvalidI18n { format, span })
61 }
62
63 pub fn unsupported_i18n_format(format: String, span: Span) -> Self {
64 Self::new(ErrorKind::UnsupportedI18nFormat { format, span })
65 }
66
67 pub fn not_implemented(feature: String, span: Span) -> Self {
68 Self::new(ErrorKind::NotImplemented { feature, span })
69 }
70
71 pub fn parse_error(message: String, span: Span) -> Self {
72 Self::new(ErrorKind::Parse { message, span })
73 }
74
75 pub fn parse_float_error(source: String, span: Span) -> Self {
76 Self::new(ErrorKind::ParseFloatError { source, span })
77 }
78
79 pub fn trailing_content(span: Span) -> Self {
80 Self::new(ErrorKind::TrailingContent { span })
81 }
82
83 pub fn external_error(source: String, details: String, span: Span) -> Self {
84 Self::new(ErrorKind::ExternalError { source, details, span })
85 }
86
87 pub fn codegen_error(message: String, span: Span) -> Self {
88 Self::new(ErrorKind::Codegen { message, span })
89 }
90
91 pub fn regex_error(err: regex::Error) -> Self {
92 Self::new(ErrorKind::Regex(err))
93 }
94
95 pub fn dialoguer_error(err: dialoguer::Error) -> Self {
96 Self::new(ErrorKind::Dialoguer(err))
97 }
98
99 pub fn span(&self) -> Span {
100 self.kind.span()
101 }
102}
103
104impl ErrorKind {
105 pub fn span(&self) -> Span {
106 match self {
107 ErrorKind::Io(_) => Span::unknown(),
108 ErrorKind::Regex(_) => Span::unknown(),
109 ErrorKind::Dialoguer(_) => Span::unknown(),
110 ErrorKind::Parse { span, .. } => *span,
111 ErrorKind::UnexpectedChar { span, .. } => *span,
112 ErrorKind::ExpectedChar { span, .. } => *span,
113 ErrorKind::ExpectedString { span, .. } => *span,
114 ErrorKind::ExpectedOneOf { span, .. } => *span,
115 ErrorKind::ExpectedClosingTag { span, .. } => *span,
116 ErrorKind::InvalidI18n { span, .. } => *span,
117 ErrorKind::UnsupportedI18nFormat { span, .. } => *span,
118 ErrorKind::NotImplemented { span, .. } => *span,
119 ErrorKind::ParseFloatError { span, .. } => *span,
120 ErrorKind::UnexpectedContent { span, .. } => *span,
121 ErrorKind::TrailingContent { span, .. } => *span,
122 ErrorKind::ExternalError { span, .. } => *span,
123 ErrorKind::Codegen { span, .. } => *span,
124 }
125 }
126}
127
128impl fmt::Display for Error {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match &*self.kind {
131 ErrorKind::Io(err) => write!(f, "IO error: {}", err),
132 ErrorKind::Regex(err) => write!(f, "Regex error: {}", err),
133 ErrorKind::Dialoguer(err) => write!(f, "Dialoguer error: {}", err),
134 ErrorKind::Parse { message, span } => {
135 write!(f, "Parse error: {}", message)?;
136 if !span.is_unknown() {
137 write!(f, " at {}:{}", span.start.line, span.start.column)?;
138 }
139 Ok(())
140 }
141 ErrorKind::UnexpectedChar { character, span } => {
142 write!(f, "Unexpected character '{}'", character)?;
143 if !span.is_unknown() {
144 write!(f, " at {}:{}", span.start.line, span.start.column)?;
145 }
146 Ok(())
147 }
148 ErrorKind::ExpectedChar { expected, found, span } => {
149 write!(f, "Expected '{}', found '{}'", expected, found)?;
150 if !span.is_unknown() {
151 write!(f, " at {}:{}", span.start.line, span.start.column)?;
152 }
153 Ok(())
154 }
155 ErrorKind::ExpectedString { expected, found, span } => {
156 write!(f, "Expected '{}', found '{}'", expected, found)?;
157 if !span.is_unknown() {
158 write!(f, " at {}:{}", span.start.line, span.start.column)?;
159 }
160 Ok(())
161 }
162 ErrorKind::ExpectedOneOf { expected, found, span } => {
163 write!(f, "Expected one of {:?}, found '{}'", expected, found)?;
164 if !span.is_unknown() {
165 write!(f, " at {}:{}", span.start.line, span.start.column)?;
166 }
167 Ok(())
168 }
169 ErrorKind::ExpectedClosingTag { expected, found, span } => {
170 write!(f, "Expected closing tag </{}>, found </{}>", expected, found)?;
171 if !span.is_unknown() {
172 write!(f, " at {}:{}", span.start.line, span.start.column)?;
173 }
174 Ok(())
175 }
176 ErrorKind::InvalidI18n { format, span } => {
177 write!(f, "Invalid i18n {}", format)?;
178 if !span.is_unknown() {
179 write!(f, " at {}:{}", span.start.line, span.start.column)?;
180 }
181 Ok(())
182 }
183 ErrorKind::UnsupportedI18nFormat { format, span } => {
184 write!(f, "Unsupported i18n format: {}", format)?;
185 if !span.is_unknown() {
186 write!(f, " at {}:{}", span.start.line, span.start.column)?;
187 }
188 Ok(())
189 }
190 ErrorKind::NotImplemented { feature, span } => {
191 write!(f, "{} not yet implemented", feature)?;
192 if !span.is_unknown() {
193 write!(f, " at {}:{}", span.start.line, span.start.column)?;
194 }
195 Ok(())
196 }
197 ErrorKind::ParseFloatError { source, span } => {
198 write!(f, "Failed to parse float: '{}'", source)?;
199 if !span.is_unknown() {
200 write!(f, " at {}:{}", span.start.line, span.start.column)?;
201 }
202 Ok(())
203 }
204 ErrorKind::UnexpectedContent { message, span } => {
205 write!(f, "Unexpected content: {}", message)?;
206 if !span.is_unknown() {
207 write!(f, " at {}:{}", span.start.line, span.start.column)?;
208 }
209 Ok(())
210 }
211 ErrorKind::TrailingContent { span } => {
212 write!(f, "Unexpected trailing content")?;
213 if !span.is_unknown() {
214 write!(f, " at {}:{}", span.start.line, span.start.column)?;
215 }
216 Ok(())
217 }
218 ErrorKind::ExternalError { source, details, span } => {
219 write!(f, "Error in {}: {}", source, details)?;
220 if !span.is_unknown() {
221 write!(f, " at {}:{}", span.start.line, span.start.column)?;
222 }
223 Ok(())
224 }
225 ErrorKind::Codegen { message, span } => {
226 write!(f, "Codegen error: {}", message)?;
227 if !span.is_unknown() {
228 write!(f, " at {}:{}", span.start.line, span.start.column)?;
229 }
230 Ok(())
231 }
232 }
233 }
234}
235
236impl std::error::Error for Error {
237 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
238 match &*self.kind {
239 ErrorKind::Io(err) => Some(err),
240 ErrorKind::Regex(err) => Some(err),
241 ErrorKind::Dialoguer(err) => Some(err),
242 _ => None,
243 }
244 }
245}
246
247impl From<std::io::Error> for Error {
248 fn from(err: std::io::Error) -> Self {
249 Error::new(ErrorKind::Io(err))
250 }
251}
252
253impl From<regex::Error> for Error {
254 fn from(err: regex::Error) -> Self {
255 Error::new(ErrorKind::Regex(err))
256 }
257}
258
259impl From<dialoguer::Error> for Error {
260 fn from(err: dialoguer::Error) -> Self {
261 Error::new(ErrorKind::Dialoguer(err))
262 }
263}
264
265impl From<serde_json::Error> for Error {
266 fn from(err: serde_json::Error) -> Self {
267 Error::new(ErrorKind::ExternalError { source: "serde_json".to_string(), details: err.to_string(), span: Span::unknown() })
268 }
269}
270
271pub type Result<T> = std::result::Result<T, Error>;