eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
mod helpers;
mod mapper;
#[cfg(test)]
mod mapper_tests;


use crate::components::ComponentManager;
use crate::commands::{
    Command, CommandResult,
};
use crate::errors::ApplicationError;
use super::{event_loop, AppState, Action};
use crate::services::GitService;
use crate::async_result::AsyncResult;
use crossterm::event::EventStream;
use notify::RecommendedWatcher;
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
use std::sync::Arc;
use tokio::sync::{mpsc, watch};

pub struct Application {
    pub(crate) state: AppState,
    pub(crate) component_manager: ComponentManager,
    pub(crate) git_service: Arc<GitService>,
    pub(crate) event_stream: EventStream,
    pub(crate) cancel_rx: watch::Receiver<bool>,
    pub(crate) async_tx: mpsc::UnboundedSender<AsyncResult>,
    pub(crate) async_rx: mpsc::UnboundedReceiver<AsyncResult>,
    pub(crate) _watcher: Option<RecommendedWatcher>, // Keep watcher alive
}

impl Application {
    pub fn new(
        state: AppState,
        component_manager: ComponentManager,
        git_service: Arc<GitService>,
        cancel_rx: watch::Receiver<bool>,
        async_tx: mpsc::UnboundedSender<AsyncResult>,
        async_rx: mpsc::UnboundedReceiver<AsyncResult>,
        watcher: Option<RecommendedWatcher>,
    ) -> Result<Self, ApplicationError> {
        let event_stream = EventStream::new();
        
        Ok(Self {
            state,
            component_manager,
            git_service,
            event_stream,
            cancel_rx,
            async_tx,
            async_rx,
            _watcher: watcher,
        })
    }
    
    pub async fn run(&mut self, terminal: &mut Terminal<CrosstermBackend<std::io::Stdout>>) -> Result<(), ApplicationError> {
        event_loop::run(self, terminal).await
    }
    
    /// Converts an action into a command for execution.
    pub(crate) fn action_to_command(&self, action: &Action) -> Option<Box<dyn Command>> {
        mapper::action_to_command(&self.state, action)
    }
    
    pub(crate) fn apply_command_result(&mut self, result: CommandResult) -> Result<(), ApplicationError> {
        match result {
            CommandResult::StateUpdate(new_state) => {
                self.state = new_state;
            }
            CommandResult::AsyncTask(_) => {
                // Async tasks are typically spawned by the command itself or handled by the event loop
                // If this variant reaches here, it might just need no state update
            }
            CommandResult::NoOp => {}
        }
        Ok(())
    }
    
    pub(crate) fn handle_async_result(&mut self, result: AsyncResult) -> Result<(), ApplicationError> {
        match result {
             AsyncResult::TaskComplete { message, should_refresh, .. } => {
                 self.state.feedback_message = Some(message);
                 if should_refresh {
                     // In a real implementation this would trigger refresh logic
                 }
             }
             AsyncResult::FileChanged => {
                 // Trigger status refresh handled by watcher usually, or set flag
             }
             AsyncResult::CommitDetailLoaded(detail) => {
                 self.state.commit_detail = Some(detail);
             }
             AsyncResult::CommitDetailError(err) => {
                 self.state.feedback_message = Some(format!("Error: {}", err));
             }
        }
        Ok(())
    }
}