1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! The SpaceAPI server struct.

use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::{Arc, Mutex};

use iron::Iron;
use router::Router;
use redis::{IntoConnectionInfo, ConnectionInfo};

mod handlers;

use ::api;
use ::api::SensorTemplate;

use ::sensors;
use ::modifiers;
use ::errors::SpaceapiServerError;


/// A Space API server instance.
///
/// You can create a new instance using the ``new`` constructor method by
/// passing it the host, the port, the ``Status`` object and a redis connection info object.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    socket_addr: SocketAddr,
    status: api::Status,
    redis_connection_info: ConnectionInfo,
    sensor_specs: sensors::SafeSensorSpecs,
    status_modifiers: Vec<Box<modifiers::StatusModifier>>,
}

impl SpaceapiServer {

    pub fn new<U, T>(socket_addr: U,
                     status: api::Status,
                     redis_connection_info: T,
                     status_modifiers: Vec<Box<modifiers::StatusModifier>>)
        -> Result<SpaceapiServer, SpaceapiServerError>
        where U: ToSocketAddrs, T: IntoConnectionInfo {
            let mut socket_addr_iter = try!(socket_addr.to_socket_addrs());
            let socket_addr = try!(socket_addr_iter.next()
                                   .ok_or(SpaceapiServerError::Message("Invalid socket address".into())));
            Ok(SpaceapiServer {
                socket_addr: socket_addr,
                status: status,
                redis_connection_info: try!(redis_connection_info.into_connection_info()),
                sensor_specs: Arc::new(Mutex::new(vec![])),
                status_modifiers: status_modifiers,
            })
        }

    fn route(self) -> Router {
        router!(
            get "/" => handlers::ReadHandler::new(self.status.clone(), self.redis_connection_info.clone(), self.sensor_specs.clone(), self.status_modifiers),
            put "/sensors/:sensor/" => handlers::UpdateHandler::new(self.redis_connection_info.clone(), self.sensor_specs.clone())
        )
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// The call returns an `HttpResult<Listening>` object, see
    /// http://ironframework.io/doc/hyper/server/struct.Listening.html
    /// for more information.
    pub fn serve(self) -> ::HttpResult<::Listening> {
        let socket_addr = self.socket_addr;
        let router = self.route();

        println!("Starting HTTP server on http://{}...", socket_addr);
        Iron::new(router).http(socket_addr)
    }

    /// Register a new sensor.
    ///
    /// The first argument is a ``api::SensorTemplate`` instance containing all static data.
    /// The second argument specifies how to get the actual sensor value from Redis.
    /// And the third argument specifies the data type of the value.
    pub fn register_sensor(&mut self, template: Box<api::SensorTemplate>, data_key: String) {
        let sensor_specs_ref = self.sensor_specs.clone();
        sensor_specs_ref.lock().unwrap().push(
            sensors::SensorSpec { template: template, data_key: data_key}
        );
    }

}