From 0e29fa02995273bfd803aea48773cbe52a7366ed Mon Sep 17 00:00:00 2001 From: Shav Kinderlehrer Date: Sat, 23 Mar 2024 13:08:14 -0400 Subject: [PATCH] Rework actions and events + start statusbar --- Cargo.lock | 251 ++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/app.rs | 63 ++++----- src/app_action.rs | 25 +++- src/app_event.rs | 6 +- src/components/global_keys.rs | 75 +++++----- src/components/mod.rs | 1 + src/components/status.rs | 74 ++++++++++ src/keys/key_commands.rs | 2 +- src/main.rs | 45 ++++++ src/tui.rs | 19 +++ 11 files changed, 474 insertions(+), 89 deletions(-) create mode 100644 src/components/status.rs diff --git a/Cargo.lock b/Cargo.lock index 90bbac0..cb6cf1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.8.11" @@ -26,6 +41,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -53,12 +83,45 @@ dependencies = [ "rustversion", ] +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "compact_str" version = "0.7.1" @@ -113,6 +176,21 @@ dependencies = [ "once_cell", ] +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "hashbrown" version = "0.14.3" @@ -129,6 +207,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indenter" version = "0.3.3" @@ -156,6 +244,12 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.153" @@ -187,6 +281,21 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.11" @@ -203,9 +312,20 @@ dependencies = [ name = "molehole" version = "0.1.0" dependencies = [ + "color-eyre", "crossterm", "eyre", "ratatui", + "url", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", ] [[package]] @@ -214,6 +334,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "parking_lot" version = "0.12.1" @@ -243,6 +369,18 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + [[package]] name = "proc-macro2" version = "1.0.78" @@ -290,6 +428,12 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustversion" version = "1.0.14" @@ -308,6 +452,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook" version = "0.3.17" @@ -404,12 +557,93 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -422,6 +656,23 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 140fd90..be10fce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +color-eyre = "0.6.2" crossterm = "0.27.0" eyre = "0.6.12" ratatui = "0.26.1" +url = "2.5.0" diff --git a/src/app.rs b/src/app.rs index 5e6ab1f..b9c3492 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,6 @@ use crossterm::event::Event; use eyre::Result; +use ratatui::prelude::*; use std::time::Duration; use crate::app_action::AppAction; @@ -8,7 +9,6 @@ use crate::component::Component; use crate::components; use crate::keys::key_commands::KeyCommand; use crate::tui; - pub struct App { pub tui: tui::Tui, pub tick_rate: Duration, @@ -22,50 +22,24 @@ impl App { pub fn new(tick_rate: Duration) -> Result { let tui = tui::init()?; - let key_commands = vec![ - KeyCommand { - key_code: "q".to_string(), - description: "Quit molehole".to_string(), - action: Some(AppAction::Quit), - }, - KeyCommand { - key_code: "g".to_string(), - description: "Scroll to top".to_string(), - action: None, - }, - KeyCommand { - key_code: "G".to_string(), - description: "Scroll to bottom".to_string(), - action: None, - }, - KeyCommand { - key_code: "k".to_string(), - description: "Scroll up one line".to_string(), - action: None, - }, - KeyCommand { - key_code: "j".to_string(), - description: "Scroll down one line".to_string(), - action: None, - }, - ]; - - let global_keys = components::global_keys::GlobalKeys { - key_commands: key_commands.clone(), - ..Default::default() - }; - Ok(Self { tui, tick_rate, - components: vec![Box::new(global_keys)], - key_commands, should_quit: false, + components: vec![], + key_commands: vec![], }) } pub fn run(&mut self) -> Result<()> { + let global_keys = components::global_keys::GlobalKeys { + key_commands: self.key_commands.clone(), + ..Default::default() + }; + let status_bar = components::status::StatusBar::default(); + self.components = vec![Box::new(global_keys), Box::new(status_bar)]; + for component in &mut self.components { component.init()?; } @@ -95,7 +69,7 @@ impl App { if let Some(event) = event { let mut actions: Vec = vec![]; for component in &mut self.components { - if let Some(action) = component.handle_event(event)? { + if let Some(action) = component.handle_event(event.clone())? { actions.push(action); } } @@ -110,9 +84,18 @@ impl App { } self.tui.draw(|frame| { - for component in &mut self.components { - let _ = component.render(frame, frame.size()); - } + let layout = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![ + Constraint::Percentage(100), + Constraint::Min(1), + ]) + .split(frame.size()); + + // status bar + let _ = self.components[1].render(frame, layout[1]); + // global_keys + let _ = self.components[0].render(frame, frame.size()); })?; Ok(()) diff --git a/src/app_action.rs b/src/app_action.rs index 93131ac..333e284 100644 --- a/src/app_action.rs +++ b/src/app_action.rs @@ -1,8 +1,25 @@ -#[derive(Default, Clone)] +use std::fmt; + +#[derive(Default, Clone, Debug)] pub enum AppAction { - StatusBarMessage(String), - StatusBarError(String), - StatusBarInput(String), + StatusBarGetInput(String), + StatusBarSetMessage(String), + StatusBarSetError(String), + OpenUrl, + + ScrollUp, + ScrollDown, + ScrollTop, + ScrollBottom, + + ShowHelpMenu, + #[default] Quit, } + +impl fmt::Display for AppAction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/src/app_event.rs b/src/app_event.rs index ee9d037..8413234 100644 --- a/src/app_event.rs +++ b/src/app_event.rs @@ -1,7 +1,11 @@ use crossterm::event::{KeyEvent, MouseEvent}; +use url::Url; -#[derive(Clone, Copy)] +#[derive(Clone)] pub enum AppEvent { Key(KeyEvent), Mouse(MouseEvent), + + StatusBarInput(String), + OpenUrl(Url), } diff --git a/src/components/global_keys.rs b/src/components/global_keys.rs index d0a5800..39ec1b7 100644 --- a/src/components/global_keys.rs +++ b/src/components/global_keys.rs @@ -24,60 +24,49 @@ pub struct GlobalKeys { impl Component for GlobalKeys { fn init(&mut self) -> eyre::Result<()> { - self.key_commands.append(&mut vec![KeyCommand { - key_code: "?".to_string(), - description: "Toggle help menu".to_string(), - action: None, - }]); - self.scroll_state = ScrollbarState::new(self.key_commands.len()).position(self.scroll); Ok(()) } + fn handle_action(&mut self, action: AppAction) { + match action { + AppAction::ScrollUp => { + if self.scroll > 0 { + self.scroll -= 1; + } + } + AppAction::ScrollDown => { + if self.scroll < self.key_commands.len() - 1 { + self.scroll += 1; + } + } + AppAction::ScrollTop => { + self.scroll = 0; + } + AppAction::ScrollBottom => { + self.scroll = self.key_commands.len() - 1; + } + AppAction::ShowHelpMenu => { + self.should_show = !self.should_show; + self.scroll = 0; + } + + _ => {} + } + self.scroll_state = self.scroll_state.position(self.scroll); + } + fn handle_key_event( &mut self, key: KeyEvent, ) -> eyre::Result> { if key.kind == KeyEventKind::Press { let key_event = serialize_key_event(key); - let eat_input = match key_event.as_str() { - "?" => { - self.should_show = !self.should_show; - self.scroll = 0; - true - } - "g" => { - self.scroll = 0; - true - } - "G" => { - self.scroll = self.key_commands.len() - 1; - true - } - "down" | "j" => { - if self.scroll < self.key_commands.len() - 1 { - self.scroll += 1; - } - true - } - "up" | "k" => { - if self.scroll > 0 { - self.scroll -= 1; - } - true - } - _ => false, - }; - self.scroll_state = self.scroll_state.position(self.scroll); - if eat_input && self.should_show { - return Ok(None); - } - for key_command in &mut self.key_commands { if key_command.key_code == key_event { - return Ok(key_command.action.clone()); + return Ok(Some(key_command.action.clone())); } } } @@ -108,7 +97,8 @@ impl Component for GlobalKeys { Title::from("Keyboard shortcuts").alignment(Alignment::Center), ) .borders(Borders::ALL) - .border_type(BorderType::Thick); + .border_type(BorderType::Thick) + .style(Style::default().bg(Color::DarkGray)); let mut lines: Vec = vec![]; for key_command in &mut self.key_commands { @@ -124,8 +114,7 @@ impl Component for GlobalKeys { let commands = Paragraph::new(lines) .block(block) .wrap(Wrap { trim: true }) - .scroll((u16::try_from(self.scroll)?, 0)) - .style(Style::default().bg(Color::DarkGray).fg(Color::White)); + .scroll((u16::try_from(self.scroll)?, 0)); if self.should_show { frame.render_widget(Clear, center); diff --git a/src/components/mod.rs b/src/components/mod.rs index 07fe4d8..a779415 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1 +1,2 @@ pub mod global_keys; +pub mod status; diff --git a/src/components/status.rs b/src/components/status.rs new file mode 100644 index 0000000..d91782f --- /dev/null +++ b/src/components/status.rs @@ -0,0 +1,74 @@ +use ratatui::prelude::*; +use ratatui::widgets::*; + +use crate::app_action::AppAction; +use crate::component::Component; +use crate::keys::key_commands::serialize_key_event; + +#[derive(Default, Clone)] +pub struct StatusBar { + message: String, + current_key: String, + error: bool, +} + +impl Component for StatusBar { + fn handle_key_event( + &mut self, + key: crossterm::event::KeyEvent, + ) -> eyre::Result> { + let key_str = serialize_key_event(key); + self.current_key = key_str; + + Ok(None) + } + + fn handle_action(&mut self, action: crate::app_action::AppAction) { + match action { + AppAction::StatusBarSetMessage(message) => { + self.error = false; + self.message = message; + } + AppAction::StatusBarSetError(message) => { + self.error = true; + self.message = message; + } + AppAction::StatusBarGetInput(_prompt) => todo!(), + _ => { + self.current_key += " "; + self.current_key += &action.to_string(); + } + } + } + + fn render( + &mut self, + frame: &mut ratatui::prelude::Frame, + rect: ratatui::prelude::Rect, + ) -> eyre::Result<()> { + let block = + Block::default().style(Style::default().bg(if self.error { + Color::Red + } else { + Color::DarkGray + })); + + let layout = Layout::default() + .direction(Direction::Horizontal) + .constraints(vec![ + Constraint::Percentage(50), + Constraint::Percentage(50), + ]) + .split(rect); + + let message = Paragraph::new(self.message.clone()).block(block.clone()); + let current_key = Paragraph::new(self.current_key.clone()) + .block(block) + .alignment(Alignment::Right); + + frame.render_widget(message, layout[0]); + frame.render_widget(current_key, layout[1]); + + Ok(()) + } +} diff --git a/src/keys/key_commands.rs b/src/keys/key_commands.rs index 27fe0ad..fc06286 100644 --- a/src/keys/key_commands.rs +++ b/src/keys/key_commands.rs @@ -6,7 +6,7 @@ use crate::app_action::AppAction; pub struct KeyCommand { pub key_code: String, pub description: String, - pub action: Option, + pub action: AppAction, } impl std::fmt::Display for KeyCommand { diff --git a/src/main.rs b/src/main.rs index ca87db6..33c2036 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,8 +8,53 @@ mod tui; use eyre::Result; +use app_action::AppAction; +use keys::key_commands::KeyCommand; + fn main() -> Result<()> { + tui::install_hooks()?; let mut app = app::App::new(std::time::Duration::from_millis(10))?; + let mut key_commands = vec![ + // Status bar + KeyCommand { + key_code: "o".to_string(), + description: "Open new link".to_string(), + action: AppAction::OpenUrl, + }, + + // Navigation + KeyCommand { + key_code: "g".to_string(), + description: "Scroll to top".to_string(), + action: AppAction::ScrollTop, + }, + KeyCommand { + key_code: "G".to_string(), + description: "Scroll to bottom".to_string(), + action: AppAction::ScrollBottom, + }, + KeyCommand { + key_code: "k".to_string(), + description: "Scroll up one line".to_string(), + action: AppAction::ScrollUp, + }, + KeyCommand { + key_code: "j".to_string(), + description: "Scroll down one line".to_string(), + action: AppAction::ScrollDown, + }, + KeyCommand { + key_code: "q".to_string(), + description: "Quit molehole".to_string(), + action: AppAction::Quit, + }, + KeyCommand { + key_code: "?".to_string(), + description: "Show help menu".to_string(), + action: AppAction::ShowHelpMenu + } + ]; + app.key_commands.append(&mut key_commands); app.run() } diff --git a/src/tui.rs b/src/tui.rs index 930f6e7..911a50d 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -10,6 +10,7 @@ use crossterm::{event, execute}; use ratatui::prelude::{CrosstermBackend, Terminal}; use std::io; use std::io::{stdout, Stdout}; +use std::panic; pub type Tui = Terminal>; @@ -41,3 +42,21 @@ pub fn get_event(tick: std::time::Duration) -> io::Result> { Ok(None) } + +pub fn install_hooks() -> eyre::Result<()> { + let hook_builder = color_eyre::config::HookBuilder::default(); + let (panic_hook, eyre_hook) = hook_builder.into_hooks(); + + let panic_hook = panic_hook.into_panic_hook(); + panic::set_hook(Box::new(move |panic_info| { + restore().unwrap(); + panic_hook(panic_info); + })); + + let eyre_hook = eyre_hook.into_eyre_hook(); + eyre::set_hook(Box::new(move |error| { + restore().unwrap(); + eyre_hook(error) + }))?; + Ok(()) +}