visualpanic_rs/
lib.rs

1//! # VisualPanic
2//!
3//! Visualize panics with native GUI dialogs on supported systems (see list at https://crates.io/crates/native-dialog).
4//!
5//! Provides a solution to panic visually, useful for GUI applications where a console view might not be available at all times. Customizable in some ways, e.g., which icon, title and dialog level should be used.
6//!
7//! ## Example 1: Use the default settings and register for the whole application
8//! ```rust
9//! # use visualpanic_rs::VisualPanic;
10//! fn main() {
11//!     VisualPanic::default().register_global();
12//! }
13//! ```
14//!
15//! ## Example 2: Use custom settings and register for the whole application
16//! ```rust
17//! # use visualpanic_rs::VisualPanic;
18//! # use visualpanic_rs::VisualPanicLevel;
19//! fn main() {
20//!     VisualPanic::new(
21//!         Some("path/to/custom_icon.png"),
22//!         Some("Custom Title"),
23//!         Some(VisualPanicLevel::Info))
24//!     .register_global();
25//! }
26//! ```
27
28#![allow(warnings, unused)]
29#![feature(panic_info_message)]
30
31use serde::{Deserialize, Serialize};
32
33/// An enum stating the possible dialog levels
34#[derive(Clone, Debug, Copy, Eq, PartialOrd, PartialEq, Ord, Hash, Default, Serialize, Deserialize)]
35pub enum VisualPanicLevel {
36    /// Error level
37    #[default]
38    Error,
39    /// Warning level (
40    Warning,
41    /// Info level
42    Info
43}
44
45/// The struct containing information on the current VisualPanic settings.
46#[derive(Clone, Debug, Eq, PartialOrd, PartialEq, Ord, Hash, Default, Serialize, Deserialize)]
47pub struct VisualPanic {
48    /// <div class="warning">Currently not implemented!</div>
49    /// Option to set a custom icon to be used.
50    /// Value must be set to a valid path, e.g.,
51    /// ```rust
52    /// Some(String::from("path/to/icon.png"));
53    /// ```
54    custom_icon: Option<String>,
55    /// Option to set a custom title to be used.
56    /// Value can be set to any UTF-8 compliant [`String`], e.g.,
57    /// ```rust
58    /// Some(String::from("Custom String"));
59    /// ```
60    custom_title: Option<String>,
61    /// Option to set a custom dialog level.
62    /// Value can be one option of [`VisualPanicLevel`], e.g.,
63    /// ```rust
64    /// # use visualpanic_rs::VisualPanicLevel;
65    /// Some(VisualPanicLevel::Error);
66    /// ```
67    custom_level: Option<VisualPanicLevel>,
68}
69
70/// Provide public methods for [`VisualPanic`].
71impl VisualPanic {
72    /// Implements a default struct with all fields set to [`None`].
73    pub fn default() -> Self {
74        return VisualPanic {
75            custom_icon: None,
76            custom_title: None,
77            custom_level: None,
78        }
79    }
80
81    /// Implements a new struct with custom options.
82    /// The icon, title and level of the dialog can be set using [`Option<T>`], e.g.,
83    /// ```rust
84    /// # use visualpanic_rs::{VisualPanic, VisualPanicLevel};
85    /// let visual_panic_options: VisualPanic = VisualPanic::new(
86    ///     Some("path/to/custom_icon.png"),
87    ///     Some("Custom Title"),
88    ///     Some(VisualPanicLevel::Info)
89    /// );
90    /// ```
91    pub fn new(custom_icon: Option<&str>, custom_title: Option<&str>, custom_level: Option<VisualPanicLevel>) -> Self {
92        let mut return_val = VisualPanic{
93            custom_icon: None,
94            custom_title: None,
95            custom_level: None,
96        };
97        if let Some(icon_str) = custom_icon {
98            return_val.custom_icon = Some(String::from(icon_str));
99        }
100        if let Some(title_str) = custom_title {
101            return_val.custom_title = Some(String::from(title_str));
102        }
103        if let Some(custom_level) = custom_level {
104            return_val.custom_level = Some(custom_level);
105        }
106        return return_val;
107    }
108
109    /// Registers a [`VisualPanic`] globally, i.e., for the whole application.
110    /// Returns currently nothing.
111    /// Will panic, if handling the &[`std::panic::PanicInfo`] fails in any way or the native message dialog can not be spawned.
112    pub fn register_global(self) {
113
114        let clone = self.clone();
115
116        std::panic::set_hook(Box::new(move |panic_info| {
117            let mut level: native_dialog::MessageType = native_dialog::MessageType::Error;
118            let mut icon: Option<String> = None;
119            let mut title = String::from(env!("CARGO_PKG_NAME"));
120            let payload_as_str = panic_info.payload().downcast_ref::<&str>().expect("Failed to extract Panic Payload to &str.");
121            let location = panic_info.location().expect("Failed to get location where the panic occured.");
122            let message = panic_info.message().expect("Failed to extract message.").to_string();
123            let location_str = format!("File: {} at Line: {}, Column: {}", location.file(), location.line(), location.column());
124            let display_message = format!("An error occured.\n({})\n\nPayload: {}\n\nMessage: {}\n\nThe program will terminate.\n", location_str, payload_as_str, message);
125
126            match &clone.custom_title {
127                None => {},
128                Some(custom) => { title = custom.clone() }
129            }
130            match &clone.custom_level {
131                None => {},
132                Some(custom) => {
133                    match custom {
134                        VisualPanicLevel::Error => { level = native_dialog::MessageType::Error }
135                        VisualPanicLevel::Warning => { level = native_dialog::MessageType::Warning }
136                        VisualPanicLevel::Info => { level = native_dialog::MessageType::Info }
137                    }
138                }
139            }
140            match &clone.custom_icon {
141                None => {}
142                Some(custom) => { icon = Some(custom.clone()) }
143            }
144
145            let _ = native_dialog::MessageDialog::new()
146                .set_title(&title)
147                .set_type(level)
148                .set_text(&display_message)
149                .show_alert()
150                .expect("Failed to launch VisualPanic Dialog.");
151        }));
152    }
153}