ocypode_lang/diagnostics/
mod.rs

1pub mod parser;
2pub mod runtime;
3use crate::errors::{Error, ErrorKind};
4use miette::{GraphicalReportHandler, JSONReportHandler};
5use std::{env, fmt::Display};
6
7/// A diagnostic struct, which is used to create a diagnostic.
8#[derive(Debug)]
9pub struct Diagnostic<T> {
10    pub diagnostic: Box<dyn miette::Diagnostic>,
11    pub handler: T,
12}
13
14impl<T: Default> Diagnostic<T> {
15    /// Create a new diagnostic.
16    pub fn new(diagnostic: Box<dyn miette::Diagnostic>) -> Self {
17        Self {
18            diagnostic,
19            handler: T::default(),
20        }
21    }
22
23    /// Create a RGB diagnostic.
24    pub fn rgb(self) -> Diagnostic<GraphicalReportHandler> {
25        Diagnostic {
26            diagnostic: self.diagnostic,
27            handler: GraphicalReportHandler::default()
28                .with_context_lines(3)
29                .with_theme(miette::GraphicalTheme {
30                    characters: miette::ThemeCharacters::emoji(),
31                    styles: matches!(env::var("NO_COLOR").as_deref(), Ok("1") | Ok("true"))
32                        .then(miette::ThemeStyles::none)
33                        .unwrap_or_else(miette::ThemeStyles::rgb),
34                }),
35        }
36    }
37}
38
39impl Diagnostic<GraphicalReportHandler> {
40    /// Display the diagnostic as a ASCII string
41    pub fn as_ascii(mut self) -> Self {
42        self.handler = self.handler.with_theme(miette::GraphicalTheme {
43            characters: miette::ThemeCharacters::ascii(),
44            styles: miette::ThemeStyles::ansi(),
45        });
46        self
47    }
48
49    /// Add a lines of context to the diagnostic. (the lines before and after the diagnostic)
50    pub fn with_context_lines(mut self, lines: usize) -> Self {
51        self.handler = self.handler.with_context_lines(lines);
52        self
53    }
54}
55
56impl Display for Diagnostic<GraphicalReportHandler> {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        self.handler.render_report(f, self.diagnostic.as_ref())
59    }
60}
61
62impl Display for Diagnostic<JSONReportHandler> {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        self.handler.render_report(f, self.diagnostic.as_ref())
65    }
66}
67
68pub fn as_diagnostic<T: Default>(err: Error, source: String, source_name: String) -> Diagnostic<T> {
69    match err.kind {
70        ErrorKind::InvalidName(ref name, ref reason, ref valid_name, ref statement_type) => {
71            Diagnostic::new(Box::new(parser::definitions::InvalidName {
72                src: miette::NamedSource::new(source_name, source),
73                name: name.clone(),
74                statement_type: statement_type.clone(),
75                reason: reason.clone(),
76                valid_name: valid_name.clone(),
77                span: err.span,
78            }))
79        }
80        ErrorKind::Parse(message) => Diagnostic::new(Box::new(parser::SyntaxError {
81            src: miette::NamedSource::new(source_name, source),
82            message,
83            span: err.span,
84        })),
85        ErrorKind::InvalidMainFunction(ref reason, ref help) => {
86            Diagnostic::new(Box::new(parser::definitions::InvalidMainFunction {
87                src: miette::NamedSource::new(source_name, source),
88                reason: reason.clone(),
89                help: help.clone(),
90                span: err.span,
91            }))
92        }
93        ErrorKind::UnDeclaredIdent(name) => {
94            Diagnostic::new(Box::new(runtime::idents::UnDeclaredIdent {
95                src: miette::NamedSource::new(source_name, source),
96                name,
97                span: err.span,
98            }))
99        }
100        ErrorKind::AlreadyDeclared(name, old_decl) => {
101            Diagnostic::new(Box::new(runtime::AlreadyDeclared {
102                src: miette::NamedSource::new(source_name, source),
103                name,
104                old_decl: old_decl.into(),
105                new_decl: err.span,
106            }))
107        }
108        ErrorKind::MissingMainFunction => {
109            Diagnostic::new(Box::new(runtime::functions::MissingMain {
110                src: miette::NamedSource::new(source_name, source),
111            }))
112        }
113        ErrorKind::InvalidExitCode(exit_code) => {
114            Diagnostic::new(Box::new(runtime::functions::InvalidExitCode {
115                src: miette::NamedSource::new(source_name, source),
116                code: exit_code,
117                span: err.span,
118            }))
119        }
120        ErrorKind::NotCallable(call_span) => {
121            Diagnostic::new(Box::new(runtime::idents::NotCallable {
122                src: miette::NamedSource::new(source_name, source),
123                call_span: call_span.into(),
124                span: err.span,
125            }))
126        }
127        ErrorKind::UncorrectArguments(args_count, func_span, params, func_name) => {
128            Diagnostic::new(Box::new(runtime::functions::UncorrectArguments {
129                src: miette::NamedSource::new(source_name, source),
130                args_count,
131                params: if !params.is_empty() {
132                    format!(
133                        "{} arguments, which are `{}`",
134                        params.len(),
135                        params
136                            .into_iter()
137                            .map(|p| p.ident.ident)
138                            .collect::<Vec<_>>()
139                            .join(", ")
140                    )
141                } else {
142                    "no arguments".to_owned()
143                },
144                func_span: func_span.into(),
145                func_name,
146                span: err.span,
147            }))
148        }
149        ErrorKind::UnexpectedType(expected, actual) => {
150            Diagnostic::new(Box::new(runtime::types::UnexpectedType {
151                src: miette::NamedSource::new(source_name, source),
152                expected,
153                actual,
154                span: err.span,
155            }))
156        }
157        ErrorKind::MultiplePackedParams(func_name) => {
158            Diagnostic::new(Box::new(parser::params::MultiplePackedParams {
159                src: miette::NamedSource::new(source_name, source),
160                func_name,
161                span: err.span,
162            }))
163        }
164        ErrorKind::PackedParamNotLast(param_name) => {
165            Diagnostic::new(Box::new(parser::params::PackedParamNotLast {
166                src: miette::NamedSource::new(source_name, source),
167                param_name,
168                span: err.span,
169            }))
170        }
171        ErrorKind::MultipleParamsWithTheSameName(param_name, func_name) => {
172            Diagnostic::new(Box::new(parser::params::MultipleParamsWithTheSameName {
173                src: miette::NamedSource::new(source_name, source),
174                param_name,
175                func_name,
176                span: err.span,
177            }))
178        }
179        ErrorKind::InvalidUnpackArg(type_name) => {
180            Diagnostic::new(Box::new(runtime::InvalidUnpackedArgument {
181                src: miette::NamedSource::new(source_name, source),
182                type_name,
183                span: err.span,
184            }))
185        }
186        ErrorKind::FormatError(reason, help_message) => {
187            Diagnostic::new(Box::new(runtime::FormatError {
188                src: miette::NamedSource::new(source_name, source),
189                reason,
190                help_message,
191                span: err.span,
192            }))
193        }
194        ErrorKind::Runtime(reason) => Diagnostic::new(Box::new(runtime::RuntimeError {
195            src: miette::NamedSource::new(source_name, source),
196            reason,
197            span: err.span,
198        })),
199    }
200}