mod app;
mod conventional;
mod error;
mod jj;
mod keys;
mod text;
mod ui;
use std::env;
use anyhow::{Context, Result};
use clap::Parser;
use crossterm::event::{self, Event, KeyEventKind};
use app::App;
use error::XorcistError;
use jj::{JjRunner, fetch_graph_log, find_jj_repo};
#[derive(Parser, Debug)]
#[command(name = "xor", version, about)]
struct Args {
#[arg(short = 'n', long, default_value = "500")]
limit: usize,
#[arg(long)]
all: bool,
}
fn main() -> Result<()> {
let args = Args::parse();
let current_dir = env::current_dir().context("failed to get current directory")?;
let repo = find_jj_repo(¤t_dir).ok_or(XorcistError::NotInRepo)?;
let runner = JjRunner::new().with_work_dir(&repo.root);
if !runner.is_available() {
return Err(XorcistError::JjNotFound.into());
}
let limit = if args.all { None } else { Some(args.limit) };
let graph_log = fetch_graph_log(&runner, limit).context("failed to fetch jj log")?;
let repo_root_display = repo
.root
.file_name()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or_else(|| repo.root.to_string_lossy().to_string());
let mut app = App::new(graph_log, repo_root_display, runner);
app.set_log_limit(limit);
run_tui(app)
}
fn run_tui(mut app: App) -> Result<()> {
let mut terminal = ratatui::init();
let result = run_event_loop(&mut terminal, &mut app);
ratatui::restore();
result
}
fn run_event_loop(terminal: &mut ratatui::DefaultTerminal, app: &mut App) -> Result<()> {
loop {
terminal.draw(|frame| {
ui::render(frame, app);
})?;
if app.should_load_more() {
app.start_loading();
terminal.draw(|frame| {
ui::render(frame, app);
})?;
app.load_more_entries()
.context("failed to load more entries")?;
}
let event = event::read()?;
if let Event::Key(key) = &event
&& key.kind == KeyEventKind::Press
&& keys::dispatch_key_event(app, *key, &event)?
{
continue;
}
if app.should_quit {
break;
}
}
Ok(())
}