eazygit/app/event_loop/
init.rs1use crate::app::{Action, reducer, Application};
4use crate::app::workflow::WorkflowState;
5
6pub 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
29pub struct RebaseRecoveryInfo {
31 pub user_message: String,
32 pub log_message: String,
33}
34
35pub fn detect_rebase_recovery_state(_repo_path: &str) -> RebaseRecoveryInfo {
38 RebaseRecoveryInfo {
39 user_message: String::new(),
40 log_message: String::new(),
41 }
42}
43
44pub 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
67pub 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
94pub 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
115pub 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(()); }
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