1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use std::{io, panic};

use anyhow::Result;
use crossterm::{
    event::DisableMouseCapture,
    terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::widgets::{Row, TableState};

use crate::{app::App, inputs::events::EventHandler, ui::render};

pub type CrosstermTerminal = ratatui::Terminal<ratatui::backend::CrosstermBackend<std::io::Stdout>>;

pub struct Tui {
    /// Interface to the terminal
    terminal: CrosstermTerminal,
    /// Terminal event handler
    events: EventHandler,
}

impl Tui {
    pub fn new(terminal: CrosstermTerminal, events: EventHandler) -> Self {
        Self { terminal, events }
    }

    /// Initialise the terminal interface
    pub fn enter(&mut self) -> Result<()> {
        terminal::enable_raw_mode()?;
        crossterm::execute!(io::stdout(), EnterAlternateScreen, DisableMouseCapture)?;

        let panic_hook = panic::take_hook();
        panic::set_hook(Box::new(move |panic| {
            Self::reset().expect("Failed to reset terminal");
            panic_hook(panic);
        }));

        self.terminal.hide_cursor()?;
        self.terminal.clear()?;
        Ok(())
    }
    /// Draw the terminal interface by rendering the widgets
    pub fn draw<'a>(
        &mut self,
        app: &mut App,
        body_left_state: &mut TableState,
        body_right_state: &mut TableState,
        diff_one_rows: &Vec<Row<'a>>,
        diff_two_rows: &Vec<Row<'a>>,
    ) -> Result<()> {
        self.terminal.draw(|rect| {
            render(
                rect,
                app,
                body_left_state,
                body_right_state,
                diff_one_rows,
                diff_two_rows,
            )
        })?;
        Ok(())
    }

    /// Resets the terminal interface.
    ///
    /// This function is also used for the panic hook to revert
    /// the terminal properties if unexpected errors occur.
    fn reset() -> Result<()> {
        terminal::disable_raw_mode()?;
        crossterm::execute!(io::stdout(), LeaveAlternateScreen)?;
        Ok(())
    }

    /// Exits the terminal interface.
    ///
    /// It disables the raw mode and reverts back the terminal properties.
    pub fn exit(&mut self) -> Result<()> {
        Self::reset()?;
        self.terminal.show_cursor()?;
        Ok(())
    }
}