use std::io;
use std::time::Duration;
use clap::Parser;
use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use log::info;
use ratatui::{backend::CrosstermBackend, Terminal};
use ratado::app::App;
use ratado::handlers::{handle_event, EventHandler};
use ratado::storage::{run_migrations, Database};
use ratado::ui;
#[derive(Parser)]
#[command(name = "ratado")]
#[command(version, about, long_about = None)]
struct Cli {}
const TICK_RATE: Duration = Duration::from_millis(250);
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Cli::parse();
tui_logger::init_logger(log::LevelFilter::Debug)?;
tui_logger::set_default_level(log::LevelFilter::Debug);
info!("Starting Ratado v{}", env!("CARGO_PKG_VERSION"));
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
let _ = restore_terminal();
original_hook(panic_info);
}));
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let db_path = Database::default_path()?;
info!("Opening database at {:?}", db_path);
let db = Database::open(&db_path).await?;
run_migrations(&db).await?;
let mut app = App::new(db).await?;
info!("App initialized with {} tasks", app.tasks.len());
let result = run_app(&mut terminal, &mut app).await;
restore_terminal()?;
if let Err(e) = result {
eprintln!("Error: {}", e);
std::process::exit(1);
}
info!("Ratado exited cleanly");
Ok(())
}
fn restore_terminal() -> io::Result<()> {
disable_raw_mode()?;
execute!(
io::stdout(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
Ok(())
}
async fn run_app<B: ratatui::backend::Backend>(
terminal: &mut Terminal<B>,
app: &mut App,
) -> Result<(), Box<dyn std::error::Error>>
where
<B as ratatui::backend::Backend>::Error: 'static,
{
let mut events = EventHandler::new(TICK_RATE);
loop {
terminal.draw(|frame| ui::draw(frame, app))?;
if let Some(event) = events.next().await {
if !handle_event(app, event).await? {
break;
}
}
if app.should_quit {
break;
}
}
Ok(())
}