sql-cli 1.73.1

SQL query tool for CSV/JSON with both interactive TUI and non-interactive CLI modes - perfect for exploration and automation
Documentation
# Redux Architecture Plan

## Current State (What we have now)

```rust
// In handle_results_input() and handle_command_input()
let action = self.key_mapper.map_key(key, &context);
if let Some(action) = action {
    self.try_handle_action(action, &context)  // Direct handling
}
```

The action system is working but still directly mutating state through method calls.

## Target State (Redux Pattern)

```rust
// Future: In handle_input()
let action = self.key_mapper.map_key(key, &context);
if let Some(action) = action {
    self.store.dispatch(action);  // Just dispatch, don't handle
}

// Somewhere else - the Store
impl Store {
    fn dispatch(&mut self, action: Action) {
        let old_state = self.state.clone();
        let new_state = reducer(&old_state, action);
        self.state = new_state;
        self.notify_subscribers();
    }
}

// Pure reducer function
fn reducer(state: &AppState, action: Action) -> AppState {
    match action {
        Action::Navigate(NavigateAction::Down(n)) => {
            let mut new_state = state.clone();
            new_state.navigation.selected_row += n;
            new_state
        }
        // ... handle all actions
    }
}

// TUI subscribes to state changes
impl EnhancedTuiApp {
    fn on_state_change(&mut self, new_state: &AppState) {
        // Update UI based on new state
        self.render();
    }
}
```

## Benefits of This Architecture

1. **Pure Functions**: Reducers are pure - same input always produces same output
2. **Testable**: Can test state transitions without UI
3. **Time Travel**: Can implement undo/redo by storing state history
4. **Debugging**: Can log every action and state change
5. **Predictable**: State changes only through actions
6. **Decoupled**: UI just subscribes to state, doesn't manage it

## Migration Path

### Phase 1: Create State and Store (Current Branch Status)
- ✅ Action enum exists
- ✅ KeyMapper maps keys to actions
- ✅ Actions are being dispatched (but handled directly)

### Phase 2: Create Central State Structure
```rust
struct AppState {
    navigation: NavigationState,
    mode: AppMode,
    selection_mode: SelectionMode,
    buffer: BufferState,
    search: SearchState,
    filter: FilterState,
    // ... all app state in one place
}
```

### Phase 3: Create Reducer
```rust
fn reducer(state: &AppState, action: Action) -> AppState {
    // Pure function that returns new state
}
```

### Phase 4: Create Store
```rust
struct Store {
    state: AppState,
    reducer: fn(&AppState, Action) -> AppState,
    subscribers: Vec<Box<dyn Subscriber>>,
}

impl Store {
    fn dispatch(&mut self, action: Action) {
        let new_state = (self.reducer)(&self.state, action);
        if new_state != self.state {
            self.state = new_state;
            self.notify_subscribers();
        }
    }
}
```

### Phase 5: Make TUI a Subscriber
```rust
impl Subscriber for EnhancedTuiApp {
    fn on_state_change(&mut self, state: &AppState) {
        // Update internal references
        // Trigger re-render
    }
}
```

## Example: Navigation in Redux Style

### Current (Imperative)
```rust
fn next_row(&mut self) {
    let nav = self.state_container.navigation_mut();
    nav.selected_row += 1;
    self.buffer_mut().set_selected_row(nav.selected_row);
    self.update_viewport();
}
```

### Future (Redux)
```rust
// Just dispatch the action
store.dispatch(Action::Navigate(NavigateAction::Down(1)));

// Reducer handles it purely
fn reducer(state: &AppState, action: Action) -> AppState {
    match action {
        Action::Navigate(NavigateAction::Down(n)) => {
            AppState {
                navigation: NavigationState {
                    selected_row: min(
                        state.navigation.selected_row + n,
                        state.data.row_count - 1
                    ),
                    ..state.navigation
                },
                ..state
            }
        }
        _ => state
    }
}
```

## Middleware Opportunities

With Redux, we can add middleware:

```rust
// Logging middleware
fn logging_middleware(action: &Action, state: &AppState) {
    debug!("Action: {:?}", action);
    debug!("State before: {:?}", state);
}

// Async middleware for API calls
fn api_middleware(action: &Action) -> Option<Future<Action>> {
    match action {
        Action::ExecuteQuery(sql) => {
            Some(async { 
                let result = api_client.query(sql).await;
                Action::QueryComplete(result)
            })
        }
        _ => None
    }
}

// Undo/Redo middleware
fn history_middleware(action: &Action, history: &mut StateHistory) {
    if action.is_undoable() {
        history.push(state.clone());
    }
}
```

## Testing Benefits

```rust
#[test]
fn test_navigation() {
    let initial_state = AppState::default();
    
    // Test moving down
    let new_state = reducer(&initial_state, Action::Navigate(NavigateAction::Down(5)));
    assert_eq!(new_state.navigation.selected_row, 5);
    
    // Test boundary
    let state_at_end = AppState {
        navigation: NavigationState { selected_row: 99, ..default() },
        data: DataState { row_count: 100, ..default() },
        ..default()
    };
    let new_state = reducer(&state_at_end, Action::Navigate(NavigateAction::Down(5)));
    assert_eq!(new_state.navigation.selected_row, 99); // Clamped at boundary
}
```

## Timeline

1. **Now**: Action system integrated, handling actions imperatively
2. **Next**: Create AppState structure consolidating all state
3. **Then**: Create reducer function for pure state transitions
4. **Then**: Create Store with dispatch and subscribe
5. **Finally**: Convert TUI to subscriber model

This will make the codebase much more maintainable and testable!