pub mod color;
pub mod config;
pub mod git;
pub mod graph;
pub mod protocol;
mod app;
mod check;
mod event;
mod external;
mod keybind;
mod view;
mod widget;
use std::{path::Path, rc::Rc};
use app::{App, Ret};
use clap::{Parser, ValueEnum};
use graph::GraphImageManager;
use serde::Deserialize;
#[derive(Parser)]
#[command(version)]
struct Args {
#[arg(short = 'n', long, value_name = "NUMBER")]
max_count: Option<usize>,
#[arg(short, long, value_name = "TYPE")]
protocol: Option<ImageProtocolType>,
#[arg(short, long, value_name = "TYPE")]
order: Option<CommitOrderType>,
#[arg(short, long, value_name = "TYPE")]
graph_width: Option<GraphWidthType>,
#[arg(short = 's', long, value_name = "TYPE")]
graph_style: Option<GraphStyle>,
#[arg(short, long, value_name = "TYPE")]
initial_selection: Option<InitialSelection>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ImageProtocolType {
Auto,
Iterm,
Kitty,
}
impl From<Option<ImageProtocolType>> for protocol::ImageProtocol {
fn from(protocol: Option<ImageProtocolType>) -> Self {
match protocol {
Some(ImageProtocolType::Auto) => protocol::auto_detect(),
Some(ImageProtocolType::Iterm) => protocol::ImageProtocol::Iterm2,
Some(ImageProtocolType::Kitty) => protocol::ImageProtocol::Kitty,
None => protocol::auto_detect(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CommitOrderType {
Chrono,
Topo,
}
impl From<Option<CommitOrderType>> for git::SortCommit {
fn from(order: Option<CommitOrderType>) -> Self {
match order {
Some(CommitOrderType::Chrono) => git::SortCommit::Chronological,
Some(CommitOrderType::Topo) => git::SortCommit::Topological,
None => git::SortCommit::Chronological,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum GraphWidthType {
Auto,
Double,
Single,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum GraphStyle {
Rounded,
Angular,
}
impl From<Option<GraphStyle>> for graph::GraphStyle {
fn from(style: Option<GraphStyle>) -> Self {
match style {
Some(GraphStyle::Rounded) => graph::GraphStyle::Rounded,
Some(GraphStyle::Angular) => graph::GraphStyle::Angular,
None => graph::GraphStyle::Rounded,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum InitialSelection {
Latest,
Head,
}
impl From<Option<InitialSelection>> for app::InitialSelection {
fn from(selection: Option<InitialSelection>) -> Self {
match selection {
Some(InitialSelection::Latest) => app::InitialSelection::Latest,
Some(InitialSelection::Head) => app::InitialSelection::Head,
None => app::InitialSelection::Latest,
}
}
}
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub fn run() -> Result<()> {
let args = Args::parse();
let (core_config, ui_config, graph_config, color_theme, keybind_patch) = config::load()?;
let keybind = keybind::KeyBind::new(keybind_patch);
let max_count = args.max_count;
let image_protocol = args.protocol.or(core_config.option.protocol).into();
let order = args.order.or(core_config.option.order).into();
let graph_width = args.graph_width.or(core_config.option.graph_width);
let graph_style = args.graph_style.or(core_config.option.graph_style).into();
let initial_selection = args
.initial_selection
.or(core_config.option.initial_selection)
.into();
let graph_color_set = color::GraphColorSet::new(&graph_config.color);
let ctx = Rc::new(app::AppContext {
keybind,
core_config,
ui_config,
color_theme,
image_protocol,
});
let ec = event::EventController::init();
let mut refresh_view_context = None;
let mut terminal = None;
let ret = loop {
let repository = git::Repository::load(Path::new("."), order, max_count)?;
let graph = graph::calc_graph(&repository);
let cell_width_type = check::decide_cell_width_type(&graph, graph_width)?;
let graph_image_manager = GraphImageManager::new(
&graph,
&graph_color_set,
cell_width_type,
graph_style,
image_protocol,
);
if terminal.is_none() {
terminal = Some(ratatui::init());
}
let mut app = App::new(
&repository,
graph_image_manager,
&graph,
&graph_color_set,
cell_width_type,
initial_selection,
ctx.clone(),
&ec,
refresh_view_context,
);
match app.run(terminal.as_mut().unwrap()) {
Ok(Ret::Quit) => {
break Ok(());
}
Ok(Ret::Refresh(request)) => {
refresh_view_context = Some(request.context);
continue;
}
Err(e) => {
break Err(e);
}
}
};
ratatui::restore();
ret.map_err(Into::into)
}