tutorials/rust/hello-server/src/main.rs

108 lines
3.7 KiB
Rust

#[macro_use] extern crate nickel;
extern crate rustc_serialize;
extern crate handlebars;
use nickel::{Nickel, HttpRouter, FormBody};
use std::io;
use std::sync::{Arc, Mutex};
mod todo;
mod store;
mod template;
// Lets us type `Add("Todo item".to_string())` instead of `TodoAction::Add("Todo item".to_string())
use todo::TodoAction::{ Add, Remove, Toggle };
// Same with the Action enum and VisibilityFilter, Action::* would work too, but this way we list what we use
use store::Action::{ Todos, Visibility };
use store::VisibilityFilter::{ ShowActive, ShowAll, ShowCompleted };
use todo::{Todo};
use store::{ Store, State, reducer };
use template::render;
fn main() {
let mut server = Nickel::new();
// Create our todo list store
let mut store = Store::create_store(reducer);
// Add some todos so we've got something to remember
store.dispatch( Todos( Add("one thing".to_string()) ) );
store.dispatch( Todos( Add("another thing".to_string()) ) );
// Put the store in a container that will let us
// safely use it in multi-threaded environment
let store_container = Arc::new( Mutex::new(store) );
// Every clone of our container is counted
// so that when the last clone goes out of scope
// the container can be deallocated
let store = store_container.clone();
// At the / path let's just render our current todo list
server.get("/", middleware! { |_req, res|
// We get our store from the container by locking it
// from other threads
let store = store.lock().unwrap();
// Render from nickel_mustache takes the
// nickel Result struct, a path to a mustache
// template, and the data to use
return render(res, "./src/todos.tpl", store.get_state())
// And here the lock is released...
});
// Let's clone it again for the next closure
let store = store_container.clone();
// This time we look for requests like /toggle/1
server.get("/:action/:id", middleware! { |_req, res|
// We will dispatch an action on our store so we
// get a mutable reference
let mut store = store.lock().unwrap();
// We try to parse the id param to an int, this words for the
// toggle and remove actions
if let Ok(num) = _req.param("id").unwrap().parse::<i16>() {
match _req.param("action").unwrap() {
"toggle" => {
store.dispatch( Todos( Toggle(num) ) )
},
"remove" => store.dispatch( Todos ( Remove(num) ) ),
_ => (),
}
} else {
// Otherwise look for a show action
match _req.param("action").unwrap() {
"show" => {
match _req.param("id").unwrap() {
"all" => store.dispatch( Visibility(ShowAll) ),
"active" => store.dispatch( Visibility(ShowActive) ),
"completed" => store.dispatch( Visibility(ShowCompleted) ),
_ => (),
}
},
_ => (),
}
}
// And render the now updated todo list
return render(res, "./src/todos.tpl", store.get_state())
});
// Let's clone it again for the next closure
let store = store_container.clone();
server.post("/*", middleware! { |req, res|
let mut store = store.lock().unwrap();
let form_body = req.form_body().ok().unwrap();
if let Some(new_todo) = form_body.get("todo") {
if new_todo.len() > 0 {
store.dispatch( Todos( Add(new_todo.to_string()) ) );
}
}
return render(res, "./src/todos.tpl", store.get_state())
});
server.listen("0.0.0.0:3000");
}