use crate::commands::{Command, CommandResult};
use crate::services::GitService;
use crate::app::{AppState, Action, reducer};
use crate::errors::CommandError;
use crate::commands::git_commands::utils::to_github_pr_url;
use std::process::Command as StdCommand;
use tracing::instrument;
pub struct PrListCommand {
pub remote: String,
}
impl Command for PrListCommand {
#[instrument(skip(self, git, state), fields(remote = %self.remote))]
fn execute(
&self,
git: &GitService,
state: &AppState,
) -> Result<CommandResult, CommandError> {
let mut new_state = state.clone();
new_state = reducer(new_state, Action::SetOpStatus(Some(format!("list PRs on {}…", self.remote))));
match git.list_pull_requests(&state.repo_path, &self.remote) {
Ok(list) => {
let mut pr_items = Vec::new();
for (num, refname) in list.into_iter().take(50) {
pr_items.push(crate::app::state::PrItem {
number: num.clone(),
title: format!("PR #{} ({})", num, refname),
_remote: self.remote.clone(),
_branch: format!("pr/{}", num),
});
}
new_state = reducer(new_state, Action::SetPrList(pr_items));
new_state = reducer(new_state, Action::ShowPrHelper);
new_state = reducer(new_state, Action::AppendOpLog(format!("Listed PRs from {}", self.remote)));
}
Err(e) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("pr list error: {e}")));
new_state = reducer(new_state, Action::SetStatusError(Some(format!("pr list error: {e}"))));
}
}
new_state = reducer(new_state, Action::SetOpStatus(None));
Ok(CommandResult::StateUpdate(new_state))
}
}
pub struct PrCheckoutCommand {
pub remote: String,
pub pr: String,
}
impl Command for PrCheckoutCommand {
#[instrument(skip(self, git, state), fields(remote = %self.remote, pr = %self.pr))]
fn execute(
&self,
git: &GitService,
state: &AppState,
) -> Result<CommandResult, CommandError> {
let mut new_state = state.clone();
new_state = reducer(new_state, Action::SetOpStatus(Some(format!("fetch PR {}…", self.pr))));
match git.fetch_pr(&state.repo_path, &self.remote, &self.pr) {
Ok(_) => {
match git.checkout_pr_branch(&state.repo_path, &self.pr) {
Ok(_) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("Checked out pr/{}", self.pr)));
new_state = reducer(new_state, Action::SetFeedback(Some(format!("Checked out pr/{}", self.pr))));
}
Err(e) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("checkout pr error: {e}")));
new_state = reducer(new_state, Action::SetStatusError(Some(format!("checkout pr error: {e}"))));
}
}
}
Err(e) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("fetch pr error: {e}")));
new_state = reducer(new_state, Action::SetStatusError(Some(format!("fetch pr error: {e}"))));
}
}
new_state = reducer(new_state, Action::SetOpStatus(None));
Ok(CommandResult::StateUpdate(new_state))
}
}
pub struct PrOpenCommand {
pub remote: String,
pub pr: String,
}
impl Command for PrOpenCommand {
#[instrument(skip(self, git, state), fields(remote = %self.remote, pr = %self.pr))]
fn execute(
&self,
git: &GitService,
state: &AppState,
) -> Result<CommandResult, CommandError> {
let mut new_state = state.clone();
new_state = reducer(new_state, Action::SetOpStatus(Some(format!("open PR {}…", self.pr))));
match git.remote_url(&state.repo_path, &self.remote) {
Ok(url) => {
if let Some(open_url) = to_github_pr_url(&url, &self.pr) {
let cmd = if cfg!(target_os = "macos") { "open" } else { "xdg-open" };
let status = StdCommand::new(cmd).arg(&open_url).status();
match status {
Ok(st) if st.success() => {
new_state = reducer(new_state, Action::AppendOpLog(format!("opened {}", open_url)));
}
Ok(st) => {
new_state = reducer(new_state, Action::SetStatusError(Some(format!("browser exit {}", st))));
}
Err(e) => {
new_state = reducer(new_state, Action::SetStatusError(Some(format!("browser error: {e}"))));
}
}
} else {
new_state = reducer(new_state, Action::SetStatusError(Some("unsupported remote for PR URL".into())));
}
}
Err(e) => {
new_state = reducer(new_state, Action::SetStatusError(Some(format!("remote url error: {e}"))));
}
}
new_state = reducer(new_state, Action::SetOpStatus(None));
Ok(CommandResult::StateUpdate(new_state))
}
}