Skip to main content

meta_language/translation/
diagnostics.rs

1//! Errors raised by the portable-core translator.
2//!
3//! `unsupported` errors are the precise obligations the issue requires: they name the construct, the
4//! source span, and the reason no faithful encoding exists yet.
5//!
6//! Mirrors `js/src/translation/diagnostics.js`; messages are identical.
7
8use std::fmt;
9
10use serde::{Deserialize, Serialize};
11
12use super::Span;
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub enum ErrorKind {
17    Syntax,
18    Type,
19    Unsupported,
20}
21
22impl ErrorKind {
23    #[must_use]
24    pub const fn as_str(self) -> &'static str {
25        match self {
26            Self::Syntax => "syntax",
27            Self::Type => "type",
28            Self::Unsupported => "unsupported",
29        }
30    }
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub struct TranslationError {
35    pub kind: ErrorKind,
36    /// The message without the span suffix.
37    pub reason: String,
38    pub span: Option<Span>,
39    /// The construct an `unsupported` error names.
40    pub construct: Option<String>,
41}
42
43impl TranslationError {
44    #[must_use]
45    pub fn new(kind: ErrorKind, reason: impl Into<String>, span: Option<Span>) -> Self {
46        Self {
47            kind,
48            reason: reason.into(),
49            span,
50            construct: None,
51        }
52    }
53
54    #[must_use]
55    pub fn syntax(reason: impl Into<String>, span: Option<Span>) -> Self {
56        Self::new(ErrorKind::Syntax, reason, span)
57    }
58
59    /// The message: the reason followed by the span, as `reason at 3..7`.
60    #[must_use]
61    pub fn message(&self) -> String {
62        self.to_string()
63    }
64}
65
66impl fmt::Display for TranslationError {
67    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match self.span {
69            Some(span) => write!(formatter, "{} at {}..{}", self.reason, span.start, span.end),
70            None => formatter.write_str(&self.reason),
71        }
72    }
73}
74
75impl std::error::Error for TranslationError {}
76
77/// A precise obligation for a construct outside the portable core.
78#[must_use]
79pub fn unsupported(construct: &str, reason: &str, span: Option<Span>) -> TranslationError {
80    TranslationError {
81        kind: ErrorKind::Unsupported,
82        reason: format!("{construct}: {reason}"),
83        span,
84        construct: Some(construct.to_owned()),
85    }
86}
87
88#[must_use]
89pub fn type_error(reason: impl Into<String>, span: Option<Span>) -> TranslationError {
90    TranslationError::new(ErrorKind::Type, reason, span)
91}
92
93pub type Result<T> = std::result::Result<T, TranslationError>;