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