mod cached;
mod contexts;
mod refresh;
use clap::{CommandFactory, Parser, Subcommand};
use contexts::configuration::application::config_service::ConfigService;
use contexts::configuration::infrastructure::toml_loader::TomlConfigRepository;
use contexts::merge_readiness::application::{
OutputToken,
errors::ErrorToken,
prompt::{ExecutionMode, RepoIdPort},
};
use contexts::merge_readiness::infrastructure::{gh::GhClient, logger::Logger};
use contexts::merge_readiness::interface::{
cli::prompt::{self, AFTER_HELP, PromptArgs},
presentation::PresentationConfigPort,
};
#[derive(Parser)]
#[command(
name = "merge-ready",
about = "PR merge status for your shell prompt",
version
)]
struct Cli {
#[command(subcommand)]
command: Option<Command>,
}
#[derive(Subcommand)]
enum Command {
#[command(after_help = AFTER_HELP)]
Prompt(PromptArgs),
Config {
#[command(subcommand)]
subcommand: ConfigCommand,
},
}
#[derive(Subcommand)]
enum ConfigCommand {
Edit,
Update,
}
struct InfraRepoIdPort;
impl RepoIdPort for InfraRepoIdPort {
fn get(&self) -> Option<String> {
contexts::merge_readiness::infrastructure::repo_id::get()
}
}
pub(crate) struct ConfigAdapter(ConfigService);
impl ConfigAdapter {
pub(crate) fn load() -> Self {
Self(ConfigService::new(&TomlConfigRepository))
}
}
impl PresentationConfigPort for ConfigAdapter {
fn render_token(&self, token: &OutputToken) -> String {
match token {
OutputToken::MergeReady => self.0.render_merge_ready(),
OutputToken::Conflict => self.0.render_conflict(),
OutputToken::UpdateBranch => self.0.render_update_branch(),
OutputToken::SyncUnknown => self.0.render_sync_unknown(),
OutputToken::CiFail => self.0.render_ci_fail(),
OutputToken::CiAction => self.0.render_ci_action(),
OutputToken::ReviewRequested => self.0.render_review(),
}
}
fn render_error_token(&self, token: ErrorToken) -> String {
match token {
ErrorToken::AuthRequired => self.0.render_auth_required(),
ErrorToken::RateLimited => self.0.render_rate_limited(),
ErrorToken::ApiError => self.0.render_api_error(),
}
}
}
fn main() {
let repo_id_port = InfraRepoIdPort;
match Cli::parse().command {
Some(Command::Prompt(args)) => {
let Some(mode) = prompt::resolve_mode(&args, &repo_id_port) else {
return;
};
match mode {
ExecutionMode::Direct => {
contexts::merge_readiness::interface::cli::prompt::direct::run(
&GhClient::new(),
&Logger,
ConfigAdapter::load(),
);
}
ExecutionMode::Cached => cached::run(),
ExecutionMode::BackgroundRefresh { repo_id } => refresh::run(&repo_id),
}
}
Some(Command::Config { subcommand }) => match subcommand {
ConfigCommand::Edit => {
let Some(path) =
contexts::configuration::infrastructure::toml_loader::config_path()
else {
eprintln!(
"failed to edit config: could not determine config path (HOME or XDG_CONFIG_HOME required)"
);
std::process::exit(1);
};
if let Err(e) = contexts::configuration::interface::cli::config::edit::run(&path) {
eprintln!("failed to edit config: {e}");
std::process::exit(1);
}
}
ConfigCommand::Update => {
if let Err(e) = contexts::configuration::interface::cli::config::update::run(
&TomlConfigRepository,
) {
eprintln!("failed to update config: {e}");
std::process::exit(1);
}
}
},
None => {
let _ = Cli::command().print_help();
}
}
}