use anyhow::Result;
use event::{Event, EventBus};
use log::debug;
use main_screen::MainScreen;
use ratatui::prelude::*;
use simplelog::{LevelFilter, WriteLogger};
use std::{env, fs::File, io, rc::Rc};
use crate::{
auth_cache::{AuthCache, LoginDetails},
login_prompt::LoginPrompt,
};
pub mod auth_cache;
pub mod event;
pub mod login_prompt;
pub mod main_screen;
pub mod store;
pub mod styles;
pub mod tui;
pub fn main() -> Result<()> {
init_logging();
let mut terminal = Terminal::new(CrosstermBackend::new(io::stderr()))?;
tui::init(&mut terminal)?;
let res = run_in_terminal(&mut terminal);
debug!("exiting");
tui::exit(&mut terminal)?;
if let Err(e) = res {
println!("{}", e);
}
Ok(())
}
fn run_in_terminal<B: Backend>(terminal: &mut Terminal<B>) -> Result<()> {
let bus = Rc::new(EventBus::new());
bus.spawn_terminal_listener();
let app: Box<dyn Screen> = match AuthCache::load() {
Ok(a) => Box::new(MainScreen::new(
bus.clone(),
LoginDetails {
creds: a.creds,
remember: true,
},
)),
Err(_) => Box::new(LoginPrompt::new(bus.clone())),
};
main_loop(app, bus, terminal)
}
pub trait Screen {
fn draw(&mut self, frame: &mut Frame);
fn handle_event(&mut self, event: Event) -> Result<ExitState>;
}
pub enum ExitState {
Running,
Quit,
ChangeScreen(Box<dyn Screen>),
}
pub fn main_loop<B: Backend>(
mut app: Box<dyn Screen>,
bus: Rc<EventBus>,
terminal: &mut Terminal<B>,
) -> Result<()> {
loop {
let mut exit_state = ExitState::Running;
while matches!(exit_state, ExitState::Running) {
tui::draw(terminal, app.as_mut())?;
let next = bus.next()?;
debug!("received event {:?}", next);
exit_state = app.handle_event(next)?;
}
match exit_state {
ExitState::Quit => break,
ExitState::ChangeScreen(s) => app = s,
ExitState::Running => unreachable!(),
}
}
Ok(())
}
fn init_logging() {
if env::var("LEARN_TUI_LOG").is_ok() {
WriteLogger::init(
LevelFilter::Debug,
simplelog::Config::default(),
File::create(".learn-tui.log").unwrap(),
)
.unwrap();
}
}