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}