1use std::convert::Infallible;
2use std::error::Error as StdError;
3use std::fmt;
4use std::io;
5use std::ops::Deref;
6use std::panic;
7use std::ptr::{self, addr_of_mut};
8
9use ndarray::ShapeError;
10
11#[cfg(not(feature = "1.10.0"))]
12use hdf5_sys::h5::hssize_t;
13use hdf5_sys::h5e::{
14 H5E_auto2_t, H5E_error2_t, H5Eget_current_stack, H5Eget_msg, H5Eprint2, H5Eset_auto2, H5Ewalk2,
15 H5E_DEFAULT, H5E_WALK_DOWNWARD,
16};
17
18use crate::internal_prelude::*;
19
20pub(crate) unsafe fn silence_errors_no_sync(silence: bool) {
25 let h5eprint: Option<unsafe extern "C" fn(hid_t, *mut libc::FILE) -> herr_t> =
28 Some(H5Eprint2 as _);
29 let h5eprint: H5E_auto2_t = std::mem::transmute(h5eprint);
30 H5Eset_auto2(H5E_DEFAULT, if silence { None } else { h5eprint }, ptr::null_mut());
31}
32
33pub fn silence_errors(silence: bool) {
35 h5lock!(silence_errors_no_sync(silence));
36}
37
38#[repr(transparent)]
40#[derive(Clone)]
41pub struct ErrorStack(Handle);
42
43impl ObjectClass for ErrorStack {
44 const NAME: &'static str = "errorstack";
45 const VALID_TYPES: &'static [H5I_type_t] = &[H5I_ERROR_STACK];
46
47 fn from_handle(handle: Handle) -> Self {
48 Self(handle)
49 }
50
51 fn handle(&self) -> &Handle {
52 &self.0
53 }
54
55 }
57
58impl ErrorStack {
59 pub(crate) fn from_current() -> Result<Self> {
60 let stack_id = h5lock!(H5Eget_current_stack());
61 Handle::try_new(stack_id).map(Self)
62 }
63
64 pub fn expand(self) -> Result<ExpandedErrorStack> {
68 struct CallbackData {
69 stack: ExpandedErrorStack,
70 err: Option<Error>,
71 }
72 unsafe extern "C" fn callback(
73 _: c_uint, err_desc: *const H5E_error2_t, data: *mut c_void,
74 ) -> herr_t {
75 panic::catch_unwind(|| unsafe {
76 let data = &mut *(data.cast::<CallbackData>());
77 if data.err.is_some() {
78 return 0;
79 }
80 let closure = |e: H5E_error2_t| -> Result<ErrorFrame> {
81 let (desc, func) = (string_from_cstr(e.desc), string_from_cstr(e.func_name));
82 let major = get_h5_str(|m, s| H5Eget_msg(e.maj_num, ptr::null_mut(), m, s))?;
83 let minor = get_h5_str(|m, s| H5Eget_msg(e.min_num, ptr::null_mut(), m, s))?;
84 Ok(ErrorFrame::new(&desc, &func, &major, &minor))
85 };
86 match closure(*err_desc) {
87 Ok(frame) => {
88 data.stack.push(frame);
89 }
90 Err(err) => {
91 data.err = Some(err);
92 }
93 }
94 0
95 })
96 .unwrap_or(-1)
97 }
98
99 let mut data = CallbackData { stack: ExpandedErrorStack::new(), err: None };
100 let data_ptr: *mut c_void = addr_of_mut!(data).cast::<c_void>();
101
102 let stack_id = self.handle().id();
103 h5lock!({
104 H5Ewalk2(stack_id, H5E_WALK_DOWNWARD, Some(callback), data_ptr);
105 });
106
107 data.err.map_or(Ok(data.stack), Err)
108 }
109}
110
111#[derive(Clone, Debug)]
113pub struct ErrorFrame {
114 desc: String,
115 func: String,
116 major: String,
117 minor: String,
118 description: String,
119}
120
121impl ErrorFrame {
122 pub(crate) fn new(desc: &str, func: &str, major: &str, minor: &str) -> Self {
123 Self {
124 desc: desc.into(),
125 func: func.into(),
126 major: major.into(),
127 minor: minor.into(),
128 description: format!("{func}(): {desc}"),
129 }
130 }
131
132 pub fn desc(&self) -> &str {
134 self.desc.as_ref()
135 }
136
137 pub fn description(&self) -> &str {
139 self.description.as_ref()
140 }
141
142 pub fn detail(&self) -> Option<String> {
145 Some(format!("Error in {}(): {} [{}: {}]", self.func, self.desc, self.major, self.minor))
146 }
147}
148
149#[derive(Clone, Debug)]
151pub struct ExpandedErrorStack {
152 frames: Vec<ErrorFrame>,
153 description: Option<String>,
154}
155
156impl Deref for ExpandedErrorStack {
157 type Target = [ErrorFrame];
158
159 fn deref(&self) -> &Self::Target {
160 &self.frames
161 }
162}
163
164impl Default for ExpandedErrorStack {
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170impl ExpandedErrorStack {
171 pub(crate) fn new() -> Self {
172 Self { frames: Vec::new(), description: None }
173 }
174
175 pub(crate) fn push(&mut self, frame: ErrorFrame) {
176 self.frames.push(frame);
177 if !self.is_empty() {
178 let top_desc = self.frames[0].description().to_owned();
179 if self.len() == 1 {
180 self.description = Some(top_desc);
181 } else {
182 self.description =
183 Some(format!("{}: {}", top_desc, self.frames[self.len() - 1].desc()));
184 }
185 }
186 }
187
188 pub fn top(&self) -> Option<&ErrorFrame> {
190 self.first()
191 }
192
193 pub fn description(&self) -> &str {
195 match self.description {
196 None => "unknown library error",
197 Some(ref desc) => desc.as_ref(),
198 }
199 }
200
201 pub fn detail(&self) -> Option<String> {
203 self.top().and_then(ErrorFrame::detail)
204 }
205}
206
207#[derive(Clone)]
209pub enum Error {
210 HDF5(ErrorStack),
212 Internal(String),
214}
215
216pub type Result<T, E = Error> = ::std::result::Result<T, E>;
219
220impl Error {
221 pub fn query() -> Result<Self> {
224 if let Ok(stack) = ErrorStack::from_current() {
225 Ok(Self::HDF5(stack))
226 } else {
227 Err(Self::Internal("Could not get errorstack".to_owned()))
228 }
229 }
230}
231
232impl From<&str> for Error {
233 fn from(desc: &str) -> Self {
234 Self::Internal(desc.into())
235 }
236}
237
238impl From<String> for Error {
239 fn from(desc: String) -> Self {
240 Self::Internal(desc)
241 }
242}
243
244impl From<Infallible> for Error {
245 fn from(_: Infallible) -> Self {
246 unreachable!("Infallible error can never be constructed")
247 }
248}
249
250impl fmt::Debug for Error {
251 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252 match *self {
253 Self::Internal(ref desc) => f.write_str(desc),
254 Self::HDF5(ref stack) => match stack.clone().expand() {
255 Ok(stack) => f.write_str(stack.description()),
256 Err(_) => f.write_str("Could not get error stack"),
257 },
258 }
259 }
260}
261
262impl fmt::Display for Error {
263 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264 match *self {
265 Self::Internal(ref desc) => f.write_str(desc),
266 Self::HDF5(ref stack) => match stack.clone().expand() {
267 Ok(stack) => f.write_str(stack.description()),
268 Err(_) => f.write_str("Could not get error stack"),
269 },
270 }
271 }
272}
273
274impl StdError for Error {}
275
276impl From<ShapeError> for Error {
277 fn from(err: ShapeError) -> Self {
278 format!("shape error: {err}").into()
279 }
280}
281
282impl From<Error> for io::Error {
283 fn from(err: Error) -> Self {
284 Self::new(io::ErrorKind::Other, err)
285 }
286}
287
288pub fn h5check<T: H5ErrorCode>(value: T) -> Result<T> {
289 H5ErrorCode::h5check(value)
290}
291
292#[allow(unused)]
293pub fn is_err_code<T: H5ErrorCode>(value: T) -> bool {
294 H5ErrorCode::is_err_code(value)
295}
296
297pub trait H5ErrorCode: Copy {
298 fn is_err_code(value: Self) -> bool;
299
300 fn h5check(value: Self) -> Result<Self> {
301 if Self::is_err_code(value) {
302 Err(Error::query().unwrap_or_else(|e| e))
303 } else {
304 Ok(value)
305 }
306 }
307}
308
309impl H5ErrorCode for hsize_t {
310 fn is_err_code(value: Self) -> bool {
311 value == 0
312 }
313}
314
315impl H5ErrorCode for herr_t {
316 fn is_err_code(value: Self) -> bool {
317 value < 0
318 }
319}
320
321#[cfg(feature = "1.10.0")]
322impl H5ErrorCode for hid_t {
323 fn is_err_code(value: Self) -> bool {
324 value < 0
325 }
326}
327
328#[cfg(not(feature = "1.10.0"))]
329impl H5ErrorCode for hssize_t {
330 fn is_err_code(value: Self) -> bool {
331 value < 0
332 }
333}
334
335impl H5ErrorCode for libc::ssize_t {
336 fn is_err_code(value: Self) -> bool {
337 value < 0
338 }
339}
340
341#[cfg(test)]
342pub mod tests {
343 use hdf5_sys::h5p::{H5Pclose, H5Pcreate};
344
345 use crate::globals::H5P_ROOT;
346 use crate::internal_prelude::*;
347
348 use super::ExpandedErrorStack;
349
350 #[test]
351 pub fn test_error_stack() {
352 let stack = h5lock!({
353 let plist_id = H5Pcreate(*H5P_ROOT);
354 H5Pclose(plist_id);
355 Error::query()
356 })
357 .unwrap();
358 let stack = match stack {
359 Error::HDF5(stack) => stack,
360 Error::Internal(internal) => panic!("Expected hdf5 error, not {}", internal),
361 }
362 .expand()
363 .unwrap();
364 assert!(stack.is_empty());
365
366 let stack = h5lock!({
367 let plist_id = H5Pcreate(*H5P_ROOT);
368 H5Pclose(plist_id);
369 H5Pclose(plist_id);
370 Error::query()
371 })
372 .unwrap();
373 let stack = match stack {
374 Error::HDF5(stack) => stack,
375 Error::Internal(internal) => panic!("Expected hdf5 error, not {}", internal),
376 }
377 .expand()
378 .unwrap();
379 assert_eq!(stack.description(), "H5Pclose(): can't close: can't locate ID");
380 assert_eq!(
381 &stack.detail().unwrap(),
382 "Error in H5Pclose(): can't close [Property lists: Unable to free object]"
383 );
384
385 assert!(stack.len() >= 2 && stack.len() <= 4); assert!(!stack.is_empty());
387
388 assert_eq!(stack[0].description(), "H5Pclose(): can't close");
389 assert_eq!(
390 &stack[0].detail().unwrap(),
391 "Error in H5Pclose(): can't close \
392 [Property lists: Unable to free object]"
393 );
394
395 #[cfg(not(feature = "1.14.0"))]
396 {
397 assert_eq!(stack[stack.len() - 1].description(), "H5I_dec_ref(): can't locate ID");
398 assert_eq!(
399 &stack[stack.len() - 1].detail().unwrap(),
400 "Error in H5I_dec_ref(): can't locate ID \
401 [Object atom: Unable to find atom information (already closed?)]"
402 );
403 }
404 #[cfg(feature = "1.14.0")]
405 {
406 assert_eq!(stack[stack.len() - 1].description(), "H5I__dec_ref(): can't locate ID");
407 assert_eq!(
408 &stack[stack.len() - 1].detail().unwrap(),
409 "Error in H5I__dec_ref(): can't locate ID \
410 [Object ID: Unable to find ID information (already closed?)]"
411 );
412 }
413
414 let empty_stack = ExpandedErrorStack::new();
415 assert!(empty_stack.is_empty());
416 assert_eq!(empty_stack.len(), 0);
417 }
418
419 #[test]
420 pub fn test_h5call() {
421 let result_no_error = h5call!({
422 let plist_id = H5Pcreate(*H5P_ROOT);
423 H5Pclose(plist_id)
424 });
425 assert!(result_no_error.is_ok());
426
427 let result_error = h5call!({
428 let plist_id = H5Pcreate(*H5P_ROOT);
429 H5Pclose(plist_id);
430 H5Pclose(plist_id)
431 });
432 assert!(result_error.is_err());
433 }
434
435 #[test]
436 pub fn test_h5try() {
437 fn f1() -> Result<herr_t> {
438 h5try!(H5Pcreate(*H5P_ROOT));
439 Ok(100)
440 }
441
442 assert_eq!(f1().unwrap(), 100);
443
444 fn f2() -> Result<herr_t> {
445 h5try!(H5Pcreate(123456));
446 Ok(100)
447 }
448
449 assert!(f2().is_err());
450 }
451}