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 {}