tui-dispatch-core 0.7.0

Core traits and types for tui-dispatch
Documentation
//! 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::{Action, ReducerResult, Store};
//!
//! #[derive(Clone, Debug)]
//! enum MyAction { Increment, Decrement }
//!
//! impl Action for MyAction {
//!     fn name(&self) -> &'static str {
//!         match self {
//!             MyAction::Increment => "Increment",
//!             MyAction::Decrement => "Decrement",
//!         }
//!     }
//! }
//!
//! #[derive(Default)]
//! struct AppState { counter: i32 }
//!
//! fn reducer(state: &mut AppState, action: MyAction) -> ReducerResult {
//!     match action {
//!         MyAction::Increment => { state.counter += 1; ReducerResult::changed() }
//!         MyAction::Decrement => { state.counter -= 1; ReducerResult::changed() }
//!     }
//! }
//!
//! let mut store = Store::new(AppState::default(), reducer);
//! let result = store.dispatch(MyAction::Increment);
//! assert!(result.changed);
//! assert_eq!(store.state().counter, 1);
//! ```
//!
//! # Async Handler Pattern
//!
//! For applications with async operations (API calls, file I/O, etc.), use a two-phase
//! action pattern:
//!
//! 1. **Intent actions** trigger async work (e.g., `FetchData`)
//! 2. **Result actions** carry the outcome back (e.g., `DidFetchData`, `DidFetchError`)
//!
//! ```ignore
//! 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"`).

#[cfg(target_arch = "wasm32")]
extern crate tinycrossterm as crossterm;

pub mod action;
pub mod bus;
pub mod component;
pub mod event;
pub mod features;
pub mod keybindings;
pub mod resource;
pub mod runtime;
pub mod store;
#[cfg(feature = "subscriptions")]
pub mod subscriptions;
#[cfg(feature = "tasks")]
pub mod tasks;
pub mod testing;

// Core trait exports
#[allow(deprecated)]
pub use action::{Action, ActionCategory, ActionParams, ActionSummary};
pub use component::Component;
pub use features::{DynamicFeatures, FeatureFlags};

// Event system exports
pub use bus::{
    process_raw_event, spawn_event_poller, DefaultBindingContext, EventBus, EventHandler,
    EventOutcome, EventRoutingState, HandlerResponse, RawEvent, RouteTarget, RoutedEvent,
    SimpleEventBus,
};
pub use event::{
    ComponentId, Event, EventContext, EventKind, EventType, GlobalKeyPolicy, NumericComponentId,
};

// Keybindings exports
pub use keybindings::{format_key_for_display, parse_key_string, BindingContext, Keybindings};

// Store exports
#[cfg(feature = "tracing")]
pub use store::LoggingMiddleware;
pub use store::{
    ComposedMiddleware, DispatchError, DispatchLimits, Middleware, NoEffect, NoopMiddleware,
    Reducer, ReducerResult, Store, StoreWithMiddleware,
};

// Runtime exports
pub use runtime::{
    DispatchErrorPolicy, EffectContext, PollerConfig, RenderContext, Runtime, RuntimeStore,
};

// Resource exports
pub use resource::DataResource;

// Task exports (requires "tasks" feature)
#[cfg(feature = "tasks")]
pub use tasks::{TaskKey, TaskManager, TaskPauseHandle};

// Subscription exports (requires "subscriptions" feature)
#[cfg(feature = "subscriptions")]
pub use subscriptions::{SubKey, SubPauseHandle, Subscriptions};

// Re-export ratatui types for convenience
pub use ratatui::{
    layout::Rect,
    style::{Color, Modifier, Style},
    text::{Line, Span, Text},
    Frame,
};

// Testing exports
pub use testing::{
    alt_key, buffer_rect_to_string_plain, buffer_to_string, buffer_to_string_plain, char_key,
    ctrl_key, into_event, key, key_event, key_events, keys, ActionAssertions, ActionAssertionsEq,
    EffectAssertions, EffectAssertionsEq, RenderHarness, StoreTestHarness, TestHarness,
};

#[cfg(feature = "testing-time")]
pub use testing::{advance_time, pause_time, resume_time};

/// Prelude module for convenient imports
pub mod prelude {
    pub use crate::action::{Action, ActionCategory, ActionParams};
    pub use crate::bus::{
        process_raw_event, spawn_event_poller, DefaultBindingContext, EventBus, EventHandler,
        EventOutcome, EventRoutingState, HandlerResponse, RawEvent, RouteTarget, RoutedEvent,
        SimpleEventBus,
    };
    pub use crate::component::Component;
    pub use crate::event::{
        ComponentId, Event, EventContext, EventKind, EventType, GlobalKeyPolicy, NumericComponentId,
    };
    pub use crate::features::{DynamicFeatures, FeatureFlags};
    pub use crate::keybindings::{
        format_key_for_display, parse_key_string, BindingContext, Keybindings,
    };
    pub use crate::reducer_compose;
    pub use crate::resource::DataResource;
    #[cfg(feature = "tracing")]
    pub use crate::store::LoggingMiddleware;
    pub use crate::store::{
        ComposedMiddleware, DispatchError, DispatchLimits, Middleware, NoEffect, NoopMiddleware,
        Reducer, ReducerResult, Store, StoreWithMiddleware,
    };

    // Runtime helpers
    pub use crate::runtime::{
        DispatchErrorPolicy, EffectContext, PollerConfig, RenderContext, Runtime, RuntimeStore,
    };
    #[cfg(feature = "subscriptions")]
    pub use crate::subscriptions::{SubKey, SubPauseHandle, Subscriptions};
    #[cfg(feature = "tasks")]
    pub use crate::tasks::{TaskKey, TaskManager, TaskPauseHandle};

    // Re-export ratatui types
    pub use ratatui::{
        layout::Rect,
        style::{Color, Modifier, Style},
        text::{Line, Span, Text},
        Frame,
    };
}