Skip to main content

runmat_core/
error.rs

1use runmat_hir::SemanticError;
2use runmat_parser::SyntaxError;
3use runmat_runtime::RuntimeError;
4use runmat_vm::CompileError;
5
6use crate::telemetry::TelemetryFailureInfo;
7
8#[derive(Debug)]
9pub enum RunError {
10    Syntax(SyntaxError),
11    Semantic(SemanticError),
12    Compile(CompileError),
13    Runtime(RuntimeError),
14}
15
16impl std::fmt::Display for RunError {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            RunError::Syntax(err) => write!(f, "{err}"),
20            RunError::Semantic(err) => write!(f, "{err}"),
21            RunError::Compile(err) => write!(f, "{err}"),
22            RunError::Runtime(err) => write!(f, "{err}"),
23        }
24    }
25}
26
27impl std::error::Error for RunError {}
28
29impl From<SyntaxError> for RunError {
30    fn from(value: SyntaxError) -> Self {
31        RunError::Syntax(value)
32    }
33}
34
35impl From<SemanticError> for RunError {
36    fn from(value: SemanticError) -> Self {
37        RunError::Semantic(value)
38    }
39}
40
41impl From<CompileError> for RunError {
42    fn from(value: CompileError) -> Self {
43        RunError::Compile(value)
44    }
45}
46
47impl From<RuntimeError> for RunError {
48    fn from(value: RuntimeError) -> Self {
49        RunError::Runtime(value)
50    }
51}
52
53impl RunError {
54    pub fn telemetry_failure_info(&self) -> TelemetryFailureInfo {
55        match self {
56            RunError::Syntax(_err) => TelemetryFailureInfo {
57                stage: "parser".to_string(),
58                code: "RunMat:ParserError".to_string(),
59                has_span: true,
60                component: Some("unknown".to_string()),
61            },
62            RunError::Semantic(err) => TelemetryFailureInfo {
63                stage: "hir".to_string(),
64                code: err
65                    .identifier
66                    .clone()
67                    .unwrap_or_else(|| "RunMat:SemanticError".to_string()),
68                has_span: err.span.is_some(),
69                component: telemetry_component_for_identifier(err.identifier.as_deref()),
70            },
71            RunError::Compile(err) => TelemetryFailureInfo {
72                stage: "compile".to_string(),
73                code: err
74                    .identifier
75                    .clone()
76                    .unwrap_or_else(|| "RunMat:CompileError".to_string()),
77                has_span: err.span.is_some(),
78                component: telemetry_component_for_identifier(err.identifier.as_deref()),
79            },
80            RunError::Runtime(err) => runtime_error_telemetry_failure_info(err),
81        }
82    }
83}
84
85pub fn runtime_error_telemetry_failure_info(err: &RuntimeError) -> TelemetryFailureInfo {
86    let identifier = err
87        .identifier()
88        .map(|value| value.to_string())
89        .unwrap_or_else(|| "RunMat:RuntimeError".to_string());
90    TelemetryFailureInfo {
91        stage: "runtime".to_string(),
92        code: identifier.clone(),
93        has_span: err.span.is_some(),
94        component: telemetry_component_for_identifier(Some(identifier.as_str())),
95    }
96}
97
98fn telemetry_component_for_identifier(identifier: Option<&str>) -> Option<String> {
99    let lower = identifier?.to_ascii_lowercase();
100    if lower.contains("undefined") || lower.contains("name") || lower.contains("import") {
101        return Some("name_resolution".to_string());
102    }
103    if lower.contains("type") || lower.contains("dimension") || lower.contains("bounds") {
104        return Some("typecheck".to_string());
105    }
106    if lower.contains("cancel") || lower.contains("interrupt") {
107        return Some("cancellation".to_string());
108    }
109    if lower.contains("io") || lower.contains("filesystem") {
110        return Some("io".to_string());
111    }
112    if lower.contains("network") || lower.contains("timeout") {
113        return Some("network".to_string());
114    }
115    if lower.contains("internal") || lower.contains("panic") {
116        return Some("internal".to_string());
117    }
118    None
119}