1use std::cell::RefCell;
25use std::ffi::CString;
26use std::ptr;
27
28thread_local! {
29 static LAST_ERROR: RefCell<Option<String>> = const { RefCell::new(None) };
31
32 static ERROR_CSTRING: RefCell<Option<CString>> = const { RefCell::new(None) };
34}
35
36pub fn set_runtime_error(msg: impl Into<String>) {
40 ERROR_CSTRING.with(|cs| *cs.borrow_mut() = None);
42 LAST_ERROR.with(|e| {
43 *e.borrow_mut() = Some(msg.into());
44 });
45}
46
47pub fn take_runtime_error() -> Option<String> {
49 LAST_ERROR.with(|e| e.borrow_mut().take())
50}
51
52pub fn has_runtime_error() -> bool {
54 LAST_ERROR.with(|e| e.borrow().is_some())
55}
56
57pub fn clear_runtime_error() {
59 LAST_ERROR.with(|e| *e.borrow_mut() = None);
60 ERROR_CSTRING.with(|e| *e.borrow_mut() = None);
61}
62
63pub fn format_panic_payload(payload: &Box<dyn std::any::Any + Send>) -> String {
65 if let Some(s) = payload.downcast_ref::<&str>() {
66 s.to_string()
67 } else if let Some(s) = payload.downcast_ref::<String>() {
68 s.clone()
69 } else {
70 "unknown panic".to_string()
71 }
72}
73
74#[unsafe(no_mangle)]
78pub extern "C" fn patch_seq_has_error() -> bool {
79 has_runtime_error()
80}
81
82#[unsafe(no_mangle)]
91pub extern "C" fn patch_seq_get_error() -> *const i8 {
92 LAST_ERROR.with(|e| {
93 let error = e.borrow();
94 match &*error {
95 Some(msg) => {
96 ERROR_CSTRING.with(|cs| {
98 let safe_msg: String = msg
100 .chars()
101 .map(|c| if c == '\0' { '?' } else { c })
102 .collect();
103 let cstring = CString::new(safe_msg).expect("null bytes already replaced");
104 let ptr = cstring.as_ptr();
105 *cs.borrow_mut() = Some(cstring);
106 ptr
107 })
108 }
109 None => ptr::null(),
110 }
111 })
112}
113
114#[unsafe(no_mangle)]
123pub extern "C" fn patch_seq_take_error() -> *const i8 {
124 let msg = take_runtime_error();
125 match msg {
126 Some(s) => ERROR_CSTRING.with(|cs| {
127 let safe_msg: String = s.chars().map(|c| if c == '\0' { '?' } else { c }).collect();
129 let cstring = CString::new(safe_msg).expect("null bytes already replaced");
130 let ptr = cstring.as_ptr();
131 *cs.borrow_mut() = Some(cstring);
132 ptr
133 }),
134 None => ptr::null(),
135 }
136}
137
138#[unsafe(no_mangle)]
140pub extern "C" fn patch_seq_clear_error() {
141 clear_runtime_error();
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_set_and_take_error() {
150 clear_runtime_error();
151 assert!(!has_runtime_error());
152
153 set_runtime_error("test error");
154 assert!(has_runtime_error());
155
156 let error = take_runtime_error();
157 assert_eq!(error, Some("test error".to_string()));
158 assert!(!has_runtime_error());
159 }
160
161 #[test]
162 fn test_clear_error() {
163 set_runtime_error("another error");
164 assert!(has_runtime_error());
165
166 clear_runtime_error();
167 assert!(!has_runtime_error());
168 assert!(take_runtime_error().is_none());
169 }
170
171 #[test]
172 fn test_format_panic_payload() {
173 let payload: Box<dyn std::any::Any + Send> = Box::new("panic message");
174 assert_eq!(format_panic_payload(&payload), "panic message");
175
176 let payload: Box<dyn std::any::Any + Send> = Box::new("owned panic".to_string());
177 assert_eq!(format_panic_payload(&payload), "owned panic");
178 }
179}