app_universe/lib.rs
1//! `app-universe` provides a framework agnostic approach to managing frontend application state.
2//! It essentially funtions like [redux](https://github.com/reduxjs/redux) from the javascript ecosystem.
3//!
4//! # Example Usage
5//!
6//! ```rust
7//! use app_universe::{ AppUniverse, AppUniverseCore };
8//!
9//! struct TestAppState {
10//! counter: u8,
11//! }
12//!
13//! pub enum Msg {
14//! Increment(u8),
15//! }
16//!
17//! impl AppUniverseCore for TestAppState {
18//! type Message = Msg;
19//!
20//! fn msg(&mut self, message: Self::Message) {
21//! match message {
22//! Msg::Increment(value) => {
23//! self.counter += value;
24//! }
25//! }
26//! }
27//! }
28//!
29//! fn main() {
30//! let state = TestAppState { counter: 0 };
31//! let mut universe = AppUniverse::new(state);
32//!
33//! universe.msg(Msg::Increment(1));
34//!
35//! let subscription = universe.subscribe(Box::new(move |universe| {
36//! println!("Counter value is {}", universe.read().counter);
37//! }));
38//!
39//! universe.msg(Msg::Increment(1));
40//!
41//! universe.unsubscribe(subscription).unwrap();
42//! }
43//! ```
44//!
45//! # The API
46//! The core API is mostly defined by the `AppUniverseCore` trait and the `AppUniverse` struct.
47//!
48//! ## The `AppUniverseCore` trait
49//!
50//! The `AppUniverseCore` trait is intended to be implemented for the struct that holds your application state.
51//!
52//! Let's implement `AppUniverseCore` for our application state. First we define our state:
53//!
54//! ```rust
55//! use app_universe::{ AppUniverse, AppUniverseCore };
56//!
57//! struct Product {
58//! id: u16
59//! }
60//! struct MyAppState {
61//! cart: Vec<Product>
62//! }
63//!
64//! # enum Msg {
65//! # AddProductToCart(Product),
66//! # }
67//! #
68//! impl AppUniverseCore for MyAppState {
69//! # type Message = Msg;
70//! # fn msg(&mut self, message: Self::Message) {
71//! # todo!();
72//! # }
73//! // We Will fill this in soon
74//! }
75//!
76//! fn main() {
77//! // We will populate this soon
78//! }
79//! ```
80//! Whenever a struct implements `AppUniverseCore`, it expects you to define 2 things:
81//!
82//! - A `Message` type
83//! - A `msg` function
84//!
85//! ## The `Message` type
86//! The Message type alias refers to an enum were each variant will be matched against in the `msg` function.
87//! This Message acts like a `action` in `redux`.
88//!
89//! ## The `msg` function
90//! The `msg` function is the **ONLY** way to mutate state. The `msg` function matches against every variant in the `Message` enum. In each match branch you are expected to mutate state in some way. This is the **ONLY** plave you can mutate state.
91//!
92//! Now let's continue with our implementation
93//!
94//! ```rust
95//! # use app_universe::{ AppUniverse, AppUniverseCore };
96//! #
97//! # struct Product {
98//! # id: u16
99//! # }
100//! # struct MyAppState {
101//! # cart: Vec<Product>
102//! # }
103//! // ...
104//! enum Msg {
105//! AddProductToCart(Product),
106//! }
107//!
108//! impl AppUniverseCore for MyAppState {
109//! type Message = Msg;
110//!
111//! fn msg(&mut self, message: Self::Message) {
112//! match message {
113//! Msg::AddProductToCart(product) => {
114//! self.cart.push(product);
115//! }
116//! }
117//! }
118//! }
119//! fn main() {
120//! // We will populate this soon
121//! }
122//! ```
123//! ## The `AppUniverse`
124//! The `AppUniverse` struct is what you will interract with most of the time.
125//! In our main function, we want to create a new app universe.
126//!
127//! ```rust
128//! # use app_universe::{ AppUniverse, AppUniverseCore };
129//! #
130//! # struct Product {
131//! # id: u16
132//! # }
133//! # struct MyAppState {
134//! # cart: Vec<Product>
135//! # }
136//!
137//! # enum Msg {
138//! # AddProductToCart(Product),
139//! # }
140//! #
141//! # impl AppUniverseCore for MyAppState {
142//! # type Message = Msg;
143//! #
144//! # fn msg(&mut self, message: Self::Message) {
145//! # match message {
146//! # Msg::AddProductToCart(product) => {
147//! # self.cart.push(product);
148//! # }
149//! # }
150//! # }
151//! # }
152//! // ...
153//! fn main() {
154//! let core = MyAppState { cart: vec![] };
155//! let mut universe = AppUniverse::new(core);
156//! }
157//! ```
158//! ## Subscribing to the `AppUniverse`
159//! Subscribing to the `AppUniverse` essentially means passing a callback that should be called whenever state changes.
160//! A subscriber function will recieve the `AppUniverse` as an argument whenever it's called. Let's subscribe to out universe in our example.
161//!
162//! ```rust
163//! # use app_universe::{ AppUniverse, AppUniverseCore };
164//! #
165//! # struct Product {
166//! # id: u16
167//! # }
168//! #
169//! # struct MyAppState {
170//! # cart: Vec<Product>
171//! # }
172//! #
173//! # enum Msg {
174//! # AddProductToCart(Product),
175//! # }
176//! # impl AppUniverseCore for MyAppState {
177//! # type Message = Msg;
178//! #
179//! # fn msg(&mut self, message: Self::Message) {
180//! # match message {
181//! # Msg::AddProductToCart(product) => {
182//! # self.cart.push(product);
183//! # }
184//! # }
185//! # }
186//! # }
187//! fn main() {
188//! let core = MyAppState { cart: vec![] };
189//! let mut universe = AppUniverse::new(core);
190//!
191//! let subscription = universe.subscribe(Box::new(|universe| { /* Do something */ }));
192//! }
193//! ```
194
195mod app_universe;
196mod tests;
197pub use crate::app_universe::*;
198
199// I want the subscription to be removed when the subscriptions go out of scope