Skip to main content

hdf5_pure_rust/
error.rs

1use std::fmt;
2use std::io;
3
4/// The main error type for hdf5-pure-rust.
5#[derive(Debug)]
6pub enum Error {
7    /// An I/O error occurred.
8    Io(io::Error),
9    /// Invalid HDF5 file format.
10    InvalidFormat(String),
11    /// Unsupported HDF5 feature or version.
12    Unsupported(String),
13    /// Other error.
14    Other(String),
15}
16
17impl fmt::Display for Error {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        match self {
20            Error::Io(e) => write!(f, "I/O error: {e}"),
21            Error::InvalidFormat(msg) => write!(f, "Invalid HDF5 format: {msg}"),
22            Error::Unsupported(msg) => write!(f, "Unsupported: {msg}"),
23            Error::Other(msg) => write!(f, "{msg}"),
24        }
25    }
26}
27
28impl std::error::Error for Error {
29    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
30        match self {
31            Error::Io(e) => Some(e),
32            _ => None,
33        }
34    }
35}
36
37impl From<io::Error> for Error {
38    fn from(e: io::Error) -> Self {
39        Error::Io(e)
40    }
41}
42
43/// Result type alias for hdf5-pure-rust.
44pub type Result<T> = std::result::Result<T, Error>;
45
46/// Registered error class metadata.
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct ErrorClass {
49    pub library: String,
50    pub class_name: String,
51    pub version: String,
52}
53
54/// Registered error message metadata.
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct ErrorMessage {
57    pub message: String,
58}
59
60/// One asynchronous event-set entry.
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct ErrorEvent {
63    pub request: String,
64    pub completed: bool,
65    pub error: Option<String>,
66}
67
68/// Lightweight HDF5 event-set analogue used for non-MPI async bookkeeping.
69#[derive(Debug, Clone, Default, PartialEq, Eq)]
70pub struct ErrorEventSet {
71    events: Vec<ErrorEvent>,
72    op_counter: u64,
73    insert_callback_registered: bool,
74    complete_callback_registered: bool,
75}
76
77/// One error stack entry.
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct ErrorStackEntry {
80    pub class_name: String,
81    pub major: String,
82    pub minor: String,
83    pub description: String,
84}
85
86/// Lightweight HDF5-style error stack.
87#[derive(Debug, Clone, Default, PartialEq, Eq)]
88pub struct ErrorStack {
89    entries: Vec<ErrorStackEntry>,
90    auto_enabled: bool,
91}
92
93impl ErrorClass {
94    /// Register an error class.
95    pub fn register_class(
96        library: impl Into<String>,
97        class_name: impl Into<String>,
98        version: impl Into<String>,
99    ) -> Self {
100        Self {
101            library: library.into(),
102            class_name: class_name.into(),
103            version: version.into(),
104        }
105    }
106
107    /// Internal register-class alias.
108    pub fn register_class_internal(
109        library: impl Into<String>,
110        class_name: impl Into<String>,
111        version: impl Into<String>,
112    ) -> Self {
113        Self::register_class(library, class_name, version)
114    }
115
116    /// Unregister an error class. The pure Rust value is consumed.
117    pub fn unregister_class(self) {}
118
119    /// Internal unregister-class alias.
120    pub fn unregister_class_internal(self) {}
121
122    /// Return this class name.
123    pub fn class_name(&self) -> &str {
124        &self.class_name
125    }
126}
127
128impl ErrorMessage {
129    /// Create an error message.
130    pub fn create_msg(message: impl Into<String>) -> Self {
131        Self {
132            message: message.into(),
133        }
134    }
135
136    /// Internal create-message alias.
137    pub fn create_msg_internal(message: impl Into<String>) -> Self {
138        Self::create_msg(message)
139    }
140
141    /// Return the message text.
142    pub fn get_msg(&self) -> &str {
143        &self.message
144    }
145
146    /// Internal message getter alias.
147    pub fn get_msg_internal(&self) -> &str {
148        self.get_msg()
149    }
150
151    /// Close an error message. The pure Rust value is consumed.
152    pub fn close_msg(self) {}
153
154    /// Internal close-message callback alias.
155    pub fn close_msg_cb(self) {}
156
157    /// Internal close-message alias.
158    pub fn close_msg_internal(self) {}
159
160    /// Internal free-message alias.
161    pub fn free_msg(self) {}
162}
163
164impl ErrorEvent {
165    /// Create an event-set entry.
166    pub fn event_new(request: impl Into<String>) -> Self {
167        Self {
168            request: request.into(),
169            completed: false,
170            error: None,
171        }
172    }
173
174    /// Free an event entry. The pure Rust value is consumed.
175    pub fn event_free(self) {}
176
177    /// Mark this event complete.
178    pub fn event_completed(&mut self) {
179        self.completed = true;
180    }
181
182    /// Record an event failure.
183    pub fn handle_fail(&mut self, error: impl Into<String>) {
184        self.completed = true;
185        self.error = Some(error.into());
186    }
187}
188
189impl ErrorEventSet {
190    /// Initialize event-set package support.
191    pub fn init_package() -> Self {
192        Self::default()
193    }
194
195    /// Terminate event-set package support.
196    pub fn term_package(&mut self) {
197        self.events.clear();
198    }
199
200    /// Create an event set.
201    pub fn create() -> Self {
202        Self::default()
203    }
204
205    /// Internal create alias.
206    pub fn create_internal() -> Self {
207        Self::create()
208    }
209
210    /// Internal close callback alias.
211    pub fn close_cb(self) {}
212
213    /// Internal close alias.
214    pub fn close_internal(self) {}
215
216    /// Append an event to the internal list.
217    pub fn list_append(&mut self, event: ErrorEvent) {
218        self.events.push(event);
219        self.op_counter = self.op_counter.saturating_add(1);
220    }
221
222    /// Return the event-list length.
223    pub fn list_count(&self) -> usize {
224        self.events.len()
225    }
226
227    /// Iterate over queued events.
228    pub fn list_iterate<F>(&self, mut callback: F)
229    where
230        F: FnMut(&ErrorEvent),
231    {
232        for event in &self.events {
233            callback(event);
234        }
235    }
236
237    /// Borrow queued events in insertion order.
238    pub fn events(&self) -> impl Iterator<Item = &ErrorEvent> {
239        self.events.iter()
240    }
241
242    /// Remove an event by index.
243    pub fn list_remove(&mut self, index: usize) -> Option<ErrorEvent> {
244        if index < self.events.len() {
245            Some(self.events.remove(index))
246        } else {
247            None
248        }
249    }
250
251    /// Insert a request.
252    pub fn insert(&mut self, request: impl Into<String>) {
253        self.insert_request(request);
254    }
255
256    /// Public request-insert alias.
257    pub fn insert_request(&mut self, request: impl Into<String>) {
258        self.insert_request_internal(request);
259    }
260
261    /// Internal request-insert alias.
262    pub fn insert_request_internal(&mut self, request: impl Into<String>) {
263        self.list_append(ErrorEvent::event_new(request));
264    }
265
266    /// Iterate over request names in insertion order.
267    pub fn requests(&self) -> impl Iterator<Item = &str> {
268        self.events.iter().map(|event| event.request.as_str())
269    }
270
271    /// Visit request names in insertion order.
272    pub fn get_requests_with<F>(&self, mut callback: F)
273    where
274        F: FnMut(&str),
275    {
276        for request in self.requests() {
277            callback(request);
278        }
279    }
280
281    /// Return request names in insertion order.
282    #[deprecated(note = "use requests() or get_requests_with() to avoid allocating a Vec<String>")]
283    pub fn get_requests(&self) -> Vec<String> {
284        self.requests().map(str::to_owned).collect()
285    }
286
287    /// Return number of events.
288    pub fn get_count(&self) -> usize {
289        self.events.len()
290    }
291
292    /// Return the total insert operation counter.
293    pub fn get_op_counter(&self) -> u64 {
294        self.op_counter
295    }
296
297    /// Complete all pending events and return the number completed.
298    pub fn wait(&mut self) -> usize {
299        let mut completed = 0;
300        for event in &mut self.events {
301            if !event.completed {
302                event.event_completed();
303                completed += 1;
304            }
305        }
306        completed
307    }
308
309    /// Cancel incomplete events and return the number canceled.
310    pub fn cancel(&mut self) -> usize {
311        let mut canceled = 0;
312        for event in &mut self.events {
313            if !event.completed {
314                event.completed = true;
315                event.error = Some("canceled".into());
316                canceled += 1;
317            }
318        }
319        canceled
320    }
321
322    /// Return whether any event has failed.
323    pub fn get_err_status(&self) -> bool {
324        self.events.iter().any(|event| event.error.is_some())
325    }
326
327    /// Return failed-event count.
328    pub fn get_err_count(&self) -> usize {
329        self.events
330            .iter()
331            .filter(|event| event.error.is_some())
332            .count()
333    }
334
335    /// Iterate over failed-event messages.
336    pub fn err_info(&self) -> impl Iterator<Item = &str> {
337        self.events
338            .iter()
339            .filter_map(|event| event.error.as_deref())
340    }
341
342    /// Visit failed-event messages.
343    pub fn get_err_info_with<F>(&self, mut callback: F)
344    where
345        F: FnMut(&str),
346    {
347        for error in self.err_info() {
348            callback(error);
349        }
350    }
351
352    /// Return failed-event messages.
353    #[deprecated(note = "use err_info() or get_err_info_with() to avoid allocating a Vec<String>")]
354    pub fn get_err_info(&self) -> Vec<String> {
355        self.err_info().map(str::to_owned).collect()
356    }
357
358    /// Internal close-failed callback alias.
359    pub fn close_failed_cb(&mut self) {
360        self.events.retain(|event| event.error.is_none());
361    }
362
363    /// Register whether an insert callback is installed on this event set.
364    pub fn register_insert_func(&mut self, registered: bool) {
365        self.insert_callback_registered = registered;
366    }
367
368    /// Register whether a completion callback is installed on this event set.
369    pub fn register_complete_func(&mut self, registered: bool) {
370        self.complete_callback_registered = registered;
371    }
372
373    /// Return whether an insert callback is installed.
374    pub fn has_insert_callback(&self) -> bool {
375        self.insert_callback_registered
376    }
377
378    /// Return whether a completion callback is installed.
379    pub fn has_complete_callback(&self) -> bool {
380        self.complete_callback_registered
381    }
382}
383
384impl ErrorStackEntry {
385    pub fn new(
386        class_name: impl Into<String>,
387        major: impl Into<String>,
388        minor: impl Into<String>,
389        description: impl Into<String>,
390    ) -> Self {
391        Self {
392            class_name: class_name.into(),
393            major: major.into(),
394            minor: minor.into(),
395            description: description.into(),
396        }
397    }
398
399    /// Copy a stack entry.
400    pub fn copy_stack_entry(&self) -> Self {
401        self.clone()
402    }
403
404    /// Set stack-entry fields.
405    pub fn set_stack_entry(
406        &mut self,
407        class_name: impl Into<String>,
408        major: impl Into<String>,
409        minor: impl Into<String>,
410        description: impl Into<String>,
411    ) {
412        *self = Self::new(class_name, major, minor, description);
413    }
414}
415
416impl ErrorStack {
417    /// Initialize error-stack support.
418    pub fn init() -> Self {
419        Self::default()
420    }
421
422    /// Internal package-initialization alias.
423    pub fn init_package() -> Self {
424        Self::init()
425    }
426
427    /// Terminate error-stack support by clearing this stack.
428    pub fn term_package(&mut self) {
429        self.entries.clear();
430    }
431
432    /// Prepare user callback state. This stack has no hidden callback payload.
433    pub fn user_cb_prepare(&self) -> bool {
434        true
435    }
436
437    /// Return a copy of the current stack.
438    pub fn get_current_stack(&self) -> Self {
439        self.clone()
440    }
441
442    /// Internal current-stack getter alias.
443    pub fn get_current_stack_internal(&self) -> Self {
444        self.get_current_stack()
445    }
446
447    /// Replace the current stack.
448    pub fn set_current_stack(&mut self, other: Self) {
449        *self = other;
450    }
451
452    /// Internal current-stack setter alias.
453    pub fn set_current_stack_internal(&mut self, other: Self) {
454        self.set_current_stack(other);
455    }
456
457    /// Close a stack. The pure Rust value is consumed.
458    pub fn close_stack(self) {}
459
460    /// Internal close-stack alias.
461    pub fn close_stack_internal(self) {}
462
463    /// Return number of entries.
464    pub fn get_num(&self) -> usize {
465        self.entries.len()
466    }
467
468    /// Internal number-of-entries alias.
469    pub fn get_num_internal(&self) -> usize {
470        self.get_num()
471    }
472
473    /// Push an entry.
474    pub fn push_stack(&mut self, entry: ErrorStackEntry) {
475        self.entries.push(entry);
476    }
477
478    /// Old public push alias.
479    pub fn push1(&mut self, entry: ErrorStackEntry) {
480        self.push_stack(entry);
481    }
482
483    /// Formatted push alias.
484    pub fn printf_stack(&mut self, entry: ErrorStackEntry) {
485        self.push_stack(entry);
486    }
487
488    /// Append another stack.
489    pub fn append_stack(&mut self, other: &Self) {
490        self.entries.extend(other.entries.iter().cloned());
491    }
492
493    /// Pop up to `count` entries.
494    pub fn pop(&mut self, count: usize) {
495        let keep = self.entries.len().saturating_sub(count);
496        self.entries.truncate(keep);
497    }
498
499    /// Internal pop alias.
500    pub fn pop_internal(&mut self, count: usize) {
501        self.pop(count);
502    }
503
504    /// Clear stack entries.
505    pub fn clear2(&mut self) {
506        self.entries.clear();
507    }
508
509    /// Old clear alias.
510    pub fn clear1(&mut self) {
511        self.clear2();
512    }
513
514    /// Clear-stack alias.
515    pub fn clear_stack(&mut self) {
516        self.clear2();
517    }
518
519    /// Internal clear-entries alias.
520    pub fn clear_entries(&mut self) {
521        self.clear2();
522    }
523
524    /// Destroy stack by clearing entries.
525    pub fn destroy_stack(&mut self) {
526        self.clear2();
527    }
528
529    /// Borrow stack entries from oldest to newest.
530    pub fn entries(&self) -> impl Iterator<Item = &ErrorStackEntry> {
531        self.entries.iter()
532    }
533
534    /// Write stack descriptions to a caller-provided formatter.
535    pub fn print_into<W>(&self, writer: &mut W) -> fmt::Result
536    where
537        W: fmt::Write + ?Sized,
538    {
539        for (index, entry) in self.entries.iter().enumerate() {
540            if index > 0 {
541                writer.write_char('\n')?;
542            }
543            writer.write_str(&entry.description)?;
544        }
545        Ok(())
546    }
547
548    /// Visit printable stack descriptions from oldest to newest.
549    pub fn print_with<F>(&self, mut callback: F)
550    where
551        F: FnMut(&str),
552    {
553        for entry in &self.entries {
554            callback(&entry.description);
555        }
556    }
557
558    /// Print stack to a string.
559    #[deprecated(note = "use print_into() or print_with() to avoid allocating a String")]
560    pub fn print(&self) -> String {
561        let mut output = String::new();
562        self.print_into(&mut output)
563            .expect("writing to String cannot fail");
564        output
565    }
566
567    /// Version-2 print alias.
568    #[deprecated(note = "use print_into() or print_with() to avoid allocating a String")]
569    pub fn print2(&self) -> String {
570        let mut output = String::new();
571        self.print_into(&mut output)
572            .expect("writing to String cannot fail");
573        output
574    }
575
576    /// Walk entries from oldest to newest.
577    pub fn walk<F>(&self, mut callback: F)
578    where
579        F: FnMut(&ErrorStackEntry),
580    {
581        for entry in &self.entries {
582            callback(entry);
583        }
584    }
585
586    /// Version-1 walk alias.
587    pub fn walk1<F>(&self, callback: F)
588    where
589        F: FnMut(&ErrorStackEntry),
590    {
591        self.walk(callback);
592    }
593
594    /// Internal walk alias.
595    pub fn walk_internal<F>(&self, callback: F)
596    where
597        F: FnMut(&ErrorStackEntry),
598    {
599        self.walk(callback);
600    }
601
602    /// Internal v1 walk-callback adapter.
603    pub fn walk1_cb<F>(&self, callback: F)
604    where
605        F: FnMut(&ErrorStackEntry),
606    {
607        self.walk(callback);
608    }
609
610    /// Internal v2 walk-callback adapter.
611    pub fn walk2_cb<F>(&self, callback: F)
612    where
613        F: FnMut(&ErrorStackEntry),
614    {
615        self.walk(callback);
616    }
617
618    /// Enable/disable default automatic error printing.
619    pub fn set_default_auto(&mut self, enabled: bool) {
620        self.auto_enabled = enabled;
621    }
622
623    /// Set automatic error printing.
624    pub fn set_auto(&mut self, enabled: bool) {
625        self.auto_enabled = enabled;
626    }
627
628    /// Version-1 automatic error printing setter.
629    pub fn set_auto1(&mut self, enabled: bool) {
630        self.set_auto(enabled);
631    }
632
633    /// Return automatic error printing state.
634    pub fn get_auto(&self) -> bool {
635        self.auto_enabled
636    }
637
638    /// Return default automatic error printing state.
639    pub fn get_default_auto_func(&self) -> bool {
640        self.auto_enabled
641    }
642
643    /// Version-1 automatic error printing getter.
644    pub fn get_auto1(&self) -> bool {
645        self.auto_enabled
646    }
647
648    /// Pause automatic error printing.
649    pub fn pause_stack(&mut self) {
650        self.auto_enabled = false;
651    }
652
653    /// Resume automatic error printing.
654    pub fn resume_stack(&mut self) {
655        self.auto_enabled = true;
656    }
657
658    /// Return the major message for the top entry.
659    pub fn get_major(&self) -> Option<&str> {
660        self.entries.last().map(|entry| entry.major.as_str())
661    }
662
663    /// Return the minor message for the top entry.
664    pub fn get_minor(&self) -> Option<&str> {
665        self.entries.last().map(|entry| entry.minor.as_str())
666    }
667}
668
669#[cfg(test)]
670mod error_stack_tests {
671    use super::*;
672
673    #[test]
674    fn error_stack_aliases_roundtrip() {
675        let class = ErrorClass::register_class("hdf5", "major", "1.0");
676        assert_eq!(class.class_name(), "major");
677        let msg = ErrorMessage::create_msg("minor");
678        assert_eq!(msg.get_msg(), "minor");
679        assert_eq!(msg.get_msg_internal(), "minor");
680
681        let mut stack = ErrorStack::init();
682        assert!(stack.user_cb_prepare());
683        stack.push_stack(ErrorStackEntry::new("major", "io", "open", "failed"));
684        assert_eq!(stack.get_num(), 1);
685        assert_eq!(stack.get_major(), Some("io"));
686        assert_eq!(stack.get_minor(), Some("open"));
687        let mut printed = String::new();
688        stack.print_into(&mut printed).unwrap();
689        assert_eq!(printed, "failed");
690
691        let mut walked = Vec::new();
692        stack.print_with(|description| walked.push(description.to_owned()));
693        assert_eq!(walked, vec!["failed"]);
694
695        let copy = stack.get_current_stack();
696        stack.append_stack(&copy);
697        assert_eq!(stack.get_num_internal(), 2);
698        stack.pop_internal(1);
699        assert_eq!(stack.get_num(), 1);
700        stack.set_auto1(true);
701        assert!(stack.get_auto1());
702        stack.pause_stack();
703        assert!(!stack.get_auto());
704        stack.resume_stack();
705        assert!(stack.get_default_auto_func());
706        stack.clear_stack();
707        assert_eq!(stack.get_num(), 0);
708    }
709
710    #[test]
711    #[allow(deprecated)]
712    fn error_stack_print_helpers_pin_streaming_and_allocating_output() {
713        let mut stack = ErrorStack::init();
714        let mut printed = String::from("prefix:");
715        stack.print_into(&mut printed).unwrap();
716        assert_eq!(printed, "prefix:");
717
718        stack.push_stack(ErrorStackEntry::new("hdf5", "major", "minor", "first"));
719        stack.printf_stack(ErrorStackEntry::new("hdf5", "major", "minor", "second"));
720
721        stack.print_into(&mut printed).unwrap();
722        assert_eq!(printed, "prefix:first\nsecond");
723
724        let mut descriptions = vec!["stale".to_string()];
725        stack.print_with(|description| descriptions.push(description.to_owned()));
726        assert_eq!(descriptions, vec!["stale", "first", "second"]);
727
728        assert_eq!(stack.print(), "first\nsecond");
729        assert_eq!(stack.print2(), "first\nsecond");
730    }
731
732    #[test]
733    fn error_event_set_aliases_roundtrip() {
734        let mut event = ErrorEvent::event_new("read");
735        assert!(!event.completed);
736        event.event_completed();
737        assert!(event.completed);
738
739        let mut failed = ErrorEvent::event_new("write");
740        failed.handle_fail("disk");
741
742        let mut set = ErrorEventSet::create();
743        set.list_append(event);
744        set.list_append(failed);
745        set.insert_request("flush");
746        assert_eq!(set.list_count(), 3);
747        assert_eq!(set.get_count(), 3);
748        assert_eq!(set.get_op_counter(), 3);
749        assert_eq!(
750            set.requests().collect::<Vec<_>>(),
751            vec!["read", "write", "flush"]
752        );
753        assert!(set.get_err_status());
754        assert_eq!(set.get_err_count(), 1);
755        assert_eq!(set.err_info().collect::<Vec<_>>(), vec!["disk"]);
756
757        let mut visited = Vec::new();
758        set.list_iterate(|event| visited.push(event.request.clone()));
759        assert_eq!(visited, vec!["read", "write", "flush"]);
760
761        assert_eq!(set.wait(), 1);
762        set.close_failed_cb();
763        let mut requests = Vec::new();
764        set.get_requests_with(|request| requests.push(request.to_owned()));
765        assert_eq!(requests, vec!["read", "flush"]);
766        assert_eq!(set.cancel(), 0);
767        assert_eq!(
768            set.list_remove(1).map(|event| event.request),
769            Some("flush".into())
770        );
771        set.term_package();
772        assert_eq!(set.requests().count(), 0);
773    }
774
775    #[test]
776    #[allow(deprecated)]
777    fn deprecated_allocating_error_wrappers_remain_callable() {
778        let mut set = ErrorEventSet::create();
779        set.insert_request("read");
780        assert_eq!(set.get_requests(), vec!["read"]);
781
782        let mut failed = ErrorEvent::event_new("write");
783        failed.handle_fail("disk");
784        set.list_append(failed);
785        assert_eq!(set.get_err_info(), vec!["disk"]);
786
787        let mut stack = ErrorStack::init();
788        stack.push_stack(ErrorStackEntry::new("major", "io", "open", "failed"));
789        assert_eq!(stack.print(), "failed");
790        assert_eq!(stack.print2(), "failed");
791    }
792}