tui-dispatch-core-0.3.1 has been yanked.
Core traits and types for tui-dispatch
This crate provides the foundational abstractions for building TUI applications with centralized state management, following a Redux/Elm-inspired architecture.
Core Concepts
- Action: Events that describe state changes
- Store: Centralized state container with reducer pattern
- Component: Pure UI elements that render based on props
- EventBus: Pub/sub system for event routing
- Keybindings: Context-aware key mapping
Basic Example
use tui_dispatch_core::prelude::*;
#[derive(Action, Clone, Debug)]
enum MyAction {
Increment,
Decrement,
}
#[derive(Default)]
struct AppState {
counter: i32,
}
fn reducer(state: &mut AppState, action: MyAction) -> bool {
match action {
MyAction::Increment => { state.counter += 1; true }
MyAction::Decrement => { state.counter -= 1; true }
}
}
let mut store = Store::new(AppState::default(), reducer);
store.dispatch(MyAction::Increment);
Async Handler Pattern
For applications with async operations (API calls, file I/O, etc.), use a two-phase action pattern:
- Intent actions trigger async work (e.g.,
FetchData) - Result actions carry the outcome back (e.g.,
DidFetchData,DidFetchError)
use tokio::sync::mpsc;
#[derive(Action, Clone, Debug)]
#[action(infer_categories)]
enum Action {
// Intent: triggers async fetch
DataFetch { id: String },
// Result: async operation completed
DataDidLoad { id: String, payload: Vec<u8> },
DataDidError { id: String, error: String },
}
// Async handler spawns a task and sends result back via channel
fn handle_async(action: &Action, tx: mpsc::UnboundedSender<Action>) {
match action {
Action::DataFetch { id } => {
let id = id.clone();
let tx = tx.clone();
tokio::spawn(async move {
match fetch_from_api(&id).await {
Ok(payload) => tx.send(Action::DataDidLoad { id, payload }),
Err(e) => tx.send(Action::DataDidError { id, error: e.to_string() }),
}
});
}
_ => {}
}
}
// Main loop receives actions from both events and async completions
loop {
tokio::select! {
Some(action) = action_rx.recv() => {
handle_async(&action, action_tx.clone());
store.dispatch(action);
}
// ... event handling
}
}
The Did* naming convention clearly identifies result actions. With #[action(infer_categories)],
these are automatically grouped (e.g., DataFetch and DataDidLoad both get category "data").