use std::collections::BTreeMap;
use rustc_serialize::json::{Json, ToJson};
use serde_json;
use iron::prelude::*;
use iron::{status, headers, middleware};
use iron::modifiers::Header;
use router::Router;
use urlencoded;
use ::api;
use ::types::RedisPool;
use ::sensors;
use ::modifiers;
#[derive(Debug)]
struct ErrorResponse {
reason: String,
}
impl ToJson for ErrorResponse {
fn to_json(&self) -> Json {
let mut d = BTreeMap::new();
d.insert("status".into(), "error".to_json());
d.insert("reason".into(), self.reason.to_json());
Json::Object(d)
}
}
pub struct ReadHandler {
status: api::Status,
redis_pool: RedisPool,
sensor_specs: sensors::SafeSensorSpecs,
status_modifiers: Vec<Box<modifiers::StatusModifier>>,
}
impl ReadHandler {
pub fn new(status: api::Status,
redis_pool: RedisPool,
sensor_specs: sensors::SafeSensorSpecs,
status_modifiers: Vec<Box<modifiers::StatusModifier>>)
-> ReadHandler {
ReadHandler {
status: status,
redis_pool: redis_pool,
sensor_specs: sensor_specs,
status_modifiers: status_modifiers,
}
}
fn build_response_json(&self) -> String {
let mut status_copy = self.status.clone();
for sensor_spec in self.sensor_specs.iter() {
match sensor_spec.get_sensor_value(self.redis_pool.clone()) {
Ok(value) => {
if status_copy.sensors.is_none() {
status_copy.sensors = Some(api::Sensors {
people_now_present: vec![],
temperature: vec![],
});
}
sensor_spec.template.to_sensor(&value, &mut status_copy.sensors.as_mut().unwrap());
},
Err(err) => {
warn!("Could not retrieve key '{}' from Redis, omiting the sensor", &sensor_spec.data_key);
match err {
sensors::SensorError::Redis(e) => debug!("Error: {:?}", e),
sensors::SensorError::R2d2(e) => debug!("Error: {:?}", e),
sensors::SensorError::UnknownSensor(e) => warn!("Error: {:?}", e),
}
},
}
}
for status_modifier in self.status_modifiers.iter() {
status_modifier.modify(&mut status_copy);
}
serde_json::to_string(&status_copy)
.expect("Status object could not be serialized to JSON. \
Please open an issue at https://github.com/spaceapi-community/spaceapi-server-rs/issues")
}
}
impl middleware::Handler for ReadHandler {
fn handle(&self, req: &mut Request) -> IronResult<Response> {
info!("{} /{} from {}", req.method, req.url.path()[0], req.remote_addr);
let body = self.build_response_json();
let response = Response::with((status::Ok, body))
.set(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())))
.set(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])))
.set(Header(headers::AccessControlAllowOrigin::Any));
Ok(response)
}
}
pub struct UpdateHandler {
redis_pool: RedisPool,
sensor_specs: sensors::SafeSensorSpecs,
}
impl UpdateHandler {
pub fn new(redis_pool: RedisPool, sensor_specs: sensors::SafeSensorSpecs)
-> UpdateHandler {
UpdateHandler {
redis_pool: redis_pool,
sensor_specs: sensor_specs,
}
}
fn update_sensor(&self, sensor: &str, value: &str) -> Result<(), sensors::SensorError> {
let sensor_spec = try!(self.sensor_specs.iter()
.find(|&spec| spec.data_key == sensor)
.ok_or(sensors::SensorError::UnknownSensor(sensor.into())));
sensor_spec.set_sensor_value(self.redis_pool.clone(), value)
}
fn ok_response(&self) -> Response {
Response::with((status::NoContent))
.set(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())))
.set(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])))
.set(Header(headers::AccessControlAllowOrigin::Any))
}
fn err_response(&self, error_code: status::Status, reason: &str) -> Response {
let error = ErrorResponse { reason: reason.into() };
Response::with((error_code, error.to_json().to_string()))
.set(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())))
.set(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])))
.set(Header(headers::AccessControlAllowOrigin::Any))
}
}
impl middleware::Handler for UpdateHandler {
fn handle(&self, req: &mut Request) -> IronResult<Response> {
info!("{} /{} from {}", req.method, req.url.path()[0], req.remote_addr);
let sensor_name;
{
let params = req.extensions.get::<Router>().unwrap();
sensor_name = params.find("sensor").unwrap().to_string();
}
let sensor_value;
{
let params = req.get_ref::<urlencoded::UrlEncodedBody>().unwrap();
sensor_value = match params.get("value") {
Some(ref values) => match values.len() {
1 => values[0].to_string(),
_ => return Ok(self.err_response(status::BadRequest, "Too many values specified")),
},
None => return Ok(self.err_response(status::BadRequest, "\"value\" parameter not specified")),
}
}
if let Err(e) = self.update_sensor(&sensor_name, &sensor_value) {
error!("Updating sensor value for sensor \"{}\" failed: {:?}", &sensor_name, e);
let response = match e {
sensors::SensorError::UnknownSensor(sensor) =>
self.err_response(status::BadRequest, &format!("Unknown sensor: {}", sensor)),
sensors::SensorError::Redis(_) | sensors::SensorError::R2d2(_) =>
self.err_response(status::InternalServerError, "Updating values in datastore failed"),
};
return Ok(response)
};
Ok(self.ok_response())
}
}