ucglib/
error.rs

1// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
2//
3//  Licensed under the Apache License, Version 2.0 (the "License");
4//  you may not use this file except in compliance with the License.
5//  You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14
15//! Errors for use by the ucg compiler.
16use std::error;
17use std::fmt;
18use std::fmt::Debug;
19
20use crate::ast::*;
21use crate::iter::FilePositioned;
22
23/// ErrorType defines the various types of errors that can result from compiling UCG into an
24/// output format.
25pub enum ErrorType {
26    // Build Errors
27    TypeFail,
28    DuplicateBinding,
29    Unsupported,
30    NoSuchSymbol,
31    BadArgLen,
32    FormatError,
33    IncludeError,
34    ImportError,
35    ReservedWordError,
36    // Parsing Errors
37    ParseError,
38    AssertError,
39    OSError,
40    // Conversion errors
41    ConvertError,
42    // User Defined Declarative Errors
43    UserDefined,
44}
45
46impl fmt::Display for ErrorType {
47    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
48        let name = match self {
49            &ErrorType::TypeFail => "TypeFail",
50            &ErrorType::DuplicateBinding => "DuplicateBinding",
51            &ErrorType::Unsupported => "Unsupported",
52            &ErrorType::NoSuchSymbol => "NoSuchSymbol",
53            &ErrorType::BadArgLen => "BadArgLen",
54            &ErrorType::FormatError => "FormatError",
55            &ErrorType::IncludeError => "IncludeError",
56            &ErrorType::ImportError => "ImportError",
57            &ErrorType::ReservedWordError => "ReservedWordError",
58            &ErrorType::ParseError => "ParseError",
59            &ErrorType::AssertError => "AssertError",
60            &ErrorType::OSError => "OSError",
61            &ErrorType::ConvertError => "ConvertError",
62            &ErrorType::UserDefined => "UserDefined",
63        };
64        w.write_str(name)
65    }
66}
67
68/// Error defines an Error type for parsing and building UCG code.
69pub struct BuildError {
70    pub err_type: ErrorType,
71    pub pos: Option<Position>,
72    pub msg: String,
73    pub cause: Option<Box<dyn error::Error>>,
74    // This field is only present to prevent people from constructing these
75    // outside of the module they are defined in.
76    _pkgonly: (),
77}
78
79impl BuildError {
80    pub fn with_pos<S: Into<String>>(msg: S, t: ErrorType, pos: Position) -> Self {
81        BuildError {
82            err_type: t,
83            pos: Some(pos),
84            msg: msg.into(),
85            cause: None,
86            _pkgonly: (),
87        }
88    }
89
90    pub fn new<S: Into<String>>(msg: S, t: ErrorType) -> Self {
91        BuildError {
92            err_type: t,
93            pos: None,
94            msg: msg.into(),
95            cause: None,
96            _pkgonly: (),
97        }
98    }
99
100    pub fn wrap_cause(mut self, cause: Box<dyn error::Error>) -> Self {
101        self.cause = Some(cause);
102        self
103    }
104
105    pub fn to_boxed(self) -> Box<Self> {
106        Box::new(self)
107    }
108
109    fn render(&self, w: &mut fmt::Formatter) -> fmt::Result {
110        if let Some(ref pos) = self.pos {
111            write!(w, "{}: {} at {}", self.err_type, self.msg, pos,)?;
112        } else {
113            write!(w, "{}: {}", self.err_type, self.msg)?;
114        }
115        if let Some(ref cause) = self.cause {
116            write!(w, "\nCaused By:\n\t{}", cause)?;
117        }
118        Ok(())
119    }
120}
121
122impl Debug for BuildError {
123    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
124        self.render(w)
125    }
126}
127
128impl fmt::Display for BuildError {
129    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
130        self.render(w)
131    }
132}
133
134impl error::Error for BuildError {
135    fn description(&self) -> &str {
136        &self.msg
137    }
138}
139
140impl<'a, C> std::convert::From<abortable_parser::Error<C>> for BuildError
141where
142    C: FilePositioned + 'a,
143    C: abortable_parser::Offsetable + Debug,
144{
145    fn from(e: abortable_parser::Error<C>) -> BuildError {
146        BuildError::from(&e)
147    }
148}
149
150impl<'a, C> std::convert::From<&'a abortable_parser::Error<C>> for BuildError
151where
152    C: FilePositioned + 'a,
153    C: abortable_parser::Offsetable + Debug,
154{
155    fn from(e: &'a abortable_parser::Error<C>) -> BuildError {
156        let ctx = e.get_context();
157        let position = Position::new(ctx.line(), ctx.column(), ctx.get_offset());
158        let err = BuildError::with_pos(e.get_msg(), ErrorType::ParseError, position);
159        match e.get_cause() {
160            None => err,
161            Some(cause) => err.wrap_cause(Box::new(BuildError::from(cause))),
162        }
163    }
164}