eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
//! Validation functions for rebase operations.
//!
//! Ensures repository state is valid before performing rebase operations.

use crate::services::GitService;
use crate::app::rebase::{RebaseSession, RebasePhase};
use std::path::Path;

/// Errors that can occur during rebase validation
#[derive(Debug, thiserror::Error)]
pub enum RebaseValidationError {
    #[error("Repository has uncommitted changes")]
    UncommittedChanges,

    #[error("Repository is already in a rebase state")]
    RebaseInProgress,

    #[error("Invalid repository state: {0}")]
    InvalidRepositoryState(String),

    #[error("No commits to rebase")]
    NoCommitsToRebase,

    #[error("Invalid base commit: {0}")]
    InvalidBaseCommit(String),

    #[error("Session is in invalid state: {0}")]
    InvalidSessionState(String),
}

/// Validator for rebase operations
pub struct RebaseValidator;

impl RebaseValidator {
    /// Validate that the repository is in a clean state for rebase
    pub fn validate_repository_state(
        git_service: &GitService,
        repo_path: &str,
    ) -> Result<(), RebaseValidationError> {
        // Check for uncommitted changes
        let status = git_service
            .status_porcelain(repo_path)
            .map_err(|_| RebaseValidationError::InvalidRepositoryState("Cannot read status".to_string()))?;

        if !status.trim().is_empty() {
            return Err(RebaseValidationError::UncommittedChanges);
        }

        // Check if rebase is already in progress
        let git_dir = Path::new(repo_path).join(".git");
        if git_dir.join("rebase-merge").exists() || git_dir.join("rebase-apply").exists() {
            return Err(RebaseValidationError::RebaseInProgress);
        }

        Ok(())
    }

    /// Validate that a rebase session is in a valid state for the given operation
    pub fn validate_session_state(
        session: &RebaseSession,
        operation: &str,
    ) -> Result<(), RebaseValidationError> {
        match operation {
            "start_planning" => {
                // Can always start planning (even from empty state)
                Ok(())
            }
            "execute_rebase" => {
                if session.entries.is_empty() {
                    return Err(RebaseValidationError::NoCommitsToRebase);
                }
                if session.base_commit.is_none() && !session.use_root {
                    return Err(RebaseValidationError::InvalidBaseCommit("No base commit specified".to_string()));
                }
                if session.phase != RebasePhase::Planning {
                    return Err(RebaseValidationError::InvalidSessionState(
                        format!("Cannot execute from phase {:?}", session.phase)
                    ));
                }
                Ok(())
            }
            "continue_rebase" => {
                if session.phase != RebasePhase::Active && session.phase != RebasePhase::Conflict {
                    return Err(RebaseValidationError::InvalidSessionState(
                        format!("Cannot continue from phase {:?}", session.phase)
                    ));
                }
                Ok(())
            }
            "abort_rebase" => {
                if session.phase == RebasePhase::Planning {
                    return Err(RebaseValidationError::InvalidSessionState(
                        "Cannot abort planning phase".to_string()
                    ));
                }
                Ok(())
            }
            _ => Ok(()),
        }
    }

    /// Validate that all entries in the session are valid
    pub fn validate_session_entries(session: &RebaseSession) -> Result<(), RebaseValidationError> {
        for entry in &session.entries {
            if entry.hash.is_empty() {
                return Err(RebaseValidationError::InvalidSessionState("Entry has empty hash".to_string()));
            }
            if entry.hash.len() < 4 {
                return Err(RebaseValidationError::InvalidSessionState("Entry hash too short".to_string()));
            }
        }
        Ok(())
    }

    /// Comprehensive validation before starting a rebase
    pub fn validate_before_execution(
        git_service: &GitService,
        repo_path: &str,
        session: &RebaseSession,
    ) -> Result<(), RebaseValidationError> {
        // Validate repository state
        Self::validate_repository_state(git_service, repo_path)?;

        // Validate session state
        Self::validate_session_state(session, "execute_rebase")?;

        // Validate session entries
        Self::validate_session_entries(session)?;

        Ok(())
    }
}