crashdump_parser/
tui.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7//     http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::app::{App, AppResult};
16use crate::event::EventHandler;
17use crate::ui;
18use crossterm::event::DisableMouseCapture;
19use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen};
20use ratatui::backend::Backend;
21use ratatui::Terminal;
22use std::io;
23use std::panic;
24
25/// Representation of a terminal user interface.
26///
27/// It is responsible for setting up the terminal,
28/// initializing the interface and handling the draw events.
29#[derive(Debug)]
30pub struct Tui<B: Backend> {
31    /// Interface to the Terminal.
32    terminal: Terminal<B>,
33    /// Terminal event handler.
34    pub events: EventHandler,
35}
36
37impl<B: Backend> Tui<B> {
38    /// Constructs a new instance of [`Tui`].
39    pub fn new(terminal: Terminal<B>, events: EventHandler) -> Self {
40        Self { terminal, events }
41    }
42
43    /// Initializes the terminal interface.
44    ///
45    /// It enables the raw mode and sets terminal properties.
46    pub fn init(&mut self) -> AppResult<()> {
47        terminal::enable_raw_mode()?;
48        crossterm::execute!(io::stdout(), EnterAlternateScreen, DisableMouseCapture)?;
49
50        // Define a custom panic hook to reset the terminal properties.
51        // This way, you won't have your terminal messed up if an unexpected error happens.
52        let panic_hook = panic::take_hook();
53        panic::set_hook(Box::new(move |panic| {
54            Self::reset().expect("failed to reset the terminal");
55            panic_hook(panic);
56        }));
57
58        self.terminal.hide_cursor()?;
59        self.terminal.clear()?;
60        Ok(())
61    }
62
63    /// [`Draw`] the terminal interface by [`rendering`] the widgets.
64    ///
65    /// [`Draw`]: ratatui::Terminal::draw
66    /// [`rendering`]: crate::ui::render
67    pub fn draw(&mut self, app: &mut App) -> AppResult<()> {
68        self.terminal.draw(|frame| ui::render(app, frame))?;
69        Ok(())
70    }
71
72    /// Resets the terminal interface.
73    ///
74    /// This function is also used for the panic hook to revert
75    /// the terminal properties if unexpected errors occur.
76    fn reset() -> AppResult<()> {
77        terminal::disable_raw_mode()?;
78        crossterm::execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
79        Ok(())
80    }
81
82    /// Exits the terminal interface.
83    ///
84    /// It disables the raw mode and reverts back the terminal properties.
85    pub fn exit(&mut self) -> AppResult<()> {
86        Self::reset()?;
87        self.terminal.show_cursor()?;
88        Ok(())
89    }
90}