tui_dispatch/lib.rs
1//! tui-dispatch: Centralized state management for Rust TUI apps
2//!
3//! Like Redux/Elm, but for terminals. Components are pure functions of state,
4//! and all state mutations happen through dispatched actions.
5//!
6//! # Quick Start
7//!
8//! ```
9//! use tui_dispatch::prelude::*;
10//!
11//! // Define your actions
12//! #[derive(Action, Clone, Debug)]
13//! enum MyAction {
14//! Increment,
15//! Decrement,
16//! Quit,
17//! }
18//!
19//! // Define your state
20//! #[derive(Default)]
21//! struct MyState {
22//! count: i32,
23//! }
24//!
25//! // Write a reducer
26//! fn reducer(state: &mut MyState, action: MyAction) -> ReducerResult {
27//! match action {
28//! MyAction::Increment => { state.count += 1; ReducerResult::changed() }
29//! MyAction::Decrement => { state.count -= 1; ReducerResult::changed() }
30//! MyAction::Quit => ReducerResult::unchanged(),
31//! }
32//! }
33//!
34//! // Create a store
35//! let mut store = Store::new(MyState::default(), reducer);
36//! store.dispatch(MyAction::Increment);
37//! assert_eq!(store.state().count, 1);
38//! ```
39//!
40//! # With Effects
41//!
42//! For async operations, return effects from the same `Store` reducer:
43//!
44//! ```
45//! use tui_dispatch::prelude::*;
46//!
47//! #[derive(Action, Clone, Debug)]
48//! enum Action {
49//! Fetch,
50//! DidLoad(String),
51//! }
52//!
53//! enum Effect {
54//! FetchData,
55//! }
56//!
57//! #[derive(Default)]
58//! struct State {
59//! data: Option<String>,
60//! loading: bool,
61//! }
62//!
63//! fn reducer(state: &mut State, action: Action) -> ReducerResult<Effect> {
64//! match action {
65//! Action::Fetch => {
66//! state.loading = true;
67//! ReducerResult::changed_with(Effect::FetchData)
68//! }
69//! Action::DidLoad(data) => {
70//! state.data = Some(data);
71//! state.loading = false;
72//! ReducerResult::changed()
73//! }
74//! }
75//! }
76//!
77//! let mut store = Store::new(State::default(), reducer);
78//! let result = store.dispatch(Action::Fetch);
79//! assert!(result.changed);
80//! assert_eq!(result.effects.len(), 1);
81//! ```
82//!
83//! See the [documentation](https://docs.rs/tui-dispatch) for full guides.
84
85// Re-export everything from core
86pub use tui_dispatch_core::reducer_compose;
87pub use tui_dispatch_core::*;
88
89// Debug utilities
90#[cfg(feature = "debug")]
91pub use tui_dispatch_debug::debug;
92#[cfg(not(feature = "debug"))]
93pub mod debug {
94 use std::fmt::Debug;
95
96 #[derive(Clone, Debug, Default)]
97 pub struct DebugEntry {
98 pub key: String,
99 pub value: String,
100 }
101
102 impl DebugEntry {
103 pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
104 Self {
105 key: key.into(),
106 value: value.into(),
107 }
108 }
109 }
110
111 #[derive(Clone, Debug, Default)]
112 pub struct DebugSection {
113 pub title: String,
114 pub entries: Vec<DebugEntry>,
115 }
116
117 impl DebugSection {
118 pub fn new(title: impl Into<String>) -> Self {
119 Self {
120 title: title.into(),
121 entries: Vec::new(),
122 }
123 }
124
125 pub fn entry(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
126 self.entries.push(DebugEntry::new(key, value));
127 self
128 }
129 }
130
131 pub trait DebugState {
132 fn debug_sections(&self) -> Vec<DebugSection>;
133 }
134
135 pub fn debug_string<T: Debug>(value: &T) -> String {
136 format!("{value:?}")
137 }
138
139 pub fn debug_string_pretty<T: Debug>(value: &T) -> String {
140 format!("{value:#?}")
141 }
142}
143
144// Re-export derive macros
145pub use tui_dispatch_macros::{Action, BindingContext, ComponentId, DebugState, FeatureFlags};
146
147/// Prelude for convenient imports
148pub mod prelude {
149 // Traits
150 pub use tui_dispatch_core::{
151 Action, ActionCategory, ActionParams, BindingContext, Component, ComponentId,
152 };
153
154 // Event system
155 pub use tui_dispatch_core::{
156 process_raw_event, spawn_event_poller, DefaultBindingContext, Event, EventBus,
157 EventContext, EventHandler, EventKind, EventRoutingState, EventType, GlobalKeyPolicy,
158 HandlerResponse, NumericComponentId, RawEvent, RouteTarget, RoutedEvent, SimpleEventBus,
159 };
160
161 // Keybindings
162 pub use tui_dispatch_core::{format_key_for_display, parse_key_string, Keybindings};
163
164 // Store
165 pub use tui_dispatch_core::reducer_compose;
166 #[cfg(feature = "tracing")]
167 pub use tui_dispatch_core::LoggingMiddleware;
168 pub use tui_dispatch_core::{
169 ComposedMiddleware, DispatchError, DispatchLimits, Middleware, NoopMiddleware, Reducer,
170 Store, StoreWithMiddleware,
171 };
172
173 // Effects and state types
174 pub use tui_dispatch_core::{DataResource, NoEffect, ReducerResult};
175
176 // Runtime helpers
177 pub use tui_dispatch_core::{
178 DispatchErrorPolicy, EffectContext, EventOutcome, PollerConfig, RenderContext, Runtime,
179 RuntimeStore,
180 };
181
182 // Tasks (requires "tasks" feature)
183 #[cfg(feature = "tasks")]
184 pub use tui_dispatch_core::{TaskKey, TaskManager, TaskPauseHandle};
185
186 // Subscriptions (requires "subscriptions" feature)
187 #[cfg(feature = "subscriptions")]
188 pub use tui_dispatch_core::{SubKey, SubPauseHandle, Subscriptions};
189
190 // Debug
191 #[cfg(feature = "debug")]
192 pub use crate::debug::{
193 ActionLoggerConfig, ActionLoggerMiddleware, DebugFreeze, DebugOverlay, DebugTableBuilder,
194 };
195 pub use crate::debug::{DebugSection, DebugState};
196
197 // Derive macros
198 pub use tui_dispatch_macros::{Action, BindingContext, ComponentId, DebugState, FeatureFlags};
199
200 // Ratatui re-exports
201 pub use tui_dispatch_core::{Color, Frame, Line, Modifier, Rect, Span, Style, Text};
202}