mirror of
https://codeberg.org/JasterV/teatui.git
synced 2026-04-26 18:10:03 +00:00
This pull request introduces significant improvements to the TeaTui framework, focusing on error handling, functional purity, and codebase simplification. Most notably, it removes the dependency on `color-eyre`, introduces custom error types for each actor (effects, events, update, view), and updates the API to encourage pure functions and more idiomatic Rust error handling. The example app and documentation are also updated to reflect these changes. **Framework error handling and API improvements:** * Introduced custom error types (`EffectsError`, `EventLoopError`, `UpdateError`, `ViewError`, and unified `ProgramError`) for each actor, replacing the use of `color-eyre` and providing clearer, structured error reporting throughout the framework. [[1]](diffhunk://#diff-1a8ddf10086f54de37be278b04f140fea42a9dd9314ebed509a03510b34a3043L2-R24) [[2]](diffhunk://#diff-dd43168709f1ba547c9dbe210d659222793384f540898dfa4159d3d28447ea59L2-R14) [[3]](diffhunk://#diff-717c070f88e33e75d62e17bcfb063657cc2cbba2198d8faf77fb3855206ba036L2-R51) [[4]](diffhunk://#diff-348239946b2211ffea21e1282dde8299ee0e5484616b8b2da9c97589719531e4L2-R23) [[5]](diffhunk://#diff-eaa72a947a5cb60aaed20788a77b8c3b94606a2b4bd9218fa2bbeeba0a98f726L34-R62) * Updated the core `start` function to accept an `init_fn` initializer, and to use the new error types in its signature and internal logic, ensuring all errors are properly propagated and handled. [[1]](diffhunk://#diff-eaa72a947a5cb60aaed20788a77b8c3b94606a2b4bd9218fa2bbeeba0a98f726L61-R95) [[2]](diffhunk://#diff-eaa72a947a5cb60aaed20788a77b8c3b94606a2b4bd9218fa2bbeeba0a98f726L88-R157) * Refactored the `Update` enum and related logic to use a single variant (`Next(M, Option<E>)`) for state transitions and side effects, simplifying the update pattern and removing the need for multiple variants. **Example application and dependency updates:** * Updated the counter example to remove `color-eyre`, use the new error types, and conform to the new pure function API (no more `Result` returns from user functions). [[1]](diffhunk://#diff-d1e739029fa7deef7dec36d183f1097a4ed266898520ddae449cff9c91f65a96L1-R10) [[2]](diffhunk://#diff-d1e739029fa7deef7dec36d183f1097a4ed266898520ddae449cff9c91f65a96L96-R111) [[3]](diffhunk://#diff-d1e739029fa7deef7dec36d183f1097a4ed266898520ddae449cff9c91f65a96L128-R129) [[4]](diffhunk://#diff-d1e739029fa7deef7dec36d183f1097a4ed266898520ddae449cff9c91f65a96R38-R42) * Updated dependencies in `Cargo.toml` files: removed `color-eyre`, bumped `ratatui` version, and added `thiserror` for error handling. [[1]](diffhunk://#diff-2e9d962a08321605940b5a657135052fbcef87b5e360662bb527c96d9a615542L11-R12) [[2]](diffhunk://#diff-c6fd35fe7c3e8bcfcf9e60f261503d791b528405f92b6ba99f7272e91939d861L9) [[3]](diffhunk://#diff-1cd61e14e7d516bb58a3b2607848174d38c432631899f34dbc88076158f3bf52L10-R12) **Documentation:** * Added a comprehensive project overview and architecture description for TeaTui in `AGENTS.md`, explaining the functional philosophy, actor model, and usage guidelines.
130 lines
3.3 KiB
Rust
130 lines
3.3 KiB
Rust
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
|
use ratatui::{
|
|
style::Stylize,
|
|
text::Line,
|
|
widgets::{Block, Paragraph},
|
|
};
|
|
use teatui::{ProgramError, update::Update};
|
|
|
|
fn main() -> Result<(), ProgramError<Model, Message, Effect>> {
|
|
teatui::start(|| (Model::default(), None), update, view, run_effects)
|
|
}
|
|
|
|
/// Defines the state of the application
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct Model {
|
|
pub counter: u64,
|
|
}
|
|
|
|
impl Model {
|
|
pub fn increment_counter(model: Model) -> Model {
|
|
Model {
|
|
counter: model.counter + 1,
|
|
}
|
|
}
|
|
|
|
pub fn decrement_counter(model: Model) -> Model {
|
|
let counter = if model.counter == 0 {
|
|
0
|
|
} else {
|
|
model.counter - 1
|
|
};
|
|
|
|
Model { counter }
|
|
}
|
|
}
|
|
|
|
/// Possible side effects to execute
|
|
#[derive(Debug)]
|
|
pub enum Effect {}
|
|
|
|
/// Messages that represent a change of state in the application
|
|
#[derive(Debug)]
|
|
pub enum Message {
|
|
IncCounter,
|
|
DecCounter,
|
|
Exit,
|
|
NoOp,
|
|
}
|
|
|
|
impl From<crossterm::event::Event> for Message {
|
|
fn from(value: Event) -> Self {
|
|
match value {
|
|
Event::Key(KeyEvent {
|
|
code: KeyCode::Esc | KeyCode::Char('q'),
|
|
kind: KeyEventKind::Press,
|
|
state: _,
|
|
modifiers: _,
|
|
}) => Self::Exit,
|
|
|
|
Event::Key(KeyEvent {
|
|
code: KeyCode::Right,
|
|
kind: KeyEventKind::Press,
|
|
state: _,
|
|
modifiers: _,
|
|
}) => Self::IncCounter,
|
|
|
|
Event::Key(KeyEvent {
|
|
code: KeyCode::Left,
|
|
kind: KeyEventKind::Press,
|
|
state: _,
|
|
modifiers: _,
|
|
}) => Self::DecCounter,
|
|
|
|
Event::Key(KeyEvent {
|
|
code: KeyCode::Char('c') | KeyCode::Char('C'),
|
|
modifiers: KeyModifiers::CONTROL,
|
|
kind: KeyEventKind::Press,
|
|
state: _,
|
|
}) => Self::Exit,
|
|
|
|
Event::FocusGained
|
|
| Event::FocusLost
|
|
| Event::Key(_)
|
|
| Event::Mouse(_)
|
|
| Event::Paste(_)
|
|
| Event::Resize(_, _) => Self::NoOp,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Elm-like update function.
|
|
///
|
|
/// Given the current state (model) and an incoming message from the outside world,
|
|
/// return the next updated state
|
|
pub fn update(model: Model, msg: Message) -> Update<Model, Effect> {
|
|
match msg {
|
|
Message::Exit => Update::Exit,
|
|
Message::NoOp => Update::Next(model, None),
|
|
Message::IncCounter => Update::Next(Model::increment_counter(model), None),
|
|
Message::DecCounter => Update::Next(Model::decrement_counter(model), None),
|
|
}
|
|
}
|
|
|
|
pub fn run_effects(_model: &Model, _effect: Effect) -> Option<Message> {
|
|
None
|
|
}
|
|
|
|
/// Elm-like View function.
|
|
///
|
|
/// Given the current state (read-only), return a drawable widget.
|
|
pub fn view(model: &Model) -> Paragraph<'static> {
|
|
let counter = model.counter;
|
|
|
|
let title = Line::from("Ratatui Actor-based Counter")
|
|
.bold()
|
|
.blue()
|
|
.centered();
|
|
|
|
let text = format!(
|
|
r#"Counter TUI!
|
|
|
|
Counter: {counter}
|
|
|
|
Press `Esc`, `Ctrl-C` or `q` to stop running."#
|
|
);
|
|
|
|
Paragraph::new(text)
|
|
.block(Block::bordered().title(title))
|
|
.centered()
|
|
}
|