use crate::app::{actions::Action, state::AppState};
pub fn handle(state: &mut AppState, action: &Action) -> bool {
match action {
Action::ShowRebaseTodo => {
state.show_rebase_todo = true;
state.rebase_todo_selected = 0;
state.command_palette = false;
state.rebase_todo_editing = false;
state.rebase_todo_edit_buffer.clear();
}
Action::HideRebaseTodo => {
state.show_rebase_todo = false;
state.rebase_todo_editing = false;
state.rebase_todo_edit_buffer.clear();
}
Action::RebaseTodoUp => {
if state.rebase_todo_selected > 0 {
state.rebase_todo_selected -= 1;
}
}
Action::RebaseTodoDown => {
let max_idx = state.rebase_todo.len().saturating_sub(1);
if state.rebase_todo_selected < max_idx {
state.rebase_todo_selected += 1;
}
}
Action::SetRebaseTodo(lines) => {
state.rebase_todo = lines.clone();
state.rebase_todo_selected = 0;
state.rebase_todo_original = lines.clone();
state.rebase_todo_dirty = false;
}
Action::SetRebaseTodoWithPath(lines, path) => {
state.rebase_todo = lines.clone();
state.rebase_todo_selected = 0;
state.rebase_todo_original = lines.clone();
state.rebase_todo_path = Some(path.clone());
state.rebase_todo_dirty = false;
}
Action::SetRebaseTodoPath(path) => {
state.rebase_todo_path = Some(path.clone());
}
Action::StartRebaseTodoEdit(idx, text) => {
state.rebase_todo_selected = (*idx).min(state.rebase_todo.len().saturating_sub(1));
state.rebase_todo_editing = true;
state.rebase_todo_edit_buffer = text.clone();
}
Action::RebaseTodoEditBuffer(text) => {
state.rebase_todo_editing = true;
state.rebase_todo_edit_buffer = text.clone();
}
Action::RebaseTodoToggleKind(idx, kind) => {
if let Some(line) = state.rebase_todo.get_mut(*idx) {
let mut parts = line.splitn(2, char::is_whitespace);
let _ = parts.next();
let tail = parts.next().unwrap_or("").trim_start();
let new_line = if tail.is_empty() {
kind.clone()
} else {
format!("{} {}", kind, tail)
};
*line = new_line;
state.rebase_todo_dirty = true;
}
}
Action::RebaseTodoRewriteLine(idx, text) => {
if let Some(line) = state.rebase_todo.get_mut(*idx) {
*line = text.clone();
state.rebase_todo_dirty = true;
}
state.rebase_todo_editing = false;
state.rebase_todo_edit_buffer.clear();
}
Action::SaveRebaseTodo => {
if let Some(path) = &state.rebase_todo_path {
match std::fs::write(path, state.rebase_todo.join("\n")) {
Ok(_) => {
state.rebase_todo_dirty = false;
}
Err(e) => {
tracing::error!(error = %e, "Failed to save rebase todo file");
}
}
}
}
_ => return false,
}
true
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_state() -> AppState {
let mut state = AppState::new();
state.repo_path = "/tmp/test".to_string();
state
}
#[test]
fn test_rebase_todo_navigation() {
let mut state = create_test_state();
state.rebase_todo = vec![
"pick abc123 First".to_string(),
"pick def456 Second".to_string(),
"pick ghi789 Third".to_string(),
];
state.rebase_todo_selected = 1;
let action = Action::RebaseTodoUp;
handle(&mut state, &action);
assert_eq!(state.rebase_todo_selected, 0);
let action = Action::RebaseTodoDown;
handle(&mut state, &action);
assert_eq!(state.rebase_todo_selected, 1);
handle(&mut state, &action);
assert_eq!(state.rebase_todo_selected, 2);
handle(&mut state, &action);
assert_eq!(state.rebase_todo_selected, 2); }
#[test]
fn test_rebase_todo_editing() {
let mut state = create_test_state();
state.rebase_todo = vec!["pick abc123 Test".to_string()];
let action = Action::StartRebaseTodoEdit(0, "pick abc123 Test".to_string());
handle(&mut state, &action);
assert!(state.rebase_todo_editing);
assert_eq!(state.rebase_todo_edit_buffer, "pick abc123 Test");
let action = Action::RebaseTodoEditBuffer("reword abc123 Modified".to_string());
handle(&mut state, &action);
assert_eq!(state.rebase_todo_edit_buffer, "reword abc123 Modified");
let action = Action::RebaseTodoRewriteLine(0, "reword abc123 Modified".to_string());
handle(&mut state, &action);
assert!(!state.rebase_todo_editing);
assert_eq!(state.rebase_todo[0], "reword abc123 Modified");
assert!(state.rebase_todo_dirty);
}
#[test]
fn test_rebase_todo_toggle_kind() {
let mut state = create_test_state();
state.rebase_todo = vec!["pick abc123 Test commit".to_string()];
let action = Action::RebaseTodoToggleKind(0, "reword".to_string());
handle(&mut state, &action);
assert_eq!(state.rebase_todo[0], "reword abc123 Test commit");
assert!(state.rebase_todo_dirty);
let action = Action::RebaseTodoToggleKind(0, "drop".to_string());
handle(&mut state, &action);
assert_eq!(state.rebase_todo[0], "drop abc123 Test commit");
}
#[test]
fn test_save_rebase_todo() {
let mut state = create_test_state();
let temp_file = std::env::temp_dir().join(format!("test_rebase_todo_{}", std::process::id()));
state.rebase_todo_path = Some(temp_file.to_string_lossy().to_string());
state.rebase_todo = vec![
"pick abc123 First".to_string(),
"pick def456 Second".to_string(),
];
state.rebase_todo_dirty = true;
let action = Action::SaveRebaseTodo;
handle(&mut state, &action);
assert!(!state.rebase_todo_dirty);
let content = std::fs::read_to_string(&temp_file).expect("Failed to read file");
assert_eq!(content, "pick abc123 First\npick def456 Second");
let _ = std::fs::remove_file(&temp_file);
}
#[test]
fn test_rebase_todo_show_hide() {
let mut state = create_test_state();
let action = Action::ShowRebaseTodo;
handle(&mut state, &action);
assert!(state.show_rebase_todo);
assert_eq!(state.rebase_todo_selected, 0);
assert!(!state.command_palette);
let action = Action::HideRebaseTodo;
handle(&mut state, &action);
assert!(!state.show_rebase_todo);
assert!(!state.rebase_todo_editing);
}
#[test]
fn test_set_rebase_todo() {
let mut state = create_test_state();
let lines = vec![
"pick abc123 First".to_string(),
"pick def456 Second".to_string(),
];
let action = Action::SetRebaseTodo(lines.clone());
handle(&mut state, &action);
assert_eq!(state.rebase_todo, lines);
assert_eq!(state.rebase_todo_original, lines);
assert_eq!(state.rebase_todo_selected, 0);
assert!(!state.rebase_todo_dirty);
}
}