runmat_runtime/
runtime_error.rs1pub use runmat_async::{
2 runtime_error as build_runtime_error, CallFrame, ErrorContext, GpuGatherRetry, RuntimeError,
3 RuntimeErrorBuilder,
4};
5
6pub fn semantic_error(identifier: &str, message: impl Into<String>) -> RuntimeError {
10 let suffix = identifier
11 .split_once(':')
12 .map_or(identifier, |(_, suffix)| suffix);
13 let namespace =
14 crate::context::legacy::error_namespace().unwrap_or_else(|| "RunMat".to_string());
15 build_runtime_error(message)
16 .with_identifier(format!("{namespace}:{suffix}"))
17 .build()
18}
19
20pub fn exception_from_error(error: &RuntimeError) -> runmat_value::MException {
25 if let Some(identifier) = error.identifier() {
26 return runmat_value::MException::new(identifier.to_string(), error.message().to_string());
27 }
28 let message = error.message();
29 if let Some(index) = message.rfind(": ") {
30 let (identifier, detail) = message.split_at(index);
31 return runmat_value::MException::new(
32 exception_identifier(identifier),
33 detail.trim_start_matches(':').trim().to_string(),
34 );
35 }
36 if let Some(index) = message.rfind(':') {
37 let (identifier, detail) = message.split_at(index);
38 return runmat_value::MException::new(
39 exception_identifier(identifier),
40 detail.trim_start_matches(':').trim().to_string(),
41 );
42 }
43 runmat_value::MException::new(exception_identifier(""), message.to_string())
44}
45
46fn exception_identifier(identifier: &str) -> String {
47 if identifier.trim().is_empty() {
48 let namespace =
49 crate::context::legacy::error_namespace().unwrap_or_else(|| "RunMat".to_string());
50 format!("{namespace}:error")
51 } else {
52 identifier.trim().to_string()
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum ReplayErrorKind {
58 UnsupportedSchema,
59 PayloadTooLarge,
60 DecodeFailed,
61 ExportRejected,
62 ImportRejected,
63}
64
65impl ReplayErrorKind {
66 pub fn identifier(self) -> &'static str {
67 match self {
68 Self::UnsupportedSchema => "RunMat:ReplayUnsupportedSchema",
69 Self::PayloadTooLarge => "RunMat:ReplayPayloadTooLarge",
70 Self::DecodeFailed => "RunMat:ReplayDecodeFailed",
71 Self::ExportRejected => "RunMat:ReplayExportRejected",
72 Self::ImportRejected => "RunMat:ReplayImportRejected",
73 }
74 }
75}
76
77pub fn replay_error(kind: ReplayErrorKind, message: impl Into<String>) -> RuntimeError {
78 build_runtime_error(message)
79 .with_builtin("replay")
80 .with_identifier(kind.identifier())
81 .build()
82}
83
84pub fn replay_error_with_source(
85 kind: ReplayErrorKind,
86 message: impl Into<String>,
87 source: impl std::error::Error + Send + Sync + 'static,
88) -> RuntimeError {
89 build_runtime_error(message)
90 .with_builtin("replay")
91 .with_identifier(kind.identifier())
92 .with_source(source)
93 .build()
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::context::{RuntimeContext, RuntimeContextGuard};
100 use crate::execution::RuntimeExecutionService;
101 use std::rc::Rc;
102
103 #[test]
104 fn semantic_errors_use_the_active_session_namespace() {
105 let context = RuntimeContext::new(Rc::new(RuntimeExecutionService::new()));
106 context.set_error_namespace("Acme");
107 let _scope = RuntimeContextGuard::enter(context);
108
109 assert_eq!(
110 semantic_error("MATLAB:badsubscript", "bad index").identifier(),
111 Some("Acme:badsubscript")
112 );
113 assert_eq!(
114 semantic_error("IndexOutOfBounds", "bad index").identifier(),
115 Some("Acme:IndexOutOfBounds")
116 );
117 }
118
119 #[test]
120 fn caught_exception_materialization_preserves_structured_and_legacy_errors() {
121 let structured = semantic_error("IndexOutOfBounds", "bad index");
122 let exception = exception_from_error(&structured);
123 assert_eq!(exception.identifier, "RunMat:IndexOutOfBounds");
124 assert_eq!(exception.message, "bad index");
125
126 let context = RuntimeContext::new(Rc::new(RuntimeExecutionService::new()));
127 context.set_error_namespace("Acme");
128 let _scope = RuntimeContextGuard::enter(context);
129 let legacy = crate::build_runtime_error("legacy detail").build();
130 let exception = exception_from_error(&legacy);
131 assert_eq!(exception.identifier, "Acme:error");
132 assert_eq!(exception.message, "legacy detail");
133 }
134}