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_ssh_url;
use tracing::instrument;
pub struct RemotePruneCommand {
pub remote: String,
}
impl Command for RemotePruneCommand {
#[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();
let remote = if self.remote.is_empty() { "origin" } else { self.remote.as_str() };
new_state = reducer(new_state, Action::SetOpStatus(Some(format!("remote prune {}…", remote))));
match git.remote_prune(&state.repo_path, remote) {
Ok(output) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("remote prune {} ok", remote)));
if !output.trim().is_empty() {
new_state = reducer(new_state, Action::AppendOpLog(output));
}
new_state = reducer(new_state, Action::SetFeedback(Some(format!("Pruned stale tracking on {}", remote))));
new_state = reducer(new_state, Action::SetRefreshing(true));
}
Err(e) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("remote prune error: {e}")));
new_state = reducer(new_state, Action::SetStatusError(Some(format!("remote prune error: {e}"))));
}
}
new_state = reducer(new_state, Action::SetOpStatus(None));
Ok(CommandResult::StateUpdate(new_state))
}
}
pub struct ShowRemoteUrlCommand {
pub remote: String,
}
impl Command for ShowRemoteUrlCommand {
#[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!("remote url {}…", self.remote))));
match git.remote_url(&state.repo_path, &self.remote) {
Ok(url) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("{} url: {}", self.remote, url)));
new_state = reducer(new_state, Action::SetFeedback(Some(format!("{} url: {}", self.remote, url))));
}
Err(e) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("remote url error: {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))
}
}
pub struct SetRemoteSshCommand {
pub remote: String,
}
impl Command for SetRemoteSshCommand {
#[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();
let remote = self.remote.as_str();
new_state = reducer(new_state, Action::SetOpStatus(Some(format!("ensure ssh remote {}…", remote))));
match git.remote_url(&state.repo_path, remote) {
Ok(url) => {
if url.starts_with("git@") || url.starts_with("ssh://") {
new_state = reducer(new_state, Action::AppendOpLog(format!("{} already SSH: {}", remote, url)));
new_state = reducer(new_state, Action::SetFeedback(Some(format!("{} already SSH", remote))));
} else if let Some(ssh_url) = to_ssh_url(&url) {
match git.set_remote_url(&state.repo_path, remote, &ssh_url) {
Ok(_) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("{} -> {}", url, ssh_url)));
new_state = reducer(new_state, Action::SetFeedback(Some(format!("Set {} to SSH", remote))));
}
Err(e) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("set-url error: {e}")));
new_state = reducer(new_state, Action::SetStatusError(Some(format!("set-url error: {e}"))));
}
}
} else {
new_state = reducer(new_state, Action::AppendOpLog(format!("cannot convert to ssh: {}", url)));
new_state = reducer(new_state, Action::SetStatusError(Some("cannot convert remote to ssh".into())));
}
}
Err(e) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("remote url error: {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))
}
}
pub struct AutoFetchCommand {
pub remote: String,
}
impl Command for AutoFetchCommand {
#[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();
let remote = if self.remote.is_empty() { "origin" } else { self.remote.as_str() };
new_state = reducer(new_state, Action::SetOpStatus(Some(format!("fetching {}…", remote))));
match git.fetch_dry_run(&state.repo_path, remote) {
Ok(output) => {
if !output.trim().is_empty() {
for line in output.lines() {
new_state = reducer(new_state, Action::AppendOpLog(line.to_string()));
}
} else {
new_state = reducer(new_state, Action::AppendOpLog(format!("fetch {}: up to date", remote)));
}
new_state = reducer(new_state, Action::SetRefreshing(true));
}
Err(e) => {
new_state = reducer(new_state, Action::AppendOpLog(format!("fetch error: {e}")));
new_state = reducer(new_state, Action::SetStatusError(Some(format!("fetch error: {e}"))));
}
}
new_state = reducer(new_state, Action::SetOpStatus(None));
Ok(CommandResult::StateUpdate(new_state))
}
}