107 lines
3.3 KiB
Rust
107 lines
3.3 KiB
Rust
use rustc_serialize::json::{self, Json, ToJson};
|
|
use store::Action::{ Visibility };
|
|
use todo::{ Todo, TodoAction, todo_reducer };
|
|
|
|
// Ripping off the canonical Redux todo example we'll add a
|
|
// visibility filter to our state in addation to the todos we already had
|
|
// This state struct will be the single source of state for our todo list program
|
|
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
|
pub struct State {
|
|
pub todos: Vec<Todo>,
|
|
pub visibility_filter: VisibilityFilter
|
|
}
|
|
|
|
// By implementing a struct we are creating something very much like
|
|
// a class, we can attach methods to it refering to `&self` or `&mut self`
|
|
impl State {
|
|
// Can be called with State::default()
|
|
pub fn default() -> State {
|
|
State {
|
|
todos: Vec::new(),
|
|
visibility_filter: VisibilityFilter::ShowAll,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToJson for State {
|
|
fn to_json(&self) -> Json {
|
|
Json::from_str(&json::encode(&self).unwrap()).unwrap()
|
|
}
|
|
}
|
|
|
|
// Rust has enums, so the enum type can replace the "type" property of Redux objects
|
|
// The enums will replace `action_creators` too since Todos(Add("Todo item".to_string()))
|
|
// is pretty clear
|
|
#[derive(Clone, Debug)]
|
|
pub enum Action {
|
|
Todos(TodoAction),
|
|
Visibility(VisibilityFilter),
|
|
}
|
|
|
|
|
|
// Our 3 visibility states
|
|
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
|
pub enum VisibilityFilter {
|
|
ShowActive,
|
|
ShowAll,
|
|
ShowCompleted,
|
|
}
|
|
|
|
// Our main reducer, returns a new State with the results of the child-reducers
|
|
// No combineReducers is implemented here, so it calls the child reducers
|
|
// by function name
|
|
pub fn reducer(state: &State, action: Action) -> State {
|
|
// Always return a new state
|
|
State {
|
|
todos: todo_reducer(&state.todos, &action),
|
|
visibility_filter: visibility_reducer(&state.visibility_filter, &action),
|
|
}
|
|
}
|
|
|
|
// Very simple reducer since the action will either be a VisibilityFilter, in which
|
|
// case we will return that, otherwise just return the incoming state
|
|
fn visibility_reducer(state: &VisibilityFilter, action: &Action) -> VisibilityFilter {
|
|
match *action {
|
|
Visibility(ref vis_action) => vis_action.clone(),
|
|
_ => state.clone(),
|
|
}
|
|
}
|
|
|
|
// Redux store implementation
|
|
pub struct Store {
|
|
state: State,
|
|
listeners: Vec<fn(&State)>,
|
|
reducer: fn(&State, Action) -> State,
|
|
}
|
|
|
|
impl Store {
|
|
// Takes a reducer function as the only argument
|
|
// To keep it simple, State::default() privides the initial state in this example
|
|
pub fn create_store(reducer: fn(&State, Action) -> State) -> Store {
|
|
Store {
|
|
state: State::default(),
|
|
listeners: Vec::new(),
|
|
reducer: reducer,
|
|
}
|
|
}
|
|
|
|
// Pushes a listener that will be called for any state change
|
|
pub fn subscribe(&mut self, listener: fn(&State)) {
|
|
self.listeners.push(listener);
|
|
}
|
|
|
|
// Simply returns a borrowed reference to the state
|
|
#[allow(dead_code)]
|
|
pub fn get_state(&self) -> &State {
|
|
&self.state
|
|
}
|
|
|
|
// Called for every new action, calls the reducer to update the state
|
|
// and then calls every listener with the new State
|
|
pub fn dispatch(&mut self, action: Action) {
|
|
self.state = (self.reducer)(&self.state, action);
|
|
for listener in &self.listeners {
|
|
listener(&self.state);
|
|
}
|
|
}
|
|
} |