tui_dispatch_core/lib.rs
1//! Core traits and types for tui-dispatch
2//!
3//! This crate provides the foundational abstractions for building TUI applications
4//! with centralized state management, following a Redux/Elm-inspired architecture.
5//!
6//! # Core Concepts
7//!
8//! - **Action**: Events that describe state changes
9//! - **Store**: Centralized state container with reducer pattern
10//! - **Component**: Pure UI elements that render based on props
11//! - **EventBus**: Pub/sub system for event routing
12//! - **Keybindings**: Context-aware key mapping
13//!
14//! # Basic Example
15//!
16//! ```ignore
17//! use tui_dispatch_core::prelude::*;
18//!
19//! #[derive(Action, Clone, Debug)]
20//! enum MyAction {
21//! Increment,
22//! Decrement,
23//! }
24//!
25//! #[derive(Default)]
26//! struct AppState {
27//! counter: i32,
28//! }
29//!
30//! fn reducer(state: &mut AppState, action: MyAction) -> bool {
31//! match action {
32//! MyAction::Increment => { state.counter += 1; true }
33//! MyAction::Decrement => { state.counter -= 1; true }
34//! }
35//! }
36//!
37//! let mut store = Store::new(AppState::default(), reducer);
38//! store.dispatch(MyAction::Increment);
39//! ```
40//!
41//! # Async Handler Pattern
42//!
43//! For applications with async operations (API calls, file I/O, etc.), use a two-phase
44//! action pattern:
45//!
46//! 1. **Intent actions** trigger async work (e.g., `FetchData`)
47//! 2. **Result actions** carry the outcome back (e.g., `DidFetchData`, `DidFetchError`)
48//!
49//! ```ignore
50//! use tokio::sync::mpsc;
51//!
52//! #[derive(Action, Clone, Debug)]
53//! #[action(infer_categories)]
54//! enum Action {
55//! // Intent: triggers async fetch
56//! DataFetch { id: String },
57//! // Result: async operation completed
58//! DataDidLoad { id: String, payload: Vec<u8> },
59//! DataDidError { id: String, error: String },
60//! }
61//!
62//! // Async handler spawns a task and sends result back via channel
63//! fn handle_async(action: &Action, tx: mpsc::UnboundedSender<Action>) {
64//! match action {
65//! Action::DataFetch { id } => {
66//! let id = id.clone();
67//! let tx = tx.clone();
68//! tokio::spawn(async move {
69//! match fetch_from_api(&id).await {
70//! Ok(payload) => tx.send(Action::DataDidLoad { id, payload }),
71//! Err(e) => tx.send(Action::DataDidError { id, error: e.to_string() }),
72//! }
73//! });
74//! }
75//! _ => {}
76//! }
77//! }
78//!
79//! // Main loop receives actions from both events and async completions
80//! loop {
81//! tokio::select! {
82//! Some(action) = action_rx.recv() => {
83//! handle_async(&action, action_tx.clone());
84//! store.dispatch(action);
85//! }
86//! // ... event handling
87//! }
88//! }
89//! ```
90//!
91//! The `Did*` naming convention clearly identifies result actions. With `#[action(infer_categories)]`,
92//! these are automatically grouped (e.g., `DataFetch` and `DataDidLoad` both get category `"data"`).
93
94pub mod action;
95pub mod bus;
96pub mod component;
97pub mod effect;
98pub mod event;
99pub mod features;
100pub mod keybindings;
101pub mod resource;
102pub mod runtime;
103pub mod store;
104#[cfg(feature = "subscriptions")]
105pub mod subscriptions;
106#[cfg(feature = "tasks")]
107pub mod tasks;
108pub mod testing;
109
110// Core trait exports
111#[allow(deprecated)]
112pub use action::{Action, ActionCategory, ActionParams, ActionSummary};
113pub use component::Component;
114pub use features::{DynamicFeatures, FeatureFlags};
115
116// Event system exports
117pub use bus::{
118 process_raw_event, spawn_event_poller, DefaultBindingContext, EventBus, EventHandler,
119 EventRoutingState, HandlerResponse, RawEvent, RouteTarget, RoutedEvent, SimpleEventBus,
120};
121pub use event::{ComponentId, Event, EventContext, EventKind, EventType, NumericComponentId};
122
123// Keybindings exports
124pub use keybindings::{format_key_for_display, parse_key_string, BindingContext, Keybindings};
125
126// Store exports
127pub use store::{
128 ComposedMiddleware, LoggingMiddleware, Middleware, NoopMiddleware, Reducer, Store,
129 StoreWithMiddleware,
130};
131
132// Runtime exports
133pub use runtime::{
134 DispatchRuntime, DispatchStore, EffectContext, EffectRuntime, EffectStoreLike, EventOutcome,
135 PollerConfig, RenderContext,
136};
137
138// Effect exports
139pub use effect::{DispatchResult, EffectReducer, EffectStore, EffectStoreWithMiddleware};
140
141// Resource exports
142pub use resource::DataResource;
143
144// Task exports (requires "tasks" feature)
145#[cfg(feature = "tasks")]
146pub use tasks::{TaskKey, TaskManager, TaskPauseHandle};
147
148// Subscription exports (requires "subscriptions" feature)
149#[cfg(feature = "subscriptions")]
150pub use subscriptions::{SubKey, SubPauseHandle, Subscriptions};
151
152// Re-export ratatui types for convenience
153pub use ratatui::{
154 layout::Rect,
155 style::{Color, Modifier, Style},
156 text::{Line, Span, Text},
157 Frame,
158};
159
160// Testing exports
161pub use testing::{
162 alt_key, buffer_rect_to_string_plain, buffer_to_string, buffer_to_string_plain, char_key,
163 ctrl_key, into_event, key, key_event, key_events, keys, ActionAssertions, ActionAssertionsEq,
164 EffectAssertions, EffectAssertionsEq, EffectStoreTestHarness, RenderHarness, StoreTestHarness,
165 TestHarness,
166};
167
168#[cfg(feature = "testing-time")]
169pub use testing::{advance_time, pause_time, resume_time};
170
171/// Prelude module for convenient imports
172pub mod prelude {
173 pub use crate::action::{Action, ActionCategory, ActionParams};
174 pub use crate::bus::{
175 process_raw_event, spawn_event_poller, DefaultBindingContext, EventBus, EventHandler,
176 EventRoutingState, HandlerResponse, RawEvent, RouteTarget, RoutedEvent, SimpleEventBus,
177 };
178 pub use crate::component::Component;
179 pub use crate::effect::{
180 DispatchResult, EffectReducer, EffectStore, EffectStoreWithMiddleware,
181 };
182 pub use crate::event::{
183 ComponentId, Event, EventContext, EventKind, EventType, NumericComponentId,
184 };
185 pub use crate::features::{DynamicFeatures, FeatureFlags};
186 pub use crate::keybindings::{
187 format_key_for_display, parse_key_string, BindingContext, Keybindings,
188 };
189 pub use crate::reducer_compose;
190 pub use crate::resource::DataResource;
191 pub use crate::store::{
192 ComposedMiddleware, LoggingMiddleware, Middleware, NoopMiddleware, Reducer, Store,
193 StoreWithMiddleware,
194 };
195
196 // Runtime helpers
197 pub use crate::runtime::{
198 DispatchRuntime, DispatchStore, EffectContext, EffectRuntime, EffectStoreLike,
199 EventOutcome, PollerConfig, RenderContext,
200 };
201 #[cfg(feature = "subscriptions")]
202 pub use crate::subscriptions::{SubKey, SubPauseHandle, Subscriptions};
203 #[cfg(feature = "tasks")]
204 pub use crate::tasks::{TaskKey, TaskManager, TaskPauseHandle};
205
206 // Re-export ratatui types
207 pub use ratatui::{
208 layout::Rect,
209 style::{Color, Modifier, Style},
210 text::{Line, Span, Text},
211 Frame,
212 };
213}