eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
use crate::commands::{Command, CommandResult, CommandError};
use crate::app::AppState;
use crate::services::GitService;
use std::process::Command as StdCommand;
use tracing::instrument;

/// Execute a custom command from config
pub struct CustomCommand {
    pub name: String,
    pub command: String,
}

impl Command for CustomCommand {
    #[instrument(skip(self, _git, state), fields(name = %self.name, command = %self.command))]
    fn execute(
        &self,
        _git: &GitService,
        state: &AppState,
    ) -> Result<CommandResult, CommandError> {
        // Execute command in repo directory
        // Support shell commands (e.g., "git push && echo 'done'")
        let output = if cfg!(target_os = "windows") {
            StdCommand::new("cmd")
                .args(["/C", &self.command])
                .current_dir(&state.repo_path)
                .output()
        } else {
            StdCommand::new("sh")
                .args(["-c", &self.command])
                .current_dir(&state.repo_path)
                .output()
        };
        
        let new_state = match output {
            Ok(output) => {
                let stdout = String::from_utf8_lossy(&output.stdout);
                let stderr = String::from_utf8_lossy(&output.stderr);
                
                if output.status.success() {
                    let message = if !stdout.trim().is_empty() {
                        format!("{}: {}", self.name, stdout.trim())
                    } else {
                        format!("{} completed", self.name)
                    };
                    crate::app::reducer(state.clone(), crate::app::Action::SetFeedback(Some(message)))
                } else {
                    let error_msg = if !stderr.trim().is_empty() {
                        format!("{} failed: {}", self.name, stderr.trim())
                    } else {
                        format!("{} failed (exit code: {})", self.name, output.status.code().unwrap_or(-1))
                    };
                    crate::app::reducer(state.clone(), crate::app::Action::SetStatusError(Some(error_msg)))
                }
            }
            Err(e) => {
                crate::app::reducer(state.clone(), crate::app::Action::SetStatusError(Some(
                    format!("Failed to execute '{}': {}", self.name, e)
                )))
            }
        };
        
        Ok(CommandResult::StateUpdate(new_state))
    }
}