Skip to main content

eazygit/app/event_loop/
init.rs

1//! Initialization and helper functions for the event loop.
2
3use crate::app::{Action, reducer, Application};
4use crate::app::workflow::WorkflowState;
5
6/// Initialize all data sources during application startup.
7/// These operations are non-critical and failures are logged but don't block startup.
8pub fn initialize_data_sources(app: &mut Application) {
9    let data_sources = [
10        ("branches", Action::RefreshBranches),
11        ("commits", Action::RefreshCommits),
12        ("stashes", Action::RefreshStashes),
13        ("tags", Action::RefreshTags),
14        ("reflog", Action::RefreshReflog),
15    ];
16    
17    for (source_name, action) in data_sources {
18        if let Err(e) = app.component_manager.update(action, &mut app.state) {
19            tracing::warn!(
20                source = source_name,
21                error = %e,
22                "Failed to initialize {} during startup (non-critical, continuing)",
23                source_name
24            );
25        }
26    }
27}
28
29/// Information about a rebase recovery state.
30pub struct RebaseRecoveryInfo {
31    pub user_message: String,
32    pub log_message: String,
33}
34
35/// Detect detailed state of an interrupted rebase for recovery messaging.
36/// Stubbed - rebase feature removed.
37pub fn detect_rebase_recovery_state(_repo_path: &str) -> RebaseRecoveryInfo {
38    RebaseRecoveryInfo {
39        user_message: String::new(),
40        log_message: String::new(),
41    }
42}
43
44/// Handle rebase recovery on startup.
45pub fn handle_rebase_recovery(app: &mut Application) {
46    if matches!(
47        app.state.workflow_context.as_ref().map(|ctx| ctx.state.clone()),
48        Some(WorkflowState::RebaseInProgress)
49    ) {
50        let recovery_info = detect_rebase_recovery_state(&app.state.repo_path);
51        
52        match app.git_service.read_rebase_todo(&app.state.repo_path) {
53            Ok((lines, _path)) => {
54                app.state = reducer(app.state.clone(), Action::RebaseLoadTodo(lines));
55                app.state = reducer(app.state.clone(), Action::SetFeedback(Some(recovery_info.user_message)));
56                app.state = reducer(app.state.clone(), Action::AppendOpLog(recovery_info.log_message));
57            }
58            Err(e) => {
59                tracing::warn!(error = %e, "Failed to load existing rebase todo on startup");
60                app.state = reducer(app.state.clone(), Action::SetFeedback(Some(recovery_info.user_message)));
61                app.state = reducer(app.state.clone(), Action::AppendOpLog(format!("Recovered rebase (todo load failed: {})", e)));
62            }
63        }
64    }
65}
66
67/// Preflight check for amend operations.
68pub fn preflight_amend(state: &crate::app::AppState) -> Result<(), String> {
69    if let Some(ctx) = &state.workflow_context {
70        match ctx.state {
71            WorkflowState::RebaseInProgress
72            | WorkflowState::CherryPickInProgress
73            | WorkflowState::MergeInProgress
74            | WorkflowState::Conflicts => {
75                return Err("Cannot amend while rebase/cherry-pick/merge/conflicts are in progress".to_string());
76            }
77            _ => {}
78        }
79    }
80
81    let has_unstaged_or_conflict = state.status_entries.iter().any(|e| e.unstaged || e.conflict);
82    if has_unstaged_or_conflict {
83        return Err("Amend requires a clean working tree (no unstaged or conflict files)".to_string());
84    }
85
86    let has_untracked = state.status_entries.iter().any(|e| !e.staged && !e.unstaged && !e.conflict);
87    if has_untracked {
88        return Err("Amend requires no untracked files; clean or add them first".to_string());
89    }
90
91    Ok(())
92}
93
94/// Preflight check for cherry-pick and revert operations.
95pub fn preflight_pick_revert(state: &crate::app::AppState, op: &str) -> Result<(), String> {
96    if let Some(ctx) = &state.workflow_context {
97        match ctx.state {
98            WorkflowState::RebaseInProgress
99            | WorkflowState::CherryPickInProgress
100            | WorkflowState::MergeInProgress
101            | WorkflowState::Conflicts => {
102                return Err(format!("{op} blocked: rebase/cherry-pick/merge/conflicts in progress"));
103            }
104            _ => {}
105        }
106    }
107
108    if !state.status_entries.is_empty() {
109        return Err(format!("{op} requires a clean working tree (no staged/unstaged/untracked/conflict files)"));
110    }
111
112    Ok(())
113}
114
115/// Validate amend input before execution.
116pub fn validate_amend_input(state: &crate::app::AppState) -> Result<(), String> {
117    let has_message = !state.commit_input.trim().is_empty();
118    let has_name = !state.amend_author_name.trim().is_empty();
119    let has_email = !state.amend_author_email.trim().is_empty();
120    
121    if !has_message && !has_name && !has_email {
122        return Ok(()); // Allow empty - git will re-edit
123    }
124    
125    if has_name != has_email {
126        return Err("Author name and email must both be provided together, or leave both empty".to_string());
127    }
128    
129    Ok(())
130}
131