Skip to main content

qubit_retry/error/
attempt_panic.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Captured attempt panic information.
11//!
12
13use std::any::Any;
14use std::error::Error;
15use std::fmt;
16
17use serde::{
18    Deserialize,
19    Serialize,
20};
21
22/// Panic payload captured from an isolated attempt.
23///
24/// `AttemptPanic` stores a best-effort text message extracted from the panic
25/// payload. String payloads and `&'static str` payloads preserve their original
26/// text; all other payload types use a generic message.
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct AttemptPanic {
29    /// Human-readable panic message.
30    message: Box<str>,
31}
32
33impl AttemptPanic {
34    /// Creates captured panic information from a message.
35    ///
36    /// # Parameters
37    /// - `message`: Panic message to store.
38    ///
39    /// # Returns
40    /// A captured panic value.
41    #[inline]
42    pub fn new(message: &str) -> Self {
43        Self::from_string(message.to_string())
44    }
45
46    /// Creates captured panic information from an owned message.
47    ///
48    /// # Parameters
49    /// - `message`: Panic message to store.
50    ///
51    /// # Returns
52    /// A captured panic value.
53    #[inline]
54    pub(crate) fn from_string(message: String) -> Self {
55        Self {
56            message: message.into_boxed_str(),
57        }
58    }
59
60    /// Extracts captured panic information from a panic payload.
61    ///
62    /// # Parameters
63    /// - `payload`: Payload returned by `catch_unwind`.
64    ///
65    /// # Returns
66    /// A captured panic value with best-effort text.
67    pub(crate) fn from_payload(payload: Box<dyn Any + Send + 'static>) -> Self {
68        match payload.downcast::<String>() {
69            Ok(message) => Self::from_string(*message),
70            Err(payload) => match payload.downcast::<&'static str>() {
71                Ok(message) => Self::new(*message),
72                Err(_) => Self::new("attempt panicked with a non-string payload"),
73            },
74        }
75    }
76
77    /// Returns the captured panic message.
78    ///
79    /// # Returns
80    /// Panic message text.
81    #[inline]
82    pub fn message(&self) -> &str {
83        &self.message
84    }
85}
86
87impl fmt::Display for AttemptPanic {
88    /// Formats the captured panic message.
89    ///
90    /// # Parameters
91    /// - `f`: Formatter provided by the standard formatting machinery.
92    ///
93    /// # Returns
94    /// Formatting result.
95    ///
96    /// # Errors
97    /// Returns a formatting error if the formatter rejects output.
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        f.write_str(&self.message)
100    }
101}
102
103impl Error for AttemptPanic {}