1use std::rc::Rc;
2
3use crossterm::event;
4use lingora_core::prelude::*;
5use rat_event::{HandleEvent, Regular};
6use rat_text::HasScreenCursor;
7use ratatui::{DefaultTerminal, prelude::*};
8use ratatui_themes::ThemeName;
9
10use crate::{
11 args::TuiArgs,
12 error::TuiError,
13 pages::{AppView, AppViewState},
14 theme::LingoraTheme,
15 user_preferences::UserPreferences,
16};
17
18pub struct App {
32 audit_result: Rc<AuditResult>,
33 state: AppViewState,
34}
35
36impl App {
37 pub fn new(settings: LingoraToml, audit_result: AuditResult) -> Self {
43 let theme = LingoraTheme::new(ThemeName::Dracula, audit_result.workspace());
44 let audit_result = Rc::new(audit_result);
45 let state = AppViewState::new(&settings, theme, audit_result.clone());
46
47 Self {
48 audit_result,
49 state,
50 }
51 }
52
53 pub fn set_theme(mut self, theme: ThemeName) -> Self {
55 self.state.set_theme(theme);
56 self
57 }
58
59 pub fn run(&mut self, terminal: &mut DefaultTerminal) -> Result<(), TuiError> {
70 while self.state.is_running() {
71 terminal.draw(|frame| self.draw(frame))?;
72 self.handle_events()?;
73 }
74
75 Ok(())
76 }
77
78 fn draw(&mut self, frame: &mut Frame) {
79 let mut view = AppView::new(&self.audit_result);
80
81 frame.render_stateful_widget(&mut view, frame.area(), &mut self.state);
82 if let Some(cursor) = self.state.screen_cursor() {
83 frame.set_cursor_position(cursor);
84 }
85 }
86
87 fn handle_events(&mut self) -> Result<(), TuiError> {
88 let event = event::read()?;
89 self.state.handle(&event, Regular);
90 Ok(())
91 }
92}
93
94impl TryFrom<LingoraToml> for App {
95 type Error = TuiError;
96
97 fn try_from(settings: LingoraToml) -> Result<Self, Self::Error> {
98 let engine = AuditEngine::try_from(&settings)?;
99 let audit_result = engine.run()?;
100
101 Ok(App::new(settings, audit_result))
102 }
103}
104
105impl TryFrom<&TuiArgs> for App {
106 type Error = TuiError;
107
108 fn try_from(value: &TuiArgs) -> Result<Self, Self::Error> {
109 let settings = LingoraToml::try_from(value.core_args())?;
110 let theme = match value.theme() {
111 Some(theme) => theme,
112 None => UserPreferences::load().theme(),
113 };
114 Self::try_from(settings).map(|app| app.set_theme(theme))
115 }
116}