use crate::app::{actions::Action, state::AppState};
use crate::app::state::{RebaseAction as OldRebaseAction, RebaseEntry as OldRebaseEntry};
pub fn handle_rebase(mut state: AppState, action: &Action) -> Option<AppState> {
match action {
Action::LogToggleSelect => {
if let Some(commit) = state.commits.get(state.commit_selected) {
if state.log_selected_hashes.contains(&commit.hash) {
state.log_selected_hashes.remove(&commit.hash);
} else {
state.log_selected_hashes.insert(commit.hash.clone());
}
}
}
Action::RebaseSetBase(hash) => {
state.rebase_base = Some(hash.clone());
}
Action::StartInteractiveRebase(commits) => {
let entries = commits
.iter()
.map(|h| {
if let Some(c) = state.commits.iter().find(|c| c.hash == *h) {
crate::app::rebase::RebaseEntry::new(
c.hash.clone(),
c.message.clone(),
crate::app::rebase::RebaseAction::Pick,
)
} else {
crate::app::rebase::RebaseEntry::new(
h.clone(),
"(not loaded)".to_string(),
crate::app::rebase::RebaseAction::Pick,
)
}
})
.collect();
state.rebase_session = crate::app::rebase::RebaseSession {
phase: crate::app::rebase::RebasePhase::Planning,
entries,
cursor: 0,
todo_path: None,
base_commit: commits.first().cloned(),
use_root: false,
dirty: true,
};
state.rebase_editor_open = true;
}
Action::RebaseToggleUseRoot => {
state.rebase_use_root = !state.rebase_use_root;
state.rebase_dirty = true;
}
Action::RebaseCancel => {
state.rebase_editor_open = false;
state.rebase_session.entries.clear();
state.rebase_session.cursor = 0;
state.rebase_session.dirty = false;
state.rebase_session.phase = crate::app::rebase::RebasePhase::Planning;
}
Action::RebaseNext => {
state.rebase_session.move_cursor_down();
}
Action::RebasePrev => {
state.rebase_session.move_cursor_up();
}
Action::RebaseMoveUp => {
state.rebase_session.move_entry_up();
}
Action::RebaseMoveDown => {
state.rebase_session.move_entry_down();
}
Action::RebaseCycleAction => {
if let Some(entry) = state.rebase_session.entries.get_mut(state.rebase_session.cursor) {
entry.action = entry.action.cycle_next();
state.rebase_session.dirty = true;
}
}
Action::RebaseSetAction(action) => {
if let Some(entry) = state.rebase_session.entries.get_mut(state.rebase_session.cursor) {
entry.action = *action;
state.rebase_session.dirty = true;
}
}
Action::RebaseLoadTodo(lines) => {
let entries = lines
.iter()
.filter_map(|line| {
if line.trim_start().starts_with('#') {
return None;
}
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
let verb = parts[0];
let hash = parts[1].to_string();
let message = line
.split_once(parts[1])
.map(|(_, msg)| msg.trim().to_string())
.unwrap_or_default();
let action = match verb {
"pick" => crate::app::rebase::RebaseAction::Pick,
"reword" => crate::app::rebase::RebaseAction::Reword,
"edit" => crate::app::rebase::RebaseAction::Edit,
"squash" => crate::app::rebase::RebaseAction::Squash,
"fixup" => crate::app::rebase::RebaseAction::Fixup,
"drop" => crate::app::rebase::RebaseAction::Drop,
_ => crate::app::rebase::RebaseAction::Pick,
};
Some(crate::app::rebase::RebaseEntry::new(hash, message, action))
} else {
None
}
})
.collect();
state.rebase_session.entries = entries;
state.rebase_session.cursor = 0;
state.rebase_session.dirty = false;
state.rebase_session.phase = crate::app::rebase::RebasePhase::Recovery;
state.rebase_session.base_commit = state.rebase_session.entries.last().map(|e| e.hash.clone());
state.rebase_editor_open = true;
}
Action::RebaseSaveAndRun => {
state.rebase_editor_open = false;
state.rebase_dirty = false;
}
Action::SetRewordAuthorName(name) => {
state.reword_author_name = Some(name.clone());
}
Action::SetRewordAuthorEmail(email) => {
state.reword_author_email = Some(email.clone());
}
Action::FocusAndClearRewordAuthorName => {
state.reword_focus_field = crate::app::state::RewordField::AuthorName;
state.reword_author_name = Some(String::new());
}
Action::FocusAndClearRewordAuthorEmail => {
state.reword_focus_field = crate::app::state::RewordField::AuthorEmail;
state.reword_author_email = Some(String::new());
}
Action::SetRewordFocus(field) => {
state.reword_focus_field = *field;
}
Action::SetEditAuthorName(name) => {
state.edit_author_name = Some(name.clone());
}
Action::SetEditAuthorEmail(email) => {
state.edit_author_email = Some(email.clone());
}
Action::FocusEditMessage => {
state.edit_focus_field = crate::app::state::RewordField::Message;
}
Action::FocusEditAuthorName => {
state.edit_focus_field = crate::app::state::RewordField::AuthorName;
}
Action::FocusEditAuthorEmail => {
state.edit_focus_field = crate::app::state::RewordField::AuthorEmail;
}
Action::FocusAndClearEditAuthorName => {
state.edit_focus_field = crate::app::state::RewordField::AuthorName;
state.edit_author_name = Some(String::new());
}
Action::FocusAndClearEditAuthorEmail => {
state.edit_focus_field = crate::app::state::RewordField::AuthorEmail;
state.edit_author_email = Some(String::new());
}
Action::RebaseAbortFromBuilder => {
}
Action::ShowRebaseRecovery => {
state.rebase_recovery_open = true;
}
Action::HideRebaseRecovery => {
state.rebase_recovery_open = false;
}
_ => return None,
}
Some(state)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::app::state::{RebaseAction, RebaseEntry};
fn create_test_state() -> AppState {
let mut state = AppState::new();
state.repo_path = "/tmp/test".to_string();
state
}
#[test]
fn test_rebase_builder_creation() {
let mut state = create_test_state();
state.commits = vec![
crate::app::state::CommitEntry {
hash: "abc123".to_string(),
short_hash: "abc123".to_string(),
message: "Test commit".to_string(),
author: "Test".to_string(),
},
];
let commits = vec!["abc123".to_string()];
let action = Action::StartInteractiveRebase(commits);
let result = handle_rebase(state, &action);
assert!(result.is_some());
let new_state = result.unwrap();
assert!(new_state.rebase_editor_open);
assert_eq!(new_state.rebase_session.entries.len(), 1);
assert_eq!(new_state.rebase_session.entries[0].action, crate::app::rebase::RebaseAction::Pick);
}
#[test]
fn test_rebase_action_cycling() {
let mut state = create_test_state();
state.rebase_session.entries = vec![crate::app::rebase::RebaseEntry::new(
"abc123".to_string(),
"Test".to_string(),
crate::app::rebase::RebaseAction::Pick,
)];
state.rebase_session.cursor = 0;
let action = Action::RebaseCycleAction;
let result = handle_rebase(state, &action);
assert!(result.is_some());
let new_state = result.unwrap();
assert_eq!(new_state.rebase_session.entries[0].action, crate::app::rebase::RebaseAction::Reword);
let state2 = new_state;
let result2 = handle_rebase(state2, &action);
assert!(result2.is_some());
let new_state2 = result2.unwrap();
assert_eq!(new_state2.rebase_session.entries[0].action, crate::app::rebase::RebaseAction::Edit);
}
#[test]
fn test_rebase_entry_movement() {
let mut state = create_test_state();
state.rebase_session.entries = vec![
crate::app::rebase::RebaseEntry::new(
"abc123".to_string(),
"First".to_string(),
crate::app::rebase::RebaseAction::Pick,
),
crate::app::rebase::RebaseEntry::new(
"def456".to_string(),
"Second".to_string(),
crate::app::rebase::RebaseAction::Pick,
),
];
state.rebase_session.cursor = 1;
let action = Action::RebaseMoveUp;
let result = handle_rebase(state, &action);
assert!(result.is_some());
let new_state = result.unwrap();
assert_eq!(new_state.rebase_session.cursor, 0);
assert_eq!(new_state.rebase_session.entries[0].hash, "def456");
assert_eq!(new_state.rebase_session.entries[1].hash, "abc123");
let state2 = new_state;
let action2 = Action::RebaseMoveDown;
let result2 = handle_rebase(state2, &action2);
assert!(result2.is_some());
let new_state2 = result2.unwrap();
assert_eq!(new_state2.rebase_session.cursor, 1);
}
#[test]
fn test_rebase_load_todo() {
let mut state = create_test_state();
let lines = vec![
"pick abc123 First commit".to_string(),
"reword def456 Second commit".to_string(),
"drop ghi789 Third commit".to_string(),
];
let action = Action::RebaseLoadTodo(lines);
let result = handle_rebase(state, &action);
assert!(result.is_some());
let new_state = result.unwrap();
assert_eq!(new_state.rebase_session.entries.len(), 3);
assert_eq!(new_state.rebase_session.entries[0].action, crate::app::rebase::RebaseAction::Pick);
assert_eq!(new_state.rebase_session.entries[1].action, crate::app::rebase::RebaseAction::Reword);
assert_eq!(new_state.rebase_session.entries[2].action, crate::app::rebase::RebaseAction::Drop);
assert!(new_state.rebase_editor_open);
}
#[test]
fn test_rebase_cancel() {
let mut state = create_test_state();
state.rebase_editor_open = true;
state.rebase_session.entries = vec![crate::app::rebase::RebaseEntry::new(
"abc123".to_string(),
"Test".to_string(),
crate::app::rebase::RebaseAction::Pick,
)];
state.rebase_session.dirty = true;
let action = Action::RebaseCancel;
let result = handle_rebase(state, &action);
assert!(result.is_some());
let new_state = result.unwrap();
assert!(!new_state.rebase_editor_open);
assert!(new_state.rebase_session.entries.is_empty());
assert!(!new_state.rebase_dirty);
}
#[test]
fn test_rebase_navigation() {
let mut state = create_test_state();
state.rebase_session.entries = vec![
crate::app::rebase::RebaseEntry::new(
"abc123".to_string(),
"First".to_string(),
crate::app::rebase::RebaseAction::Pick,
),
crate::app::rebase::RebaseEntry::new(
"def456".to_string(),
"Second".to_string(),
crate::app::rebase::RebaseAction::Pick,
),
];
state.rebase_session.cursor = 0;
let action = Action::RebaseNext;
let result = handle_rebase(state, &action);
assert!(result.is_some());
let new_state = result.unwrap();
assert_eq!(new_state.rebase_session.cursor, 1);
let state2 = new_state;
let action2 = Action::RebasePrev;
let result2 = handle_rebase(state2, &action2);
assert!(result2.is_some());
let new_state2 = result2.unwrap();
assert_eq!(new_state2.rebase_session.cursor, 0);
}
#[test]
fn test_rebase_recovery_actions() {
let mut state = create_test_state();
state.rebase_recovery_open = false;
let action = Action::ShowRebaseRecovery;
let result = handle_rebase(state, &action);
assert!(result.is_some());
let new_state = result.unwrap();
assert!(new_state.rebase_recovery_open);
let state2 = new_state;
let action2 = Action::HideRebaseRecovery;
let result2 = handle_rebase(state2, &action2);
assert!(result2.is_some());
let new_state2 = result2.unwrap();
assert!(!new_state2.rebase_recovery_open);
}
}