use crate::app::{Action, reducer, Application};
use crate::app::workflow::WorkflowState;
pub fn initialize_data_sources(app: &mut Application) {
let data_sources = [
("branches", Action::RefreshBranches),
("commits", Action::RefreshCommits),
("stashes", Action::RefreshStashes),
("tags", Action::RefreshTags),
("reflog", Action::RefreshReflog),
];
for (source_name, action) in data_sources {
if let Err(e) = app.component_manager.update(action, &mut app.state) {
tracing::warn!(
source = source_name,
error = %e,
"Failed to initialize {} during startup (non-critical, continuing)",
source_name
);
}
}
}
pub struct RebaseRecoveryInfo {
pub user_message: String,
pub log_message: String,
}
pub fn detect_rebase_recovery_state(_repo_path: &str) -> RebaseRecoveryInfo {
RebaseRecoveryInfo {
user_message: String::new(),
log_message: String::new(),
}
}
pub fn handle_rebase_recovery(app: &mut Application) {
if matches!(
app.state.workflow_context.as_ref().map(|ctx| ctx.state.clone()),
Some(WorkflowState::RebaseInProgress)
) {
let recovery_info = detect_rebase_recovery_state(&app.state.repo_path);
match app.git_service.read_rebase_todo(&app.state.repo_path) {
Ok((lines, _path)) => {
app.state = reducer(app.state.clone(), Action::RebaseLoadTodo(lines));
app.state = reducer(app.state.clone(), Action::SetFeedback(Some(recovery_info.user_message)));
app.state = reducer(app.state.clone(), Action::AppendOpLog(recovery_info.log_message));
}
Err(e) => {
tracing::warn!(error = %e, "Failed to load existing rebase todo on startup");
app.state = reducer(app.state.clone(), Action::SetFeedback(Some(recovery_info.user_message)));
app.state = reducer(app.state.clone(), Action::AppendOpLog(format!("Recovered rebase (todo load failed: {})", e)));
}
}
}
}
pub fn preflight_amend(state: &crate::app::AppState) -> Result<(), String> {
if let Some(ctx) = &state.workflow_context {
match ctx.state {
WorkflowState::RebaseInProgress
| WorkflowState::CherryPickInProgress
| WorkflowState::MergeInProgress
| WorkflowState::Conflicts => {
return Err("Cannot amend while rebase/cherry-pick/merge/conflicts are in progress".to_string());
}
_ => {}
}
}
let has_unstaged_or_conflict = state.status_entries.iter().any(|e| e.unstaged || e.conflict);
if has_unstaged_or_conflict {
return Err("Amend requires a clean working tree (no unstaged or conflict files)".to_string());
}
let has_untracked = state.status_entries.iter().any(|e| !e.staged && !e.unstaged && !e.conflict);
if has_untracked {
return Err("Amend requires no untracked files; clean or add them first".to_string());
}
Ok(())
}
pub fn preflight_pick_revert(state: &crate::app::AppState, op: &str) -> Result<(), String> {
if let Some(ctx) = &state.workflow_context {
match ctx.state {
WorkflowState::RebaseInProgress
| WorkflowState::CherryPickInProgress
| WorkflowState::MergeInProgress
| WorkflowState::Conflicts => {
return Err(format!("{op} blocked: rebase/cherry-pick/merge/conflicts in progress"));
}
_ => {}
}
}
if !state.status_entries.is_empty() {
return Err(format!("{op} requires a clean working tree (no staged/unstaged/untracked/conflict files)"));
}
Ok(())
}
pub fn validate_amend_input(state: &crate::app::AppState) -> Result<(), String> {
let has_message = !state.commit_input.trim().is_empty();
let has_name = !state.amend_author_name.trim().is_empty();
let has_email = !state.amend_author_email.trim().is_empty();
if !has_message && !has_name && !has_email {
return Ok(()); }
if has_name != has_email {
return Err("Author name and email must both be provided together, or leave both empty".to_string());
}
Ok(())
}