oxide_mvu/lib.rs
1#![cfg_attr(feature = "no_std", no_std)]
2
3//! A lightweight Model-View-Update (MVU) runtime for Rust with `no_std` support.
4//!
5//! Implements the MVU pattern for building predictable, testable applications with
6//! unidirectional data flow and controlled side effects.
7//!
8//! ## Example
9//!
10//! ```rust
11//! use oxide_mvu::{Emitter, Effect, MvuLogic, MvuRuntime, Renderer};
12//!
13//! #[derive(Clone)]
14//! enum Event {
15//! AccumulateClicked,
16//! }
17//!
18//! #[derive(Clone)]
19//! struct Model {
20//! count: i32,
21//! }
22//!
23//! struct Props {
24//! count: i32,
25//! on_accumulate_click: Box<dyn Fn()>,
26//! }
27//!
28//! struct MyLogic;
29//!
30//! impl MvuLogic<Event, Model, Props> for MyLogic {
31//! fn init(&self, model: Model) -> (Model, Effect<Event>) {
32//! (model, Effect::none())
33//! }
34//!
35//! fn update(&self, event: Event, model: &Model) -> (Model, Effect<Event>) {
36//! match event {
37//! Event::AccumulateClicked => {
38//! let new_model = Model {
39//! count: model.count + 1,
40//! ..model.clone()
41//! };
42//! (new_model, Effect::none())
43//! }
44//! }
45//! }
46//!
47//! fn view(&self, model: &Model, emitter: &Emitter<Event>) -> Props {
48//! let emitter = emitter.clone();
49//! Props {
50//! count: model.count,
51//! on_accumulate_click: Box::new(move || {
52//! emitter.try_emit(Event::AccumulateClicked);
53//! }),
54//! }
55//! }
56//! }
57//!
58//! struct MyRenderer;
59//!
60//! impl Renderer<Props> for MyRenderer {
61//! fn render(&mut self, _props: Props) {}
62//! }
63//!
64//! async fn main_async() {
65//! // Create a spawner for your async runtime.
66//! // This is how `Effect`s are executed.
67//! let spawner = |fut| {
68//! // Spawn the future on your chosen runtime.
69//! // Examples:
70//! // tokio::spawn(fut);
71//! // async_std::task::spawn(fut);
72//! let _ = fut;
73//! };
74//!
75//! let runtime = MvuRuntime::new(
76//! Model { count: 0 },
77//! MyLogic,
78//! MyRenderer,
79//! spawner,
80//! );
81//!
82//! // `run()` returns a Future representing the event loop.
83//! // It must be awaited inside an async context.
84//! runtime.run().await;
85//! }
86//! ```
87//!
88//! In a real application, `main_async` would be executed by your async runtime
89//! (e.g. via `#[tokio::main]`, `async_std::main`, or an embedded executor).
90
91#[cfg(feature = "no_std")]
92extern crate alloc;
93
94/// Trait alias for event type constraints.
95///
96/// All events must implement these bounds to work with the MVU runtime.
97pub trait Event: Send + Sync + Clone + 'static {}
98
99/// Blanket implementation for any type that satisfies the bounds.
100impl<T> Event for T where T: Send + Sync + Clone + 'static {}
101
102// Module declarations
103mod effect;
104mod emitter;
105mod logic;
106mod renderer;
107mod runtime;
108
109// Public re-exports
110pub use effect::Effect;
111pub use emitter::Emitter;
112pub use logic::MvuLogic;
113pub use renderer::Renderer;
114pub use runtime::{MvuRuntime, Spawner};
115
116// Test utilities (only available with 'testing' feature or during tests)
117#[cfg(any(test, feature = "testing"))]
118pub use renderer::TestRenderer;
119#[cfg(any(test, feature = "testing"))]
120pub use runtime::{create_test_spawner, TestMvuDriver, TestMvuRuntime};