use std::path::PathBuf;
use clap::{Parser, Subcommand};
use color_eyre::Result;
use crossterm::event::{self, Event};
use crossterm::style::force_color_output;
use jk_cli::{JjLog, JjLogCommand};
use jk_tui::log_view::{ActionResult, LogView};
mod key;
use key::AppKey;
#[derive(Debug, Parser)]
#[command(version, about)]
struct Args {
#[arg(short = 'R', long = "repository")]
repository: Option<PathBuf>,
#[arg(short = 'n', long)]
limit: Option<usize>,
#[command(subcommand)]
command: Option<Command>,
}
#[derive(Debug, Subcommand)]
enum Command {
Log(LogArgs),
}
#[derive(Debug, Parser)]
struct LogArgs {
#[arg(short = 'n', long)]
limit: Option<usize>,
}
fn main() -> Result<()> {
color_eyre::install()?;
tracing_subscriber::fmt::init();
let args = Args::parse();
let source = log_source(&args);
let entries = source.load()?;
let app = LogView::new(entries);
run_terminal(app, source)?;
Ok(())
}
fn log_source(args: &Args) -> JjLog {
let (command, limit) = match &args.command {
Some(Command::Log(log_args)) => (JjLogCommand::Log, log_args.limit.or(args.limit)),
None => (JjLogCommand::ConfiguredDefault, args.limit),
};
let source = JjLog::default().with_command(command).with_limit(limit);
if let Some(repository) = &args.repository {
source.with_repository(repository)
} else {
source
}
}
fn run_terminal(mut app: LogView, mut source: JjLog) -> Result<()> {
force_color_output(true);
let mut terminal = ratatui::try_init().inspect_err(|_| ratatui::restore())?;
let _terminal_restore = TerminalRestore;
let mut needs_redraw = true;
loop {
if needs_redraw {
terminal.draw(|frame| app.render(frame))?;
needs_redraw = false;
}
match event::read()? {
Event::Key(key) => {
let AppKey::Action(action) = AppKey::from_crossterm(key) else {
continue;
};
match app.apply(action) {
ActionResult::Refresh => refresh_log(&mut app, &source),
ActionResult::SwitchHome => {
switch_log_command(&mut app, &mut source, JjLogCommand::ConfiguredDefault);
}
ActionResult::SwitchLog => {
switch_log_command(&mut app, &mut source, JjLogCommand::Log);
}
ActionResult::Quit => break,
_ => {}
}
needs_redraw = true;
}
Event::Resize(_, _) => {
needs_redraw = true;
}
_ => {}
}
}
Ok(())
}
fn refresh_log(app: &mut LogView, source: &JjLog) {
match source.load() {
Ok(snapshot) => app.refresh(snapshot),
Err(error) => app.show_error(error.to_string()),
}
}
fn switch_log_command(app: &mut LogView, source: &mut JjLog, command: JjLogCommand) {
let next_source = source.clone().with_command(command);
match next_source.load() {
Ok(snapshot) => {
*source = next_source;
app.refresh(snapshot);
}
Err(error) => app.show_error(error.to_string()),
}
}
struct TerminalRestore;
impl Drop for TerminalRestore {
fn drop(&mut self) {
ratatui::restore();
}
}