1mod error_std;
2use std::{
3 convert::Infallible,
4 error::Error,
5 fmt::{self, Debug, Display, Formatter},
6 ops::Range,
7};
8use url::{ParseError, Url};
9use yggdrasil_shared::DiagnosticLevel;
10
11pub type Result<T> = std::result::Result<T, DokiError>;
13pub type MaybeRanged = Option<Range<usize>>;
15#[derive(Debug)]
17pub struct DokiError {
18 pub kind: Box<DokiErrorKind>,
20 pub level: DiagnosticLevel,
22 pub file: Option<Url>,
24 pub range: Option<Range<usize>>,
26}
27
28#[derive(Debug)]
30pub enum DokiErrorKind {
31 IOError(std::io::Error),
33 FormatError(std::fmt::Error),
36 SyntaxError(String),
38 TypeMismatch(String),
40 RuntimeError(String),
42 UndefinedVariable {
44 name: String,
46 },
47 Unreachable,
49 }
52
53impl DokiError {
54 #[inline]
56 pub fn set_url(&mut self, url: Url) {
57 self.file = Some(url);
58 }
59 #[inline]
61 #[cfg(any(unix, windows, target_os = "redox"))]
62 pub fn set_path(&mut self, url: &std::path::Path) -> Result<()> {
63 self.file = Some(Url::from_file_path(url)?);
64 Ok(())
65 }
66 #[inline]
68 pub fn set_range(&mut self, start: usize, end: usize) {
69 self.range = Some(Range { start, end });
70 }
71 #[inline]
73 pub fn unreachable() -> Self {
74 Self { kind: Box::new(DokiErrorKind::Unreachable), level: DiagnosticLevel::None, file: None, range: None }
75 }
76
77 #[inline]
79 pub fn undefined_variable(name: impl Into<String>) -> DokiError {
80 let kind = DokiErrorKind::UndefinedVariable { name: name.into() };
81 Self { kind: Box::new(kind), level: DiagnosticLevel::None, file: None, range: None }
82 }
83}
84
85impl DokiError {
86 pub fn is_deprecated(&self) -> bool {
90 false
91 }
92 pub fn is_unnecessary(&self) -> bool {
96 false
97 }
98}
99
100macro_rules! error_msg {
101 ($name:ident => $t:ident) => {
102 pub fn $name(msg: impl Into<String>) -> DokiError {
104 let kind = DokiErrorKind::$t(msg.into());
105 Self { kind: Box::new(kind), level: DiagnosticLevel::None, file: None, range: None }
106 }
107 };
108 ($($name:ident => $t:ident),+ $(,)?) => (
109 impl DokiError { $(error_msg!($name=>$t);)+ }
110 );
111}
112
113error_msg![
114 syntax_error => SyntaxError,
115 type_mismatch => TypeMismatch,
116 runtime_error => RuntimeError,
117];
118
119impl Error for DokiError {}
120
121impl Display for DokiError {
122 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
123 let path = match &self.file {
124 Some(s) => s.path(),
125 None => "<Anonymous>",
126 };
127 match &self.range {
128 Some(s) => writeln!(f, "at ({}, {}) of {}", s.start, s.end, path)?,
129 None => writeln!(f, "at {}", path)?,
130 }
131 write!(f, "{:indent$}{}", self.kind, indent = 4)
132 }
133}
134
135impl Display for DokiErrorKind {
136 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
137 match self {
138 Self::IOError(e) => {
139 write!(f, "{}", e)
140 }
141 Self::FormatError(e) => {
142 write!(f, "{}", e)
143 }
144 Self::SyntaxError(msg) => {
145 f.write_str("SyntaxError: ")?;
146 f.write_str(msg)
147 }
148 Self::TypeMismatch(msg) => {
149 f.write_str("TypeError: ")?;
150 f.write_str(msg)
151 }
152 Self::RuntimeError(msg) => {
153 f.write_str("RuntimeError: ")?;
154 f.write_str(msg)
155 }
156 Self::UndefinedVariable { name } => {
157 write!(f, "RuntimeError: Variable {} not found in scope", name)
158 }
159 Self::Unreachable => {
160 f.write_str("InternalError: ")?;
161 f.write_str("Entered unreachable code!")
162 }
163 }
164 }
165}