doki_error/error/
mod.rs

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
11/// All result about tailwind
12pub type Result<T> = std::result::Result<T, DokiError>;
13/// Maybe have ast position
14pub type MaybeRanged = Option<Range<usize>>;
15/// Error type for all tailwind operators
16#[derive(Debug)]
17pub struct DokiError {
18    /// Actual error kind
19    pub kind: Box<DokiErrorKind>,
20    /// Error level for report
21    pub level: DiagnosticLevel,
22    /// File name where error occurred
23    pub file: Option<Url>,
24    /// Range offset where error occurred
25    pub range: Option<Range<usize>>,
26}
27
28/// Actual error data for the error
29#[derive(Debug)]
30pub enum DokiErrorKind {
31    /// The error type for I/O operations
32    IOError(std::io::Error),
33    /// The error type which is returned from formatting a message into a
34    /// stream.
35    FormatError(std::fmt::Error),
36    /// The error type which is
37    SyntaxError(String),
38    /// The error type which is
39    TypeMismatch(String),
40    /// The error type which is occurred at runtime
41    RuntimeError(String),
42    /// Runtime error when variable is undefined
43    UndefinedVariable {
44        /// The name of the undefined variable
45        name: String,
46    },
47    /// A forbidden cst_node encountered
48    Unreachable,
49    // #[error(transparent)]
50    // UnknownError(#[from] anyhow::Error),
51}
52
53impl DokiError {
54    /// Set a new url for the error
55    #[inline]
56    pub fn set_url(&mut self, url: Url) {
57        self.file = Some(url);
58    }
59    /// Set a local path for the error
60    #[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    /// Set a new range for the error
67    #[inline]
68    pub fn set_range(&mut self, start: usize, end: usize) {
69        self.range = Some(Range { start, end });
70    }
71    /// Constructor of [`NoteErrorKind::Unreachable`]
72    #[inline]
73    pub fn unreachable() -> Self {
74        Self { kind: Box::new(DokiErrorKind::Unreachable), level: DiagnosticLevel::None, file: None, range: None }
75    }
76
77    /// Constructor of [`NoteErrorKind::UndefinedVariable`]
78    #[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    /// Deprecated or obsolete code.
87    /// Clients are allowed to rendered diagnostics with this tag strike
88    /// through.
89    pub fn is_deprecated(&self) -> bool {
90        false
91    }
92    /// Unused or unnecessary code.
93    /// Clients are allowed to render diagnostics with this tag faded out
94    /// instead of having an error squiggle.
95    pub fn is_unnecessary(&self) -> bool {
96        false
97    }
98}
99
100macro_rules! error_msg {
101    ($name:ident => $t:ident) => {
102        /// Constructor of [`NoteErrorKind::$t`]
103        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}