<commit_before>use std::io::Write;

struct StdoutLog;

impl log::Log for StdoutLog {

    // TODO: Add support for log filtering. For now we just output everything as
    // there is no way to change the log verbosity.

    fn enabled(&self, _metadata: &log::Metadata) -> bool {
        true
    }

    fn log(&self, record: &log::Record) {
        let now = std::time::SystemTime::now();

        print!("[");
        print! {
            "{level} {timestamp} ",
            level = record.level(),
            timestamp = humantime::format_rfc3339_nanos(now)
        };

        match record.file() {
            Some(file) => print!("{file}"),
            None => print!("<unknown>"),
        }

        match record.line() {
            Some(line) => print!(":{line}"),
            None => print!(":<unknown>"),
        }

        print!("{}", record.args());
        print!("]");

        println!();
    }

    fn flush(&self) {
        // There is nothing we can do in case of a flushing error, so we just
        // ignore it.
        let _ = std::io::stdout().flush();
    }
}
<commit_msg>Fix log format issues.<commit_after>use std::io::Write;

struct StdoutLog;

impl log::Log for StdoutLog {

    // TODO: Add support for log filtering. For now we just output everything as
    // there is no way to change the log verbosity.

    fn enabled(&self, _metadata: &log::Metadata) -> bool {
        true
    }

    fn log(&self, record: &log::Record) {
        let now = std::time::SystemTime::now();

        print! {
            "[{level} {timestamp} ",
            level = record.level(),
            timestamp = humantime::format_rfc3339_nanos(now)
        };
        match record.file() {
            Some(file) => print!("{file}"),
            None => print!("<unknown>"),
        }
        match record.line() {
            Some(line) => print!(":{line}]"),
            None => print!(":<unknown>]"),
        }

        println!(" {}", record.args());
    }

    fn flush(&self) {
        // There is nothing we can do in case of a flushing error, so we just
        // ignore it.
        let _ = std::io::stdout().flush();
    }
}
<|endoftext|>


<commit_before>//! The main entry point for the Space API server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

pub mod datastore;
pub mod redis_store;

use std::net::Ipv4Addr;
use std::sync::Mutex;
use std::sync::Arc;

use rustc_serialize::json::{Json, ToJson};
use iron::{Request, Response, IronResult, Iron, Set};
use iron::{status, headers, middleware};
use iron::modifiers::Header;

pub use datastore::DataStore;
use spaceapi::Optional::{Value, Absent};
use spaceapi::SensorTemplate::{PeopleNowPresentSensorTemplate, TemperatureSensorTemplate};

/// A specification of a sensor.
///
/// The ``sensor`` field contains the static data of a sensor and the
/// ``data_key`` says how to find the sensor value in the datastore.
#[derive(Debug)]
pub struct SensorSpec {
    sensor: spaceapi::SensorTemplate,
    data_key: String,
    data_type: SensorValueType,
}

/// All possible value types a sensor can have.
#[derive(Debug)]
pub enum SensorValueType {
    Int,
    Float,
    Bool,
}

/// The actual sensor values.
#[derive(Debug)]
pub enum SensorValue {
    Int(i64),
    Float(f64),
    Bool(bool),
}

/// 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 datastore.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: spaceapi::Status,
    datastore: Arc<Mutex<Box<DataStore>>>,
    sensors: Vec<SensorSpec>
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, port: u16, status: spaceapi::Status, datastore: Arc<Mutex<Box<DataStore>>>) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: port,
            status: status,
            datastore: datastore,
            sensors: vec![],
        }
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// This call is blocking. It can be interrupted with SIGINT (Ctrl+C).
    pub fn serve(self) {
        let host = self.host;
        let port = self.port;
        println!("Starting HTTP server on http://{}:{}...", host, port);
        Iron::new(self).http((host, port)).unwrap();
    }

    /// Register a new sensor.
    ///
    /// The first argument is a ``spaceapi::Sensor`` instance containing all static data. The
    /// second argument specifies how to get the actual sensor value from the datastore. And the
    /// third argument specifies the data type of the value.
    pub fn register_sensor(&mut self, sensor: spaceapi::SensorTemplate, data_key: String, data_type: SensorValueType) {
        self.sensors.push(
            SensorSpec { sensor: sensor, data_key: data_key, data_type: data_type }
        );
    }

    fn build_response_json(&self) -> Json {

        // Create a mutable copy of the status struct
        let mut status_copy = self.status.clone();

        // Prepare sensor vector
        let mut temperature_sensors: Vec<spaceapi::TemperatureSensor> = Vec::new();
        let mut people_now_present_sensors: Vec<spaceapi::PeopleNowPresentSensor> = Vec::new();

        // Process registered sensors
        if !self.sensors.is_empty() {

            // Get access to datastore
            let datastore_clone = self.datastore.clone();
            let datastore_lock = datastore_clone.lock().unwrap();

            // Add sensor data
            for sensor_spec in &self.sensors {

                // Retrieve sensor value
                let value: Option<SensorValue> = match datastore_lock.retrieve(&sensor_spec.data_key) {
                    Ok(v) => {
                        match sensor_spec.data_type {
                            SensorValueType::Float => match v.parse::<f64>() {
                                Ok(i) => Some(SensorValue::Float(i)),
                                Err(_) => None,
                            },
                            SensorValueType::Int => match v.parse::<i64>() {
                                Ok(i) => Some(SensorValue::Int(i)),
                                Err(_) => None,
                            },
                            SensorValueType::Bool => None,  // TODO
                        }
                    },
                    Err(_) => None,
                };

                // If value is available, save sensor data
                if value.is_some() {
                    match sensor_spec.sensor {

                        // Create temperature sensor instance
                        TemperatureSensorTemplate { ref unit, ref location, ref name, ref description } => {
                            let sensor = spaceapi::TemperatureSensor {
                                unit: (*unit).clone(),
                                location: (*location).clone(),
                                name: (*name).clone(),
                                description: (*description).clone(),
                                value: match value.unwrap() {
                                    SensorValue::Float(v) => v,
                                    _ => unreachable!(),
                                },
                            };
                            temperature_sensors.push(sensor);
                        },

                        // Create people now present sensor instance
                        PeopleNowPresentSensorTemplate { ref location, ref name, ref names, ref description } => {
                            let sensor = spaceapi::PeopleNowPresentSensor {
                                location: (*location).clone(),
                                name: (*name).clone(),
                                names: (*names).clone(),
                                description: (*description).clone(),
                                value: match value.unwrap() {
                                    SensorValue::Int(v) => v,
                                    _ => unreachable!(),
                                },
                            };
                            people_now_present_sensors.push(sensor);
                        },

                    }
                }

            }

        }

        // Add sensors to status object
        status_copy.sensors = Value(spaceapi::Sensors {
            people_now_present: match people_now_present_sensors.is_empty() {
                true => Absent,
                false => Value(people_now_present_sensors),
            },
            temperature: match temperature_sensors.is_empty() {
                true => Absent,
                false => Value(temperature_sensors),
            },
        });

        // Serialize to JSON
        status_copy.to_json()
    }

}

impl middleware::Handler for SpaceapiServer {

    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        // Get response body
        let body = self.build_response_json().to_string();

        // Create response
        let mut response = Response::with((status::Ok, body));

        // Set headers
        response.set_mut(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())));
        response.set_mut(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])));
        response.set_mut(Header(headers::AccessControlAllowOrigin::Any));

        Ok(response)
    }
}

#[cfg(test)]
mod test {
    extern crate spaceapi;
    extern crate rustc_serialize;

    use std::net::Ipv4Addr;
    use std::sync::{Mutex,Arc};
    use rustc_serialize::json::Json;
    use spaceapi::Optional;
    use super::SpaceapiServer;
    use super::datastore::DataStore;
    use super::redis_store::RedisStore;

    fn get_test_data() -> Json {
        // Create minimal status object
        let status = spaceapi::Status::new(
            "ourspace".to_string(),
            "https://example.com/logo.png".to_string(),
            "https://example.com/".to_string(),
            spaceapi::Location {
                address: Optional::Value("Street 1, Zürich, Switzerland".to_string()),
                lat: 47.123,
                lon: 8.88,
            },
            spaceapi::Contact {
                irc: Optional::Absent,
                twitter: Optional::Absent,
                foursquare: Optional::Absent,
                email: Optional::Value("hi@example.com".to_string()),
            },
            vec![
                "email".to_string(),
                "twitter".to_string(),
            ],
        );

        // Create datastore (TODO: Create dummy store for testing?)
        let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

        // Initialize server
        let server = SpaceapiServer::new(Ipv4Addr::new(127, 0, 0, 1), 3001, status, datastore);

        // Build JSON
        server.build_response_json()
    }

    #[test]
    /// Verify that the result is a JSON object.
    fn verify_json_obj() {
        let json = get_test_data();
        assert!(json.is_object());
    }

    #[test]
    /// Verify that the result has the correct keys.
    fn verify_json_keys() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>
        let keys: Vec<String> = status.keys().cloned().collect();  // Collect the keys
        assert_eq!(keys, ["api", "contact", "issue_report_channels", "location",
                          "logo", "sensors", "space", "state", "url"]);
    }

    #[test]
    /// Verify static data
    fn verify_json_static_data() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>

        // Strings
        assert_eq!(status.get("api").unwrap().as_string().unwrap(), "0.13");
        assert_eq!(status.get("space").unwrap().as_string().unwrap(), "ourspace");
        assert_eq!(status.get("url").unwrap().as_string().unwrap(), "https://example.com/");
        assert_eq!(status.get("logo").unwrap().as_string().unwrap(), "https://example.com/logo.png");

        // Channels
        let channels: &Vec<Json> = status.get("issue_report_channels").unwrap().as_array().unwrap();
        let channel_values: Vec<String> = channels.iter()
                                                  .cloned()
                                                  .map(|c| c.as_string().unwrap().to_string())
                                                  .collect();
        assert_eq!(vec!["email", "twitter"], channel_values);
    }

    #[test]
    /// Verify location
    fn verify_json_location() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let location = status.get("location").unwrap().as_object().unwrap();

        // Verify data
        let address = location.get("address").unwrap().as_string().unwrap();
        assert_eq!("Street 1, Zürich, Switzerland".to_string(), address);
        assert_eq!(47.123, location.get("lat").unwrap().as_f64().unwrap());
        assert_eq!(8.88, location.get("lon").unwrap().as_f64().unwrap());
    }

    #[test]
    /// Verify contact
    fn verify_json_contact() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let contact = status.get("contact").unwrap().as_object().unwrap();

        // Verify data
        let email = contact.get("email").unwrap().as_string().unwrap();
        assert_eq!("hi@example.com".to_string(), email);
    }

    /* TODO: Update
    #[test]
    /// Verify sensor data
    fn verify_json_sensors() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let sensors = status.get("sensors").unwrap().as_object().unwrap();
        let temperature_sensor = sensors.get("temperature").unwrap()
                                        .as_array().unwrap()[0]  // List of sensors
                                        .as_object().unwrap();  // Sensor object
        let people_sensor = sensors.get("people_now_present").unwrap()  // List of sensors
                                        .as_array().unwrap()[0]  // Sensor object
                                        .as_object().unwrap();

        // Verify temperature sensor
        let temp_location = temperature_sensor.get("location").unwrap().as_string().unwrap();
        let temp_name = temperature_sensor.get("name").unwrap().as_string().unwrap();
        let temp_unit = temperature_sensor.get("unit").unwrap().as_string().unwrap();
        let temp_value = temperature_sensor.get("value").unwrap().as_f64().unwrap();
        assert_eq!("Basement".to_string(), temp_location);
        assert_eq!("Raspberry CPU".to_string(), temp_name);
        assert_eq!("°C".to_string(), temp_unit);
        assert_eq!(42.5, temp_value);

        // Verify peoplesensor
        let people_location = people_sensor.get("location").unwrap().as_string().unwrap();
        let people_value = people_sensor.get("value").unwrap().as_u64().unwrap();
        assert_eq!("Hackerspace".to_string(), people_location);
        assert_eq!(23, people_value);
    }
    */

}
<commit_msg>Moved Optional enum to separate module<commit_after>//! The main entry point for the Space API server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

pub mod datastore;
pub mod redis_store;

use std::net::Ipv4Addr;
use std::sync::Mutex;
use std::sync::Arc;

use rustc_serialize::json::{Json, ToJson};
use iron::{Request, Response, IronResult, Iron, Set};
use iron::{status, headers, middleware};
use iron::modifiers::Header;

pub use datastore::DataStore;
use spaceapi::utils::Optional;
use spaceapi::SensorTemplate::{PeopleNowPresentSensorTemplate, TemperatureSensorTemplate};

/// A specification of a sensor.
///
/// The ``sensor`` field contains the static data of a sensor and the
/// ``data_key`` says how to find the sensor value in the datastore.
#[derive(Debug)]
pub struct SensorSpec {
    sensor: spaceapi::SensorTemplate,
    data_key: String,
    data_type: SensorValueType,
}

/// All possible value types a sensor can have.
#[derive(Debug)]
pub enum SensorValueType {
    Int,
    Float,
    Bool,
}

/// The actual sensor values.
#[derive(Debug)]
pub enum SensorValue {
    Int(i64),
    Float(f64),
    Bool(bool),
}

/// 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 datastore.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: spaceapi::Status,
    datastore: Arc<Mutex<Box<DataStore>>>,
    sensors: Vec<SensorSpec>
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, port: u16, status: spaceapi::Status, datastore: Arc<Mutex<Box<DataStore>>>) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: port,
            status: status,
            datastore: datastore,
            sensors: vec![],
        }
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// This call is blocking. It can be interrupted with SIGINT (Ctrl+C).
    pub fn serve(self) {
        let host = self.host;
        let port = self.port;
        println!("Starting HTTP server on http://{}:{}...", host, port);
        Iron::new(self).http((host, port)).unwrap();
    }

    /// Register a new sensor.
    ///
    /// The first argument is a ``spaceapi::Sensor`` instance containing all static data. The
    /// second argument specifies how to get the actual sensor value from the datastore. And the
    /// third argument specifies the data type of the value.
    pub fn register_sensor(&mut self, sensor: spaceapi::SensorTemplate, data_key: String, data_type: SensorValueType) {
        self.sensors.push(
            SensorSpec { sensor: sensor, data_key: data_key, data_type: data_type }
        );
    }

    fn build_response_json(&self) -> Json {

        // Create a mutable copy of the status struct
        let mut status_copy = self.status.clone();

        // Prepare sensor vector
        let mut temperature_sensors: Vec<spaceapi::TemperatureSensor> = Vec::new();
        let mut people_now_present_sensors: Vec<spaceapi::PeopleNowPresentSensor> = Vec::new();

        // Process registered sensors
        if !self.sensors.is_empty() {

            // Get access to datastore
            let datastore_clone = self.datastore.clone();
            let datastore_lock = datastore_clone.lock().unwrap();

            // Add sensor data
            for sensor_spec in &self.sensors {

                // Retrieve sensor value
                let value: Option<SensorValue> = match datastore_lock.retrieve(&sensor_spec.data_key) {
                    Ok(v) => {
                        match sensor_spec.data_type {
                            SensorValueType::Float => match v.parse::<f64>() {
                                Ok(i) => Some(SensorValue::Float(i)),
                                Err(_) => None,
                            },
                            SensorValueType::Int => match v.parse::<i64>() {
                                Ok(i) => Some(SensorValue::Int(i)),
                                Err(_) => None,
                            },
                            SensorValueType::Bool => None,  // TODO
                        }
                    },
                    Err(_) => None,
                };

                // If value is available, save sensor data
                if value.is_some() {
                    match sensor_spec.sensor {

                        // Create temperature sensor instance
                        TemperatureSensorTemplate { ref unit, ref location, ref name, ref description } => {
                            let sensor = spaceapi::TemperatureSensor {
                                unit: (*unit).clone(),
                                location: (*location).clone(),
                                name: (*name).clone(),
                                description: (*description).clone(),
                                value: match value.unwrap() {
                                    SensorValue::Float(v) => v,
                                    _ => unreachable!(),
                                },
                            };
                            temperature_sensors.push(sensor);
                        },

                        // Create people now present sensor instance
                        PeopleNowPresentSensorTemplate { ref location, ref name, ref names, ref description } => {
                            let sensor = spaceapi::PeopleNowPresentSensor {
                                location: (*location).clone(),
                                name: (*name).clone(),
                                names: (*names).clone(),
                                description: (*description).clone(),
                                value: match value.unwrap() {
                                    SensorValue::Int(v) => v,
                                    _ => unreachable!(),
                                },
                            };
                            people_now_present_sensors.push(sensor);
                        },

                    }
                }

            }

        }

        // Add sensors to status object
        status_copy.sensors = Optional::Value(spaceapi::Sensors {
            people_now_present: match people_now_present_sensors.is_empty() {
                true => Optional::Absent,
                false => Optional::Value(people_now_present_sensors),
            },
            temperature: match temperature_sensors.is_empty() {
                true => Optional::Absent,
                false => Optional::Value(temperature_sensors),
            },
        });

        // Serialize to JSON
        status_copy.to_json()
    }

}

impl middleware::Handler for SpaceapiServer {

    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        // Get response body
        let body = self.build_response_json().to_string();

        // Create response
        let mut response = Response::with((status::Ok, body));

        // Set headers
        response.set_mut(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())));
        response.set_mut(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])));
        response.set_mut(Header(headers::AccessControlAllowOrigin::Any));

        Ok(response)
    }
}

#[cfg(test)]
mod test {
    extern crate spaceapi;
    extern crate rustc_serialize;

    use std::net::Ipv4Addr;
    use std::sync::{Mutex,Arc};
    use rustc_serialize::json::Json;
    use spaceapi::Optional;
    use super::SpaceapiServer;
    use super::datastore::DataStore;
    use super::redis_store::RedisStore;

    fn get_test_data() -> Json {
        // Create minimal status object
        let status = spaceapi::Status::new(
            "ourspace".to_string(),
            "https://example.com/logo.png".to_string(),
            "https://example.com/".to_string(),
            spaceapi::Location {
                address: Optional::Value("Street 1, Zürich, Switzerland".to_string()),
                lat: 47.123,
                lon: 8.88,
            },
            spaceapi::Contact {
                irc: Optional::Absent,
                twitter: Optional::Absent,
                foursquare: Optional::Absent,
                email: Optional::Value("hi@example.com".to_string()),
            },
            vec![
                "email".to_string(),
                "twitter".to_string(),
            ],
        );

        // Create datastore (TODO: Create dummy store for testing?)
        let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

        // Initialize server
        let server = SpaceapiServer::new(Ipv4Addr::new(127, 0, 0, 1), 3001, status, datastore);

        // Build JSON
        server.build_response_json()
    }

    #[test]
    /// Verify that the result is a JSON object.
    fn verify_json_obj() {
        let json = get_test_data();
        assert!(json.is_object());
    }

    #[test]
    /// Verify that the result has the correct keys.
    fn verify_json_keys() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>
        let keys: Vec<String> = status.keys().cloned().collect();  // Collect the keys
        assert_eq!(keys, ["api", "contact", "issue_report_channels", "location",
                          "logo", "sensors", "space", "state", "url"]);
    }

    #[test]
    /// Verify static data
    fn verify_json_static_data() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>

        // Strings
        assert_eq!(status.get("api").unwrap().as_string().unwrap(), "0.13");
        assert_eq!(status.get("space").unwrap().as_string().unwrap(), "ourspace");
        assert_eq!(status.get("url").unwrap().as_string().unwrap(), "https://example.com/");
        assert_eq!(status.get("logo").unwrap().as_string().unwrap(), "https://example.com/logo.png");

        // Channels
        let channels: &Vec<Json> = status.get("issue_report_channels").unwrap().as_array().unwrap();
        let channel_values: Vec<String> = channels.iter()
                                                  .cloned()
                                                  .map(|c| c.as_string().unwrap().to_string())
                                                  .collect();
        assert_eq!(vec!["email", "twitter"], channel_values);
    }

    #[test]
    /// Verify location
    fn verify_json_location() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let location = status.get("location").unwrap().as_object().unwrap();

        // Verify data
        let address = location.get("address").unwrap().as_string().unwrap();
        assert_eq!("Street 1, Zürich, Switzerland".to_string(), address);
        assert_eq!(47.123, location.get("lat").unwrap().as_f64().unwrap());
        assert_eq!(8.88, location.get("lon").unwrap().as_f64().unwrap());
    }

    #[test]
    /// Verify contact
    fn verify_json_contact() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let contact = status.get("contact").unwrap().as_object().unwrap();

        // Verify data
        let email = contact.get("email").unwrap().as_string().unwrap();
        assert_eq!("hi@example.com".to_string(), email);
    }

    /* TODO: Update
    #[test]
    /// Verify sensor data
    fn verify_json_sensors() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let sensors = status.get("sensors").unwrap().as_object().unwrap();
        let temperature_sensor = sensors.get("temperature").unwrap()
                                        .as_array().unwrap()[0]  // List of sensors
                                        .as_object().unwrap();  // Sensor object
        let people_sensor = sensors.get("people_now_present").unwrap()  // List of sensors
                                        .as_array().unwrap()[0]  // Sensor object
                                        .as_object().unwrap();

        // Verify temperature sensor
        let temp_location = temperature_sensor.get("location").unwrap().as_string().unwrap();
        let temp_name = temperature_sensor.get("name").unwrap().as_string().unwrap();
        let temp_unit = temperature_sensor.get("unit").unwrap().as_string().unwrap();
        let temp_value = temperature_sensor.get("value").unwrap().as_f64().unwrap();
        assert_eq!("Basement".to_string(), temp_location);
        assert_eq!("Raspberry CPU".to_string(), temp_name);
        assert_eq!("°C".to_string(), temp_unit);
        assert_eq!(42.5, temp_value);

        // Verify peoplesensor
        let people_location = people_sensor.get("location").unwrap().as_string().unwrap();
        let people_value = people_sensor.get("value").unwrap().as_u64().unwrap();
        assert_eq!("Hackerspace".to_string(), people_location);
        assert_eq!(23, people_value);
    }
    */

}
<|endoftext|>


<commit_before>use syscalls::command;

const COUNT: u32 = 0;
const ON: u32 = 1;
const OFF: u32 = 2;
const TOGGLE: u32 = 3;

pub fn count() -> isize {
    unsafe {
        command(8, COUNT, 0)
    }
}

pub fn on(led_num: u32) {
    unsafe {
        command(8, ON, led_num as isize);
    }
}

pub fn off(led_num: u32) {
    unsafe {
        command(8, OFF, led_num as isize);
    }
}

pub fn toggle(led_num: u32) {
    unsafe {
        command(8, TOGGLE, led_num as isize);
    }
}
<commit_msg>Fix led driver number<commit_after>use syscalls::command;

const COUNT: u32 = 0;
const ON: u32 = 1;
const OFF: u32 = 2;
const TOGGLE: u32 = 3;
const LED_DRIVER: u32 = 2;

pub fn count() -> isize {
    unsafe {
        command(LED_DRIVER, COUNT, 0)
    }
}

pub fn on(led_num: u32) {
    unsafe {
        command(LED_DRIVER, ON, led_num as isize);
    }
}

pub fn off(led_num: u32) {
    unsafe {
        command(LED_DRIVER, OFF, led_num as isize);
    }
}

pub fn toggle(led_num: u32) {
    unsafe {
        command(LED_DRIVER, TOGGLE, led_num as isize);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 rs-transducers developers
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */
pub mod transducers;
pub mod applications;

use std::marker::PhantomData;

/// Defines a reducing function from I to O with step errors of E
pub trait Reducing<I, O, E> {
    /// The type of each value after the reducing function
    type Item;

    /// Transducers must call the underlying `init`
    fn init(&mut self) {}

    /// Each step, may fail
    fn step(&mut self, value: I) -> Result<(), E>;

    /// Transducers must call the underlying `complete`
    fn complete(self) -> O;
}

/// Defines a transducer that transforms a reducing function RI into
/// a reducing function RO
pub trait Transducer<RI> {
    type RO;
    fn new(self, reducing_fn: RI) -> Self::RO;
}

// TODO - composing will work very differently
// pub struct ComposedTransducer<AT, BT, B> {
//     a: AT,
//     b: BT,
//     phantom: PhantomData<B>
// }

// impl<A, AT, B, BT, C> Transducer<A, C> for ComposedTransducer<AT, BT, B>
//     where AT: Transducer<A, B>,
//           BT: Transducer<B, C> {
//     #[inline]
//     fn accept(&mut self, value: Option<A>) -> TransductionResult<C> {
//         match self.a.accept(value) {
//             TransductionResult::End => self.b.accept(None),
//             TransductionResult::None => TransductionResult::None,
//             TransductionResult::Some(interim) => self.b.accept(Some(interim))
//         }
//     }
// }

// pub fn compose<A, AT, B, BT, C>(b: BT,
//                                 a: AT) -> ComposedTransducer<AT, BT, B>
//     where AT: Transducer<A, B>,
//           BT: Transducer<B, C> {
//     ComposedTransducer {
//         a: a,
//         b: b,
//         phantom: PhantomData
//     }
// }

#[cfg(test)]
mod test {
    use std::thread;

    use super::transducers;
    use super::applications::vec::Ref;
//    use super::applications::channels::transducing_channel;

    #[test]
    fn test_vec_ref() {
        let source = vec![1, 2, 3];
        let transducer = transducers::map(|x| x + 1);
        let result = source.transduce_ref(transducer).unwrap();
        assert_eq!(vec![2, 3, 4], result);
    }

    // #[test]
    // fn test_vec_drain() {
    //     let source = vec![1, 2, 3, 4, 5];
    //     let transducer = transducers::filter(|x| x % 2 == 0);
    //     let result = source.transduce_drain(transducer);
    //     assert_eq!(vec![2, 4], result);
    // }

    // #[test]
    // fn test_compose() {
    //     let source = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    //     let add_five = transducers::map(|x| x + 5);
    //     let filter_even = transducers::filter(|x| x % 2 == 0);
    //     let combined = super::compose(filter_even, add_five);
    //     let result = source.transduce_ref(combined);
    //     assert_eq!(vec![6, 8, 10, 12, 14], result);
    // }

    // #[test]
    // fn test_partition() {
    //     let source = vec![1, 2, 3, 4, 5, 6];
    //     let transducer = transducers::partition(2);
    //     let result = source.transduce_drain(transducer);
    //     let expected_result:Vec<Vec<usize>> = vec![vec![1, 2], vec![3, 4], vec![5, 6]];
    //     assert_eq!(expected_result, result);
    // }

    // #[test]
    // fn test_take() {
    //     let source = vec![1, 2, 3, 4, 5, 6, 7];
    //     let transducer = transducers::take(5);
    //     let result = source.transduce_drain(transducer);
    //     assert_eq!(vec![1, 2, 3, 4, 5], result);

    //     let source2 = vec![1, 2, 3, 4, 5, 6, 7];
    //     let transducer2 = super::compose(transducers::take(2),
    //                                      transducers::filter(|x| x % 2 == 0));
    //     let result = source2.transduce_drain(transducer2);
    //     assert_eq!(vec![2, 4], result);
    // }

    // #[test]
    // fn test_channels() {
    //     let transducer = super::compose(transducers::partition_all(6),
    //                                     transducers::filter(|x| x % 2 == 0));
    //     let (mut tx, rx) = transducing_channel(transducer);
    //     thread::spawn(move|| {
    //         for i in 0..10 {
    //             tx.send(i).unwrap();
    //         }
    //     });
    //     assert_eq!(vec![0, 2, 4, 6, 8], rx.recv().unwrap());
    // }
}
<commit_msg>Test for channels<commit_after>/*
 * Copyright 2016 rs-transducers developers
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */
pub mod transducers;
pub mod applications;

use std::marker::PhantomData;

/// Defines a reducing function from I to O with step errors of E
pub trait Reducing<I, O, E> {
    /// The type of each value after the reducing function
    type Item;

    /// Transducers must call the underlying `init`
    fn init(&mut self) {}

    /// Each step, may fail
    fn step(&mut self, value: I) -> Result<(), E>;

    /// Transducers must call the underlying `complete`
    fn complete(self) -> O;
}

/// Defines a transducer that transforms a reducing function RI into
/// a reducing function RO
pub trait Transducer<RI> {
    type RO;
    fn new(self, reducing_fn: RI) -> Self::RO;
}

// TODO - composing will work very differently
// pub struct ComposedTransducer<AT, BT, B> {
//     a: AT,
//     b: BT,
//     phantom: PhantomData<B>
// }

// impl<A, AT, B, BT, C> Transducer<A, C> for ComposedTransducer<AT, BT, B>
//     where AT: Transducer<A, B>,
//           BT: Transducer<B, C> {
//     #[inline]
//     fn accept(&mut self, value: Option<A>) -> TransductionResult<C> {
//         match self.a.accept(value) {
//             TransductionResult::End => self.b.accept(None),
//             TransductionResult::None => TransductionResult::None,
//             TransductionResult::Some(interim) => self.b.accept(Some(interim))
//         }
//     }
// }

// pub fn compose<A, AT, B, BT, C>(b: BT,
//                                 a: AT) -> ComposedTransducer<AT, BT, B>
//     where AT: Transducer<A, B>,
//           BT: Transducer<B, C> {
//     ComposedTransducer {
//         a: a,
//         b: b,
//         phantom: PhantomData
//     }
// }

#[cfg(test)]
mod test {
    use std::thread;

    use super::transducers;
    use super::applications::vec::Ref;
    use super::applications::channels::transducing_channel;

    #[test]
    fn test_vec_ref() {
        let source = vec![1, 2, 3];
        let transducer = transducers::map(|x| x + 1);
        let result = source.transduce_ref(transducer).unwrap();
        assert_eq!(vec![2, 3, 4], result);
    }

    // #[test]
    // fn test_vec_drain() {
    //     let source = vec![1, 2, 3, 4, 5];
    //     let transducer = transducers::filter(|x| x % 2 == 0);
    //     let result = source.transduce_drain(transducer);
    //     assert_eq!(vec![2, 4], result);
    // }

    // #[test]
    // fn test_compose() {
    //     let source = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    //     let add_five = transducers::map(|x| x + 5);
    //     let filter_even = transducers::filter(|x| x % 2 == 0);
    //     let combined = super::compose(filter_even, add_five);
    //     let result = source.transduce_ref(combined);
    //     assert_eq!(vec![6, 8, 10, 12, 14], result);
    // }

    // #[test]
    // fn test_partition() {
    //     let source = vec![1, 2, 3, 4, 5, 6];
    //     let transducer = transducers::partition(2);
    //     let result = source.transduce_drain(transducer);
    //     let expected_result:Vec<Vec<usize>> = vec![vec![1, 2], vec![3, 4], vec![5, 6]];
    //     assert_eq!(expected_result, result);
    // }

    // #[test]
    // fn test_take() {
    //     let source = vec![1, 2, 3, 4, 5, 6, 7];
    //     let transducer = transducers::take(5);
    //     let result = source.transduce_drain(transducer);
    //     assert_eq!(vec![1, 2, 3, 4, 5], result);

    //     let source2 = vec![1, 2, 3, 4, 5, 6, 7];
    //     let transducer2 = super::compose(transducers::take(2),
    //                                      transducers::filter(|x| x % 2 == 0));
    //     let result = source2.transduce_drain(transducer2);
    //     assert_eq!(vec![2, 4], result);
    // }

    #[test]
    fn test_channels() {
        let transducer = transducers::map(|x| x + 1);
        let (mut tx, rx) = transducing_channel(transducer);
        thread::spawn(move|| {
            for i in 0..3 {
                tx.send(i).unwrap();
            }
            tx.close().unwrap();
        });
        assert_eq!(1, rx.recv().unwrap());
        assert_eq!(2, rx.recv().unwrap());
        assert_eq!(3, rx.recv().unwrap());
    }
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(concat_idents)]
// #![deny(warnings)]
// #![deny(bad_style)]

extern crate "rustc-serialize" as serialize;
extern crate time;

#[cfg(feature = "postgres")]
extern crate postgres;
extern crate uuid;

mod prelude {
    pub use {
        ToIsPredicate,
        ToOrPredicate,
        ToAndPredicate,
        ToInPredicate,
        ToInRangePredicate,
        ToInequalityPredicate,
        ToExcludePredicate,
        ToLikePredicate,
        ToIsNullPredicate,
        Selectable,
        Queryable,
        Orderable,
        ToSelectQuery,
        Updatable,
        ToFieldUpdate,
        ToInsertValue,
        Deletable,
        ToExpression,
        ToListExpression
    };
}

pub use field::{
    Field,
    NamedField,

    BoolField,
    I8Field,
    I16Field,
    I32Field,
    I64Field,
    F32Field,
    F64Field,
    StringField,
    ByteListField,
    JsonField,
    TimespecField,
};

pub use predicate::{
    Predicate,
    SharedPredicate,
    IsPredicate, ToIsPredicate,
    OrPredicate, ToOrPredicate,
    AndPredicate, ToAndPredicate,
    InPredicate, ToInPredicate,
    InRangePredicate, ToInRangePredicate, InRangeBounds,
    InequalityPredicate, ToInequalityPredicate, Inequality,
    ExcludePredicate, ToExcludePredicate,
    LikePredicate, ToLikePredicate,
    IsNullPredicate, ToIsNullPredicate,
    RawPredicate
};

pub use select_query::{
    Selectable,
    Queryable,
    Orderable,
    SelectQuery,
    SharedSelectQuery,
    ToSelectQuery,
    Select,
    NoResult,
    LimitOne,
    LimitTwo,
    LimitMany
};

pub use update_query::{
    UpdateQuery,
    FieldUpdate,
    FieldUpd,
    Updatable,
    ToFieldUpdate,
};

pub use insert_query::{
    InsertQuery,
    Insertable,
    ToInsertValue,
    InsertValue,
};

pub use delete_query::{
    DeleteQuery,
    Deletable,
};

pub use expression::{
    BoxedExpression,
    UntypedExpression,
    Expression,
    SharedExpression,
    RawExpression,
    ListExpression,
    ToExpression,
    ToListExpression
};

pub use sql::{SqlContext, ToSql, QueryToSql, FromToSql, ToPredicateValue};
#[cfg(feature = "postgres")] pub use sql::{AsPostgresValue};
pub use from::{TableDef, Table, BoxedTable, SharedTable, From, BoxedFrom, SharedFrom};

pub use function::{
    Sum, SumArg,
    Min, MinArg,
    Max, MaxArg,
    Avg, AvgArg,
    Count, CountArg,
    CountAll
};

pub use placeholder::{
    Placeholder
};

macro_rules! with_clone{
    ($slf: ident, $v:ident, $ex:expr) => ({
        let mut $v = $slf.clone();
        $ex;
        $v
    })
}

mod field;
mod predicate;
mod select_query;
mod insert_query;

#[macro_use]
mod delete_query;
mod update_query;
pub mod sql;
mod expression;
mod order_by;
mod from;
mod join;
mod distinct;
mod group_by;
mod function;
mod placeholder;

<commit_msg>feat(lib): expose more to support dt-orm<commit_after>#![feature(core)]
#![feature(concat_idents)]
// #![deny(warnings)]
// #![deny(bad_style)]

extern crate "rustc-serialize" as serialize;
extern crate time;

#[cfg(feature = "postgres")]
extern crate postgres;
extern crate uuid;

mod prelude {
    pub use {
        ToIsPredicate,
        ToOrPredicate,
        ToAndPredicate,
        ToInPredicate,
        ToInRangePredicate,
        ToInequalityPredicate,
        ToExcludePredicate,
        ToLikePredicate,
        ToIsNullPredicate,
        Selectable,
        Queryable,
        Orderable,
        ToSelectQuery,
        Updatable,
        ToFieldUpdate,
        ToInsertValue,
        Deletable,
        ToExpression,
        ToListExpression
    };
}

pub use field::{
    Field,
    BoxedField,
    SharedField,
    NamedField,

    BoolField,
    I8Field,
    I16Field,
    I32Field,
    I64Field,
    F32Field,
    F64Field,
    StringField,
    ByteListField,
    JsonField,
    TimespecField,
};

pub use predicate::{
    Predicate,
    SharedPredicate,
    IsPredicate, ToIsPredicate,
    OrPredicate, ToOrPredicate,
    AndPredicate, ToAndPredicate,
    InPredicate, ToInPredicate,
    InRangePredicate, ToInRangePredicate, InRangeBounds,
    InequalityPredicate, ToInequalityPredicate, Inequality,
    ExcludePredicate, ToExcludePredicate,
    LikePredicate, ToLikePredicate,
    IsNullPredicate, ToIsNullPredicate,
    RawPredicate
};

pub use select_query::{
    Selectable,
    Queryable,
    Orderable,
    SelectQuery,
    SharedSelectQuery,
    ToSelectQuery,
    Select,
    NoResult,
    LimitOne,
    LimitTwo,
    LimitMany
};

pub use update_query::{
    UpdateQuery,
    FieldUpdate,
    FieldUpd,
    Updatable,
    ToFieldUpdate,
};

pub use insert_query::{
    InsertQuery,
    Insertable,
    ToInsertValue,
    InsertValue,
};

pub use delete_query::{
    DeleteQuery,
    Deletable,
};

pub use expression::{
    BoxedExpression,
    UntypedExpression,
    Expression,
    SharedExpression,
    RawExpression,
    ListExpression,
    ToExpression,
    ToListExpression
};

pub use sql::{SqlContext, ToSql, QueryToSql, FromToSql, ToPredicateValue};
#[cfg(feature = "postgres")] pub use sql::{AsPostgresValue};
pub use from::{TableDef, Table, BoxedTable, SharedTable, From, BoxedFrom, SharedFrom};

pub use function::{
    Sum, SumArg,
    Min, MinArg,
    Max, MaxArg,
    Avg, AvgArg,
    Count, CountArg,
    CountAll
};

pub use placeholder::{
    Placeholder
};

macro_rules! with_clone{
    ($slf: ident, $v:ident, $ex:expr) => ({
        let mut $v = $slf.clone();
        $ex;
        $v
    })
}

mod field;
mod predicate;
mod select_query;
mod insert_query;

#[macro_use]
mod delete_query;
mod update_query;
pub mod sql;
mod expression;
mod order_by;
mod from;
mod join;
mod distinct;
mod group_by;
mod function;
mod placeholder;

<|endoftext|>


<commit_before>//! Json String Literal Generator
//!
//! Write json with Rust syntax instead of hard to read inline strings.
//! Results are converted to a `&'static str` at compile-time.
//!
//! # Usage
//!
//! This crate is on [crates.io](https://crates.io/crates/json_str).
//!
//! There are two ways to reference `json_str` in your projects, depending on whether you're on
//! the `stable`/`beta` or `nightly` channels.
//!
//! ## Stable
//!
//! To get started, add `json_str` to your `Cargo.toml`:
//!
//! ```ignore
//! [dependencies]
//! json_str = "*"
//! ```
//!
//! And reference it in your crate root:
//!
//! ```ignore
//! #[macro_use]
//! extern crate json_str;
//! ```
//!
//! ## Nightly
//!
//! To get started, add `json_str` to your `Cargo.toml`:
//!
//! ```ignore
//! [dependencies]
//! json_str = { version = "*", features = "nightly" }
//! ```
//!
//! And reference it in your crate root:
//!
//! ```ignore
//! #![feature(plugin)]
//! #![plugin(json_str)]
//! ```
//!
//! If you're on the `nightly` channel, it's better to use the above `plugin` version with the `nightly`
//! feature because the conversion and sanitisation takes place at compile-time instead of runtime,
//! saving precious runtime cycles.
//!
//! ## Examples
//!
//! The `json_str!` macro will take an inline token tree and return a sanitised json `String`:
//!
//! ```
//! # #![feature(plugin)]
//! # #![plugin(json_str)]
//! # fn main() {
//! let json = json_str!({
//! 	"query": {
//! 		"filtered": {
//! 			"query": {
//! 				"match_all": {}
//! 			},
//! 			"filter": {
//! 				"geo_distance": {
//! 					"distance": "20km",
//! 					"location": {
//! 						"lat": 37.776,
//! 						"lon": -122.41
//! 					}
//! 				}
//! 			}
//! 		}
//! 	}
//! });
//! # }
//! ```
//!
//! This will also work for unquoted keys for something a bit more `rusty`:
//!
//! ```
//! # #![feature(plugin)]
//! # #![plugin(json_str)]
//! # fn main() {
//! let json = json_str!({
//! 	query: {
//! 		filtered: {
//! 			query: {
//! 				match_all: {}
//! 			},
//! 			filter: {
//! 				geo_distance: {
//! 					distance: "20km",
//! 					location: {
//! 						lat: 37.776,
//! 						lon: -122.41
//! 					}
//! 				}
//! 			}
//! 		}
//! 	}
//! });
//! # }
//! ```
//!
//! On `nightly`, there's an additional plugin called `json_lit` that returns a `&'satic str`
//! instead of a `String`, so you can avoid allocating each time. The syntax is otherwise the same
//! as `json_str`:
//!
//! ```
//! # #![feature(plugin)]
//! # #![plugin(json_str)]
//! # fn main() {
//! let json = json_lit!({
//! 	"query": {
//! 		"filtered": {
//! 			"query": {
//! 				"match_all": {}
//! 			},
//! 			"filter": {
//! 				"geo_distance": {
//! 					"distance": "20km",
//! 					"location": {
//! 						"lat": 37.776,
//! 						"lon": -122.41
//! 					}
//! 				}
//! 			}
//! 		}
//! 	}
//! });
//! # }
//! ```
//!
//! For json values that can't be fully determined at compile-time,
//! use [json_macros](https://github.com/tomjakubowski/json_macros) instead.

#![doc(html_root_url = "http://kodraus.github.io/rustdoc/json_str/")]
#![cfg_attr(feature = "nightly", crate_type="dylib")]
#![cfg_attr(feature = "nightly", feature(plugin_registrar, rustc_private, quote, plugin, stmt_expr_attributes))]

#[doc(hidden)]
pub mod parse;

#[cfg(feature = "nightly")]
include!("lib.rs.in");

#[cfg_attr(not(feature = "nightly"), macro_export)]
#[cfg(not(feature = "nightly"))]
macro_rules! json_str {
	($j:tt) => ({
		let json_raw = stringify!($j);
		let mut json = String::with_capacity(json_raw.len());
		$crate::parse::sanitise(json_raw.as_bytes(), &mut json);

		json
	})
}
<commit_msg>fix error in docs<commit_after>//! Json String Literal Generator
//!
//! Write json with Rust syntax instead of hard to read inline strings.
//! This crate doesn't evaluate any expressions, it just converts a Rust _token tree_ into a
//! minified json string.
//!
//! # Usage
//!
//! This crate is on [crates.io](https://crates.io/crates/json_str).
//!
//! There are two ways to reference `json_str` in your projects, depending on whether you're on
//! the `stable`/`beta` or `nightly` channels.
//!
//! ## Stable
//!
//! To get started, add `json_str` to your `Cargo.toml`:
//!
//! ```ignore
//! [dependencies]
//! json_str = "*"
//! ```
//!
//! And reference it in your crate root:
//!
//! ```ignore
//! #[macro_use]
//! extern crate json_str;
//! ```
//!
//! ## Nightly
//!
//! To get started, add `json_str` to your `Cargo.toml`:
//!
//! ```ignore
//! [dependencies]
//! json_str = { version = "*", features = "nightly" }
//! ```
//!
//! And reference it in your crate root:
//!
//! ```ignore
//! #![feature(plugin)]
//! #![plugin(json_str)]
//! ```
//!
//! If you're on the `nightly` channel, it's better to use the above `plugin` version with the `nightly`
//! feature because the conversion and sanitisation takes place at compile-time instead of runtime,
//! saving precious runtime cycles.
//!
//! ## Examples
//!
//! The `json_str!` macro will take an inline token tree and return a sanitised json `String`:
//!
//! ```
//! # #![feature(plugin)]
//! # #![plugin(json_str)]
//! # fn main() {
//! let json = json_str!({
//! 	"query": {
//! 		"filtered": {
//! 			"query": {
//! 				"match_all": {}
//! 			},
//! 			"filter": {
//! 				"geo_distance": {
//! 					"distance": "20km",
//! 					"location": {
//! 						"lat": 37.776,
//! 						"lon": -122.41
//! 					}
//! 				}
//! 			}
//! 		}
//! 	}
//! });
//! # }
//! ```
//!
//! This will also work for unquoted keys for something a bit more `rusty`:
//!
//! ```
//! # #![feature(plugin)]
//! # #![plugin(json_str)]
//! # fn main() {
//! let json = json_str!({
//! 	query: {
//! 		filtered: {
//! 			query: {
//! 				match_all: {}
//! 			},
//! 			filter: {
//! 				geo_distance: {
//! 					distance: "20km",
//! 					location: {
//! 						lat: 37.776,
//! 						lon: -122.41
//! 					}
//! 				}
//! 			}
//! 		}
//! 	}
//! });
//! # }
//! ```
//!
//! On `nightly`, there's an additional plugin called `json_lit` that returns a `&'satic str`
//! instead of a `String`, so you can avoid allocating each time. The syntax is otherwise the same
//! as `json_str`:
//!
//! ```
//! # #![feature(plugin)]
//! # #![plugin(json_str)]
//! # fn main() {
//! let json = json_lit!({
//! 	"query": {
//! 		"filtered": {
//! 			"query": {
//! 				"match_all": {}
//! 			},
//! 			"filter": {
//! 				"geo_distance": {
//! 					"distance": "20km",
//! 					"location": {
//! 						"lat": 37.776,
//! 						"lon": -122.41
//! 					}
//! 				}
//! 			}
//! 		}
//! 	}
//! });
//! # }
//! ```
//!
//! For json values that can't be fully determined at compile-time,
//! use [json_macros](https://github.com/tomjakubowski/json_macros) instead.

#![doc(html_root_url = "http://kodraus.github.io/rustdoc/json_str/")]
#![cfg_attr(feature = "nightly", crate_type="dylib")]
#![cfg_attr(feature = "nightly", feature(plugin_registrar, rustc_private, quote, plugin, stmt_expr_attributes))]

#[doc(hidden)]
pub mod parse;

#[cfg(feature = "nightly")]
include!("lib.rs.in");

#[cfg_attr(not(feature = "nightly"), macro_export)]
#[cfg(not(feature = "nightly"))]
macro_rules! json_str {
	($j:tt) => ({
		let json_raw = stringify!($j);
		let mut json = String::with_capacity(json_raw.len());
		$crate::parse::sanitise(json_raw.as_bytes(), &mut json);

		json
	})
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate rustc_serialize;

/// Width of game board
#[cfg(not(test))] pub const WIDTH: usize = 8;
#[cfg(test)]      pub const WIDTH: usize = 3;
/// Height of game board
#[cfg(not(test))] pub const HEIGHT: usize = 8;
#[cfg(test)]      pub const HEIGHT: usize = 3;
/// Number of rows
#[cfg(not(test))] pub const ROWS: usize = 2;
#[cfg(test)]      pub const ROWS: usize = 1;

const RED: Player = Player::Red;
const BLUE: Player = Player::Blue;

const ROCK: RPS = RPS::Rock;
const PAPER: RPS = RPS::Paper;
const SCISSORS: RPS = RPS::Scissors;

const WIN: Outcome = Outcome::Win;
const LOSE: Outcome = Outcome::Lose;
const DRAW: Outcome = Outcome::Draw;

use rand::{Rng, Rand};

use std::marker::PhantomData;

use moves::{is_valid, Move};
use win_conditions::{WinCondition};
use field::{Field, PovField};
use unit::{Unit, GeneralUnit};

pub mod moves;
pub mod win_conditions;
pub mod unit;
pub mod field;

#[derive(Clone, Debug)]
pub struct Game<T: WinCondition<GeneralUnit>> {
    turns: u32,
    current_turn: Player,
    winner: Option<Player>,
    field: Field<GeneralUnit>,
    rules: Rules<GeneralUnit, T>,
}

impl<T: WinCondition<GeneralUnit>> Game<T> {
    pub fn new(rules: Rules<GeneralUnit, T>) -> Game<T> {
        let mut rows = [[None; WIDTH]; HEIGHT];
        for i in 0..ROWS {
            rows[i] = [Some(RED.random_unit()); WIDTH];
            rows[HEIGHT - i - 1] = [Some(BLUE.random_unit()); WIDTH];
        }
        let field = Field { rows: rows };
        Game {
            turns: 1,
            current_turn: RED,
            winner: None,
            field: field,
            rules: rules,
        }
    }

    pub fn turns(&self) -> u32 { self.turns }
    pub fn current_turn(&self) -> Player { self.current_turn }
    pub fn winner(&self) -> Option<Player> { self.winner }
    pub fn field(&self) -> &Field<GeneralUnit> { &self.field }

    pub fn force_win(&mut self, player: Player) {
        if !self.winner.is_some() { self.winner = Some(player); }
    }

    pub fn perspective(&self, player: Player) -> PovField {
        PovField::from((&self.field, player))
    }

    pub fn make_move(&mut self, movement: Move) -> Result<Option<Outcome>, MoveError> {
        if self.winner.is_some() { return Err(MoveError::GameAlreadyFinished); }

        if !is_valid(movement) {
            return Err(MoveError::DeclinedByMoveCondition);
        }

        let (from_x, from_y) = movement.from;

        if from_x >= WIDTH || from_y >= HEIGHT { return Err(MoveError::PositionOutOfBounds); }

        let attack_outcome;
        let (to_x, to_y);

        if let Some(ref unit) = self.field.rows[from_y][from_x].as_ref() {
            if unit.owner != self.current_turn { return Err(MoveError::WrongOwner); }
            let dist = movement.apply(unit.owner);
            to_x = dist.0;
            to_y = dist.1;
            if to_x >= WIDTH || to_y >= HEIGHT { return Err(MoveError::PositionOutOfBounds); }

            if let Some(ref defender) = self.field.rows[to_y][to_x].as_ref() {
                if defender.owner == self.current_turn { return Err(MoveError::SameOwner); }

                match unit.attack(defender) {
                    Some(res) => {
                        attack_outcome = Some(res);
                    },
                    None => { return Err(MoveError::UnexpextedError); }
                }
            } else {
                attack_outcome = None;
            }

        } else {
            return Err(MoveError::NoUnitInPosition);
        }

        if let Some(outcome) = attack_outcome {
            match outcome {
                WIN => {
                    self.field.rows[to_y][to_x] = self.field.rows[from_y][from_x];
                    self.field.rows[from_y][from_x] = None;
                    self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                },
                LOSE => {
                    self.field.rows[from_y][from_x] = None;
                    self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                },
                DRAW => {
                    let v1 = self.field.rows[from_y][from_x].as_mut().unwrap().visible;
                    let v2 = self.field.rows[to_y][to_x].as_mut().unwrap().visible;

                    if v1 && v2{
                        self.field.rows[from_y][from_x].as_mut().unwrap().rps = rand::random();
                        self.field.rows[to_y][to_x].as_mut().unwrap().rps = rand::random();
                    } else {
                        self.field.rows[from_y][from_x].as_mut().unwrap().visible = true;
                        self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                    }
                }
            }
        } else {
            self.field.rows[to_y][to_x] = self.field.rows[from_y][from_x];
            self.field.rows[from_y][from_x] = None;
        }

        self.winner = self.rules.win_condition.winner(&self.field);
        self.turns += 1;
        self.current_turn = self.current_turn.next();

        Ok(attack_outcome)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MoveError {
    GameAlreadyFinished,
    DeclinedByMoveCondition,
    PositionOutOfBounds,
    WrongOwner,
    NoUnitInPosition,
    SameOwner,
    UnexpextedError,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum Player {
    Red,
    Blue,
}

impl Player {
    fn next(&self) -> Player {
        match *self {
            RED => BLUE,
            BLUE => RED,
        }
    }

    fn unit(&self, rps: RPS) -> GeneralUnit {
        GeneralUnit::new(rps, *self)
    }

    fn random_unit(&self) -> GeneralUnit {
        self.unit(rand::random())
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum RPS {
    Rock,
    Paper,
    Scissors,
}

impl RPS {
    fn attack(&self, opponent: RPS) -> Outcome {
        match (*self, opponent) {
            (PAPER, ROCK) | (ROCK, SCISSORS) | (SCISSORS, PAPER) => WIN,
            (ROCK, PAPER) | (SCISSORS, ROCK) | (PAPER, SCISSORS) => LOSE,
            _ => DRAW,
        }
    }
}

impl Rand for RPS {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        match rng.next_u32() % 3 {
            0 => ROCK,
            1 => PAPER,
            2 => SCISSORS,
            _ => { unreachable!(); }
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Outcome {
    Win,
    Lose,
    Draw,
}

#[derive(Clone, Debug)]
pub struct Rules<K, T: WinCondition<K>> where K: Unit {
    pub win_condition: T,
    phantom_data: PhantomData<K>,
}

impl<K: Unit, T: WinCondition<K>> Rules<K, T> {
    pub fn new(win_condition: T) -> Rules<K, T> {
        Rules {
            win_condition: win_condition,
            phantom_data: PhantomData,
        }
    }
}

#[test]
fn basic_test() {
    use moves::{Direction};
    use win_conditions::EliminateCondition;

    let rules = Rules::new(EliminateCondition);
    let mut game = Game::new(rules);
    let move1 = Move::new(0, 0, Direction::Forward);

    assert_eq!(game.make_move(move1), Ok(None));

    assert_eq!(game.make_move(move1), Err(MoveError::NoUnitInPosition));

    let move2 = Move::new(0, 2, Direction::Forward);
    assert!(game.make_move(move2).unwrap().is_some());
}
<commit_msg>Correct game generation<commit_after>extern crate rand;
extern crate rustc_serialize;

/// Width of game board
#[cfg(not(test))] pub const WIDTH: usize = 8;
#[cfg(test)]      pub const WIDTH: usize = 3;
/// Height of game board
#[cfg(not(test))] pub const HEIGHT: usize = 8;
#[cfg(test)]      pub const HEIGHT: usize = 3;
/// Number of rows
#[cfg(not(test))] pub const ROWS: usize = 2;
#[cfg(test)]      pub const ROWS: usize = 1;

const RED: Player = Player::Red;
const BLUE: Player = Player::Blue;

const ROCK: RPS = RPS::Rock;
const PAPER: RPS = RPS::Paper;
const SCISSORS: RPS = RPS::Scissors;

const WIN: Outcome = Outcome::Win;
const LOSE: Outcome = Outcome::Lose;
const DRAW: Outcome = Outcome::Draw;

use rand::{Rng, Rand};

use std::marker::PhantomData;

use moves::{is_valid, Move};
use win_conditions::{WinCondition};
use field::{Field, PovField};
use unit::{Unit, GeneralUnit};

pub mod moves;
pub mod win_conditions;
pub mod unit;
pub mod field;

#[derive(Clone, Debug)]
pub struct Game<T: WinCondition<GeneralUnit>> {
    turns: u32,
    current_turn: Player,
    winner: Option<Player>,
    field: Field<GeneralUnit>,
    rules: Rules<GeneralUnit, T>,
}

impl<T: WinCondition<GeneralUnit>> Game<T> {
    pub fn new(rules: Rules<GeneralUnit, T>) -> Game<T> {
        let mut rows = [[None; WIDTH]; HEIGHT];
        for i in 0..ROWS {
            rows[i] = Self::random_row(RED);
            rows[HEIGHT - i - 1] = Self::random_row(BLUE);
        }
        let field = Field { rows: rows };
        Game {
            turns: 1,
            current_turn: RED,
            winner: None,
            field: field,
            rules: rules,
        }
    }

    fn random_row(player: Player) -> [Option<GeneralUnit>; WIDTH] {
        let mut res = [None; WIDTH];
        for i in 0..WIDTH {
            res[i] = Some(player.random_unit());
        }
        res
    }

    pub fn turns(&self) -> u32 { self.turns }
    pub fn current_turn(&self) -> Player { self.current_turn }
    pub fn winner(&self) -> Option<Player> { self.winner }
    pub fn field(&self) -> &Field<GeneralUnit> { &self.field }

    pub fn force_win(&mut self, player: Player) {
        if !self.winner.is_some() { self.winner = Some(player); }
    }

    pub fn perspective(&self, player: Player) -> PovField {
        PovField::from((&self.field, player))
    }

    pub fn make_move(&mut self, movement: Move) -> Result<Option<Outcome>, MoveError> {
        if self.winner.is_some() { return Err(MoveError::GameAlreadyFinished); }

        if !is_valid(movement) {
            return Err(MoveError::DeclinedByMoveCondition);
        }

        let (from_x, from_y) = movement.from;

        if from_x >= WIDTH || from_y >= HEIGHT { return Err(MoveError::PositionOutOfBounds); }

        let attack_outcome;
        let (to_x, to_y);

        if let Some(ref unit) = self.field.rows[from_y][from_x].as_ref() {
            if unit.owner != self.current_turn { return Err(MoveError::WrongOwner); }
            let dist = movement.apply(unit.owner);
            to_x = dist.0;
            to_y = dist.1;
            if to_x >= WIDTH || to_y >= HEIGHT { return Err(MoveError::PositionOutOfBounds); }

            if let Some(ref defender) = self.field.rows[to_y][to_x].as_ref() {
                if defender.owner == self.current_turn { return Err(MoveError::SameOwner); }

                match unit.attack(defender) {
                    Some(res) => {
                        attack_outcome = Some(res);
                    },
                    None => { return Err(MoveError::UnexpextedError); }
                }
            } else {
                attack_outcome = None;
            }

        } else {
            return Err(MoveError::NoUnitInPosition);
        }

        if let Some(outcome) = attack_outcome {
            match outcome {
                WIN => {
                    self.field.rows[to_y][to_x] = self.field.rows[from_y][from_x];
                    self.field.rows[from_y][from_x] = None;
                    self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                },
                LOSE => {
                    self.field.rows[from_y][from_x] = None;
                    self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                },
                DRAW => {
                    let v1 = self.field.rows[from_y][from_x].as_mut().unwrap().visible;
                    let v2 = self.field.rows[to_y][to_x].as_mut().unwrap().visible;

                    if v1 && v2{
                        self.field.rows[from_y][from_x].as_mut().unwrap().rps = rand::random();
                        self.field.rows[to_y][to_x].as_mut().unwrap().rps = rand::random();
                    } else {
                        self.field.rows[from_y][from_x].as_mut().unwrap().visible = true;
                        self.field.rows[to_y][to_x].as_mut().unwrap().visible = true;
                    }
                }
            }
        } else {
            self.field.rows[to_y][to_x] = self.field.rows[from_y][from_x];
            self.field.rows[from_y][from_x] = None;
        }

        self.winner = self.rules.win_condition.winner(&self.field);
        self.turns += 1;
        self.current_turn = self.current_turn.next();

        Ok(attack_outcome)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MoveError {
    GameAlreadyFinished,
    DeclinedByMoveCondition,
    PositionOutOfBounds,
    WrongOwner,
    NoUnitInPosition,
    SameOwner,
    UnexpextedError,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum Player {
    Red,
    Blue,
}

impl Player {
    fn next(&self) -> Player {
        match *self {
            RED => BLUE,
            BLUE => RED,
        }
    }

    fn unit(&self, rps: RPS) -> GeneralUnit {
        GeneralUnit::new(rps, *self)
    }

    fn random_unit(&self) -> GeneralUnit {
        self.unit(rand::random())
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum RPS {
    Rock,
    Paper,
    Scissors,
}

impl RPS {
    fn attack(&self, opponent: RPS) -> Outcome {
        match (*self, opponent) {
            (PAPER, ROCK) | (ROCK, SCISSORS) | (SCISSORS, PAPER) => WIN,
            (ROCK, PAPER) | (SCISSORS, ROCK) | (PAPER, SCISSORS) => LOSE,
            _ => DRAW,
        }
    }
}

impl Rand for RPS {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        match rng.next_u32() % 3 {
            0 => ROCK,
            1 => PAPER,
            2 => SCISSORS,
            _ => { unreachable!(); }
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Outcome {
    Win,
    Lose,
    Draw,
}

#[derive(Clone, Debug)]
pub struct Rules<K, T: WinCondition<K>> where K: Unit {
    pub win_condition: T,
    phantom_data: PhantomData<K>,
}

impl<K: Unit, T: WinCondition<K>> Rules<K, T> {
    pub fn new(win_condition: T) -> Rules<K, T> {
        Rules {
            win_condition: win_condition,
            phantom_data: PhantomData,
        }
    }
}

#[test]
fn basic_test() {
    use moves::{Direction};
    use win_conditions::EliminateCondition;

    let rules = Rules::new(EliminateCondition);
    let mut game = Game::new(rules);
    let move1 = Move::new(0, 0, Direction::Forward);

    assert_eq!(game.make_move(move1), Ok(None));

    assert_eq!(game.make_move(move1), Err(MoveError::NoUnitInPosition));

    let move2 = Move::new(0, 2, Direction::Forward);
    assert!(game.make_move(move2).unwrap().is_some());
}
<|endoftext|>


<commit_before>extern crate xmltree;
extern crate hyper;
extern crate url;
extern crate regex;

use hyper::Client;
use hyper::header::Connection;
use std::io::Read;
use std::io::Write;
use regex::Regex;

/// Turns "123" into 123
fn intify(instr: &str) -> u32{
    // TODO: Better error handling
    instr.to_owned().parse::<u32>().unwrap()
}

/// Used for air-date of an episode etc
#[derive(Debug,Clone)]
pub struct Date {
    pub year: u32,
    pub month: u32,
    pub day: u32,
}

fn dateify(instr: &str) -> Option<Date>{
    let chunks:Vec<&str> = instr.split("-").collect();

    Some(Date{
        year: intify(&chunks[0]),
        month: intify(&chunks[1]),
        day: intify(&chunks[2]),
    })
}

/// Errors in contacting TheTVDB
#[derive(Debug)]
pub enum TvdbError {
    SeriesNotFound,
    CommunicationError{reason: String},
    DataError{reason: String},
    Cancelled,
}

/// Series ID from TheTVDB.com, along with language
#[derive(Debug,Clone)]
pub struct EpisodeId{
    seriesid: u32,
    language: String,
}

impl EpisodeId{
    pub fn new(seriesid: u32, lang: &str) -> EpisodeId{
        EpisodeId{
            seriesid: seriesid,
            language: lang.to_owned(),
        }
    }
}

impl From<u32> for EpisodeId{
    fn from(x: u32) -> Self{
        EpisodeId{seriesid: x, language: "en".to_owned()}
    }
}

impl From<SeriesSearchResult> for EpisodeId{
    fn from(x: SeriesSearchResult) -> Self{
        EpisodeId{seriesid: x.seriesid, language: x.language}
    }
}

/// Series info as returned from TheTVDB's series search method:
/// http://www.thetvdb.com/wiki/index.php?title=API:GetSeries
#[derive(Debug,Clone)]
pub struct SeriesSearchResult{
    /// TheTVDB's series ID ('seriesid' is preferred over 'id' from XML response)
    pub seriesid: u32,

    /// Series name in the language indicated by `language`
    pub seriesname: String,

    /// Language this episode information is in
    pub language: String,

    /// Description of series
    pub overview: Option<String>,

    /// Relative path to the highest rated banner
    pub banner: Option<String>,

    /// [IMDB](http://www.imdb.com/) ID for this series
    pub imdb_id: Option<String>,

    /// First aired date
    pub firstaired: Option<Date>,

    /// Network this series aired on
    pub network: Option<String>,

    /// [zap2it](http://zap2it.com/) ID for this series
    pub zap2it_id: Option<String>,
}

/// Base episode record,
/// http://www.thetvdb.com/wiki/index.php?title=API:Base_Episode_Record
#[derive(Debug,Clone)]
pub struct EpisodeInfo{
    pub id: u32, //id
    // Combined_episodenumber
    // Combined_season
    // DVD_chapter
    // DVD_discid
    // DVD_episodenumber
    // DVD_season
    // Director
    // EpImgFlag
    pub episodename: String, // EpisodeName
    // EpisodeNumber
    // FirstAired
    // GuestStars
    // IMDB_ID
    // Language
    // Overview
    // ProductionCode
    // Rating
    // RatingCount
    // SeasonNumber
    // Writer
    // absolute_number
    // airsafter_season
    // airsbefore_episode
    // airsbefore_season
    // filename
    // lastupdated
    // seasonid
    // seriesid
    // thumb_added
    // thumb_height
    // thumb_width
}

fn get_xmltree_from_url(url: hyper::Url) -> Result<xmltree::Element, TvdbError>{
    // Check if URL is in cache
    let urlstr = url.serialize();
    let re = Regex::new("[^a-zA-Z0-9_-]+").unwrap();
    let cachefile = format!("cache/cache__{}", re.replace_all(&urlstr, "_"));

    let mut body = Vec::new();

    if std::path::Path::new(&cachefile).exists() {
        println!("Reading from cached path");
        let f = std::fs::File::open(&cachefile).ok().expect("failed to open cache file");
        let mut reader = std::io::BufReader::new(f);
        reader.read_to_end(&mut body).unwrap();
    } else {
        let client = Client::new();
        let res = client.get(url)
            .header(Connection::close())
            .send();

        let mut res = match res {
            Err(e) => return Err(TvdbError::CommunicationError{reason: format!("Error contacting TVDB: {}", e)}), // FIXME: http://stackoverflow.com/questions/28911833/error-handling-best-practices
            Ok(r) => r
        };

        // Read the Response.
        res.read_to_end(&mut body).expect("Failed to read response");
    }

    {
        println!("Saving XML to {}", cachefile);
        std::fs::create_dir_all("cache").expect("Failed to create cache dir");
        let mut f = std::fs::File::create(cachefile).ok().expect("Failed to create file");
        f.write_all(&mut body).ok().unwrap();
    }

    // Parse XML
    let bs = String::from_utf8(body).unwrap();
    let tree = xmltree::Element::parse(bs.as_bytes());

    return Ok(tree);
}

/// Main interface
#[derive(Debug,Clone)]
pub struct Tvdb{
    pub key: String,
}

impl Tvdb{
    /// Initalise API with the given API key. A key can be aquired via
    /// the [API Key Registration page](http://thetvdb.com/?tab=apiregister)
    pub fn new(key: String) -> Tvdb{
        Tvdb{key: key}
    }

    /// Searches for a given series name.
    pub fn search(&self, seriesname: String, lang: String) -> Result<Vec<SeriesSearchResult>, TvdbError>{
        let params = url::form_urlencoded::serialize([("seriesname", &seriesname), ("language", &lang)].iter());
        let formatted_url = format!("http://thetvdb.com/api/GetSeries.php?{}", params);
        let url = hyper::Url::parse(&formatted_url).ok().expect("invalid URL");
        println!("Getting {}", url);

        let tree = try!(get_xmltree_from_url(url));

        // Convert XML into structs
        let mut results : Vec<SeriesSearchResult> = vec![];

        for child in tree.children.iter(){

            fn get_text(child: &xmltree::Element, x: &str) -> Option<String>{
                child.get_child(x).and_then(|id_child| id_child.text.clone())
            }

            let r = SeriesSearchResult{
                seriesid:   intify(&get_text(child, "seriesid").expect("Search result XML missing 'seriesid' element")),
                seriesname: get_text(child, "SeriesName").expect("Search result XML Missing 'SeriesName' element"),
                language:   get_text(child, "language").expect("Search result XML missing 'language' element"),
                overview:   get_text(child, "Overview"),
                banner:     get_text(child, "banner"),
                imdb_id:    get_text(child, "IMDB_ID"),
                firstaired: get_text(child, "FirstAired").and_then(|x| dateify(&x)),
                network:    get_text(child, "Network"),
                zap2it_id:  get_text(child, "zap2it_id"),
            };

            results.push(r);
        }

        if results.is_empty(){
            return Err(TvdbError::SeriesNotFound);
        }

        return Ok(results);
    }

    fn episode_inner(&self, epid: EpisodeId, season: u32, episode: u32) -> Result<EpisodeInfo, TvdbError>{
        // <mirrorpath>/api/<apikey>/series/{seriesid}/default/{season}/{episode}/{language}.xml

        let formatted_url = format!("http://thetvdb.com/api/{apikey}/series/{seriesid}/default/{season}/{episode}/{language}.xml",
                                    apikey=self.key,
                                    seriesid=epid.seriesid,
                                    language=epid.language,
                                    season=season,
                                    episode=episode,
                                    );
        let url = hyper::Url::parse(&formatted_url).ok().expect("invalid URL");
        println!("Getting {}", url);

        // Perform request
        let tree = try!(get_xmltree_from_url(url));
        let root = tree.children.first().unwrap();

        fn get_text(child: &xmltree::Element, x: &str) -> Option<String>{
            child.get_child(x).and_then(|id_child| id_child.text.clone())
        }

        // Convert XML into struct
        Ok(EpisodeInfo{
            id: intify(&get_text(root, "id").unwrap()),
            episodename: get_text(root, "EpisodeName").unwrap(),
        })
    }

    /// Get episode information for given season/episode number
    pub fn episode<T: Into<EpisodeId>>(&self, epid: T, season: u32, episode: u32) -> Result<EpisodeInfo, TvdbError>{
        self.episode_inner(epid.into(), season, episode)
    }
}

#[cfg(test)]
mod test{
    use super::*;

    const APIKEY: &'static str = "0629B785CE550C8D";

    #[test]
    fn search() {
        let api = Tvdb::new(APIKEY.to_owned());
        let sr = api.search("scrubs".to_owned(), "en".to_owned());
        assert!(sr.ok().unwrap()[0].seriesname == "Scrubs");
    }

    #[test]
    fn nonexist() {
        let api = Tvdb::new(APIKEY.to_owned());
        let sr = api.search("ladlkgdklfgsdfglk".to_owned(), "en".to_owned());
        assert!(sr.is_err());
    }

    #[test]
    fn lookup_by_epid(){
        let api = Tvdb::new(APIKEY.to_owned());
        let ep = api.episode(EpisodeId::new(76156, "en"), 1, 2).ok().unwrap();
        assert!(ep.episodename == "My Mentor");
    }

    #[test]
    fn lookup_by_u32(){
        let api = Tvdb::new(APIKEY.to_owned());
        let ep = api.episode(76156, 1, 2).ok().unwrap();
        assert!(ep.episodename == "My Mentor");
    }

    #[test]
    fn epinfo_default(){
        let api = Tvdb::new(APIKEY.to_owned());
        let sr = api.search("scrubs".to_owned(), "en".to_owned()).ok().unwrap();
        let ep = api.episode(sr[0].clone(), 1, 2).ok().unwrap();
        assert!(ep.episodename == "My Mentor");
    }

}
<commit_msg>Remove use aliases for things only used once<commit_after>extern crate xmltree;
extern crate hyper;
extern crate url;
extern crate regex;

use std::io::{Read,Write};

/// Turns "123" into 123
fn intify(instr: &str) -> u32{
    // TODO: Better error handling
    instr.to_owned().parse::<u32>().unwrap()
}

/// Used for air-date of an episode etc
#[derive(Debug,Clone)]
pub struct Date {
    pub year: u32,
    pub month: u32,
    pub day: u32,
}

fn dateify(instr: &str) -> Option<Date>{
    let chunks:Vec<&str> = instr.split("-").collect();

    Some(Date{
        year: intify(&chunks[0]),
        month: intify(&chunks[1]),
        day: intify(&chunks[2]),
    })
}

/// Errors in contacting TheTVDB
#[derive(Debug)]
pub enum TvdbError {
    SeriesNotFound,
    CommunicationError{reason: String},
    DataError{reason: String},
    Cancelled,
}

/// Series ID from TheTVDB.com, along with language
#[derive(Debug,Clone)]
pub struct EpisodeId{
    seriesid: u32,
    language: String,
}

impl EpisodeId{
    pub fn new(seriesid: u32, lang: &str) -> EpisodeId{
        EpisodeId{
            seriesid: seriesid,
            language: lang.to_owned(),
        }
    }
}

impl From<u32> for EpisodeId{
    fn from(x: u32) -> Self{
        EpisodeId{seriesid: x, language: "en".to_owned()}
    }
}

impl From<SeriesSearchResult> for EpisodeId{
    fn from(x: SeriesSearchResult) -> Self{
        EpisodeId{seriesid: x.seriesid, language: x.language}
    }
}

/// Series info as returned from TheTVDB's series search method:
/// http://www.thetvdb.com/wiki/index.php?title=API:GetSeries
#[derive(Debug,Clone)]
pub struct SeriesSearchResult{
    /// TheTVDB's series ID ('seriesid' is preferred over 'id' from XML response)
    pub seriesid: u32,

    /// Series name in the language indicated by `language`
    pub seriesname: String,

    /// Language this episode information is in
    pub language: String,

    /// Description of series
    pub overview: Option<String>,

    /// Relative path to the highest rated banner
    pub banner: Option<String>,

    /// [IMDB](http://www.imdb.com/) ID for this series
    pub imdb_id: Option<String>,

    /// First aired date
    pub firstaired: Option<Date>,

    /// Network this series aired on
    pub network: Option<String>,

    /// [zap2it](http://zap2it.com/) ID for this series
    pub zap2it_id: Option<String>,
}

/// Base episode record,
/// http://www.thetvdb.com/wiki/index.php?title=API:Base_Episode_Record
#[derive(Debug,Clone)]
pub struct EpisodeInfo{
    pub id: u32, //id
    // Combined_episodenumber
    // Combined_season
    // DVD_chapter
    // DVD_discid
    // DVD_episodenumber
    // DVD_season
    // Director
    // EpImgFlag
    pub episodename: String, // EpisodeName
    // EpisodeNumber
    // FirstAired
    // GuestStars
    // IMDB_ID
    // Language
    // Overview
    // ProductionCode
    // Rating
    // RatingCount
    // SeasonNumber
    // Writer
    // absolute_number
    // airsafter_season
    // airsbefore_episode
    // airsbefore_season
    // filename
    // lastupdated
    // seasonid
    // seriesid
    // thumb_added
    // thumb_height
    // thumb_width
}

fn get_xmltree_from_url(url: hyper::Url) -> Result<xmltree::Element, TvdbError>{
    // Check if URL is in cache
    let urlstr = url.serialize();
    let re = regex::Regex::new("[^a-zA-Z0-9_-]+").unwrap();
    let cachefile = format!("cache/cache__{}", re.replace_all(&urlstr, "_"));

    let mut body = Vec::new();

    if std::path::Path::new(&cachefile).exists() {
        println!("Reading from cached path");
        let f = std::fs::File::open(&cachefile).ok().expect("failed to open cache file");
        let mut reader = std::io::BufReader::new(f);
        reader.read_to_end(&mut body).unwrap();
    } else {
        let client = hyper::Client::new();
        let res = client.get(url)
            .header(hyper::header::Connection::close())
            .send();

        let mut res = match res {
            Err(e) => return Err(TvdbError::CommunicationError{reason: format!("Error contacting TVDB: {}", e)}), // FIXME: http://stackoverflow.com/questions/28911833/error-handling-best-practices
            Ok(r) => r
        };

        // Read the Response.
        res.read_to_end(&mut body).expect("Failed to read response");
    }

    {
        println!("Saving XML to {}", cachefile);
        std::fs::create_dir_all("cache").expect("Failed to create cache dir");
        let mut f = std::fs::File::create(cachefile).ok().expect("Failed to create file");
        f.write_all(&mut body).ok().unwrap();
    }

    // Parse XML
    let bs = String::from_utf8(body).unwrap();
    let tree = xmltree::Element::parse(bs.as_bytes());

    return Ok(tree);
}

/// Main interface
#[derive(Debug,Clone)]
pub struct Tvdb{
    pub key: String,
}

impl Tvdb{
    /// Initalise API with the given API key. A key can be aquired via
    /// the [API Key Registration page](http://thetvdb.com/?tab=apiregister)
    pub fn new(key: String) -> Tvdb{
        Tvdb{key: key}
    }

    /// Searches for a given series name.
    pub fn search(&self, seriesname: String, lang: String) -> Result<Vec<SeriesSearchResult>, TvdbError>{
        let params = url::form_urlencoded::serialize([("seriesname", &seriesname), ("language", &lang)].iter());
        let formatted_url = format!("http://thetvdb.com/api/GetSeries.php?{}", params);
        let url = hyper::Url::parse(&formatted_url).ok().expect("invalid URL");
        println!("Getting {}", url);

        let tree = try!(get_xmltree_from_url(url));

        // Convert XML into structs
        let mut results : Vec<SeriesSearchResult> = vec![];

        for child in tree.children.iter(){

            fn get_text(child: &xmltree::Element, x: &str) -> Option<String>{
                child.get_child(x).and_then(|id_child| id_child.text.clone())
            }

            let r = SeriesSearchResult{
                seriesid:   intify(&get_text(child, "seriesid").expect("Search result XML missing 'seriesid' element")),
                seriesname: get_text(child, "SeriesName").expect("Search result XML Missing 'SeriesName' element"),
                language:   get_text(child, "language").expect("Search result XML missing 'language' element"),
                overview:   get_text(child, "Overview"),
                banner:     get_text(child, "banner"),
                imdb_id:    get_text(child, "IMDB_ID"),
                firstaired: get_text(child, "FirstAired").and_then(|x| dateify(&x)),
                network:    get_text(child, "Network"),
                zap2it_id:  get_text(child, "zap2it_id"),
            };

            results.push(r);
        }

        if results.is_empty(){
            return Err(TvdbError::SeriesNotFound);
        }

        return Ok(results);
    }

    fn episode_inner(&self, epid: EpisodeId, season: u32, episode: u32) -> Result<EpisodeInfo, TvdbError>{
        // <mirrorpath>/api/<apikey>/series/{seriesid}/default/{season}/{episode}/{language}.xml

        let formatted_url = format!("http://thetvdb.com/api/{apikey}/series/{seriesid}/default/{season}/{episode}/{language}.xml",
                                    apikey=self.key,
                                    seriesid=epid.seriesid,
                                    language=epid.language,
                                    season=season,
                                    episode=episode,
                                    );
        let url = hyper::Url::parse(&formatted_url).ok().expect("invalid URL");
        println!("Getting {}", url);

        // Perform request
        let tree = try!(get_xmltree_from_url(url));
        let root = tree.children.first().unwrap();

        fn get_text(child: &xmltree::Element, x: &str) -> Option<String>{
            child.get_child(x).and_then(|id_child| id_child.text.clone())
        }

        // Convert XML into struct
        Ok(EpisodeInfo{
            id: intify(&get_text(root, "id").unwrap()),
            episodename: get_text(root, "EpisodeName").unwrap(),
        })
    }

    /// Get episode information for given season/episode number
    pub fn episode<T: Into<EpisodeId>>(&self, epid: T, season: u32, episode: u32) -> Result<EpisodeInfo, TvdbError>{
        self.episode_inner(epid.into(), season, episode)
    }
}

#[cfg(test)]
mod test{
    use super::*;

    const APIKEY: &'static str = "0629B785CE550C8D";

    #[test]
    fn search() {
        let api = Tvdb::new(APIKEY.to_owned());
        let sr = api.search("scrubs".to_owned(), "en".to_owned());
        assert!(sr.ok().unwrap()[0].seriesname == "Scrubs");
    }

    #[test]
    fn nonexist() {
        let api = Tvdb::new(APIKEY.to_owned());
        let sr = api.search("ladlkgdklfgsdfglk".to_owned(), "en".to_owned());
        assert!(sr.is_err());
    }

    #[test]
    fn lookup_by_epid(){
        let api = Tvdb::new(APIKEY.to_owned());
        let ep = api.episode(EpisodeId::new(76156, "en"), 1, 2).ok().unwrap();
        assert!(ep.episodename == "My Mentor");
    }

    #[test]
    fn lookup_by_u32(){
        let api = Tvdb::new(APIKEY.to_owned());
        let ep = api.episode(76156, 1, 2).ok().unwrap();
        assert!(ep.episodename == "My Mentor");
    }

    #[test]
    fn epinfo_default(){
        let api = Tvdb::new(APIKEY.to_owned());
        let sr = api.search("scrubs".to_owned(), "en".to_owned()).ok().unwrap();
        let ep = api.episode(sr[0].clone(), 1, 2).ok().unwrap();
        assert!(ep.episodename == "My Mentor");
    }

}
<|endoftext|>


<commit_before>//! Rust friendly bindings to the various *nix system functions.
//!
//! Modules are structured according to the C header file that they would be
//! defined in.
#![crate_name = "nix"]
#![cfg(unix)]
#![allow(non_camel_case_types)]
// latest bitflags triggers a rustc bug with cross-crate macro expansions causing dead_code
// warnings even though the macro expands into something with allow(dead_code)
#![allow(dead_code)]
#![cfg_attr(test, deny(warnings))]
#![recursion_limit = "500"]
#![deny(unused_qualifications)]

extern crate bytes;
#[macro_use]
extern crate bitflags;

#[macro_use]
extern crate cfg_if;
extern crate void;

#[macro_use] mod macros;

pub extern crate libc;

use errno::Errno;

pub mod errno;
pub mod features;
pub mod fcntl;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod mount;

#[cfg(any(target_os = "dragonfly",
          target_os = "freebsd",
          target_os = "fushsia",
          target_os = "linux",
          target_os = "netbsd"))]
pub mod mqueue;

pub mod pty;

pub mod poll;

pub mod net;

#[cfg(any(target_os = "dragonfly",
          target_os = "freebsd",
          target_os = "ios",
          target_os = "linux",
          target_os = "macos",
          target_os = "netbsd",
          target_os = "openbsd"))]
pub mod ifaddrs;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod sched;

pub mod sys;

// This can be implemented for other platforms as soon as libc
// provides bindings for them.
#[cfg(all(target_os = "linux",
          any(target_arch = "x86", target_arch = "x86_64")))]
pub mod ucontext;

pub mod unistd;

/*
 *
 * ===== Result / Error =====
 *
 */

use libc::c_char;
use std::{ptr, result};
use std::ffi::{CStr, OsStr};
use std::path::{Path, PathBuf};
use std::os::unix::ffi::OsStrExt;
use std::fmt;
use std::error;
use libc::PATH_MAX;

/// Nix Result Type
pub type Result<T> = result::Result<T, Error>;

/// Nix Error Type
///
/// The nix error type provides a common way of dealing with
/// various system system/libc calls that might fail.  Each
/// error has a corresponding errno (usually the one from the
/// underlying OS) to which it can be mapped in addition to
/// implementing other common traits.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Error {
    Sys(Errno),
    InvalidPath,
    /// The operation involved a conversion to Rust's native String type, which failed because the
    /// string did not contain all valid UTF-8.
    InvalidUtf8,
    /// The operation is not supported by Nix, in this instance either use the libc bindings or
    /// consult the module documentation to see if there is a more appropriate interface available.
    UnsupportedOperation,
}

impl Error {

    /// Create a nix Error from a given errno
    pub fn from_errno(errno: Errno) -> Error {
        Error::Sys(errno)
    }

    /// Get the current errno and convert it to a nix Error
    pub fn last() -> Error {
        Error::Sys(Errno::last())
    }

    /// Create a new invalid argument error (`EINVAL`)
    pub fn invalid_argument() -> Error {
        Error::Sys(Errno::EINVAL)
    }

}

impl From<Errno> for Error {
    fn from(errno: Errno) -> Error { Error::from_errno(errno) }
}

impl From<std::string::FromUtf8Error> for Error {
    fn from(_: std::string::FromUtf8Error) -> Error { Error::InvalidUtf8 }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::InvalidPath => "Invalid path",
            Error::InvalidUtf8 => "Invalid UTF-8 string",
            Error::UnsupportedOperation => "Unsupported Operation",
            Error::Sys(ref errno) => errno.desc(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::InvalidPath => write!(f, "Invalid path"),
            Error::InvalidUtf8 => write!(f, "Invalid UTF-8 string"),
            Error::UnsupportedOperation => write!(f, "Unsupported Operation"),
            Error::Sys(errno) => write!(f, "{:?}: {}", errno, errno.desc()),
        }
    }
}

pub trait NixPath {
    fn len(&self) -> usize;

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
        where F: FnOnce(&CStr) -> T;
}

impl NixPath for str {
    fn len(&self) -> usize {
        NixPath::len(OsStr::new(self))
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
        where F: FnOnce(&CStr) -> T {
            OsStr::new(self).with_nix_path(f)
        }
}

impl NixPath for OsStr {
    fn len(&self) -> usize {
        self.as_bytes().len()
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
        where F: FnOnce(&CStr) -> T {
            self.as_bytes().with_nix_path(f)
        }
}

impl NixPath for CStr {
    fn len(&self) -> usize {
        self.to_bytes().len()
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
            where F: FnOnce(&CStr) -> T {
        // Equivalence with the [u8] impl.
        if self.len() >= PATH_MAX as usize {
            return Err(Error::InvalidPath);
        }

        Ok(f(self))
    }
}

impl NixPath for [u8] {
    fn len(&self) -> usize {
        self.len()
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
            where F: FnOnce(&CStr) -> T {
        let mut buf = [0u8; PATH_MAX as usize];

        if self.len() >= PATH_MAX as usize {
            return Err(Error::InvalidPath);
        }

        match self.iter().position(|b| *b == 0) {
            Some(_) => Err(Error::InvalidPath),
            None => {
                unsafe {
                    // TODO: Replace with bytes::copy_memory. rust-lang/rust#24028
                    ptr::copy_nonoverlapping(self.as_ptr(), buf.as_mut_ptr(), self.len());
                    Ok(f(CStr::from_ptr(buf.as_ptr() as *const c_char)))
                }

            }
        }
    }
}

impl NixPath for Path {
    fn len(&self) -> usize {
        NixPath::len(self.as_os_str())
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
        self.as_os_str().with_nix_path(f)
    }
}

impl NixPath for PathBuf {
    fn len(&self) -> usize {
        NixPath::len(self.as_os_str())
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
        self.as_os_str().with_nix_path(f)
    }
}

/// Treats `None` as an empty string.
impl<'a, NP: ?Sized + NixPath>  NixPath for Option<&'a NP> {
    fn len(&self) -> usize {
        self.map_or(0, NixPath::len)
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
        if let Some(nix_path) = *self {
            nix_path.with_nix_path(f)
        } else {
            unsafe { CStr::from_ptr("\0".as_ptr() as *const _).with_nix_path(f) }
        }
    }
}
<commit_msg>Prevent the use of unstable compiler features<commit_after>//! Rust friendly bindings to the various *nix system functions.
//!
//! Modules are structured according to the C header file that they would be
//! defined in.
#![crate_name = "nix"]
#![cfg(unix)]
#![allow(non_camel_case_types)]
// latest bitflags triggers a rustc bug with cross-crate macro expansions causing dead_code
// warnings even though the macro expands into something with allow(dead_code)
#![allow(dead_code)]
#![cfg_attr(test, deny(warnings))]
#![recursion_limit = "500"]
#![deny(unused_qualifications)]
#![deny(unstable_features)]

extern crate bytes;
#[macro_use]
extern crate bitflags;

#[macro_use]
extern crate cfg_if;
extern crate void;

#[macro_use] mod macros;

pub extern crate libc;

use errno::Errno;

pub mod errno;
pub mod features;
pub mod fcntl;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod mount;

#[cfg(any(target_os = "dragonfly",
          target_os = "freebsd",
          target_os = "fushsia",
          target_os = "linux",
          target_os = "netbsd"))]
pub mod mqueue;

pub mod pty;

pub mod poll;

pub mod net;

#[cfg(any(target_os = "dragonfly",
          target_os = "freebsd",
          target_os = "ios",
          target_os = "linux",
          target_os = "macos",
          target_os = "netbsd",
          target_os = "openbsd"))]
pub mod ifaddrs;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod sched;

pub mod sys;

// This can be implemented for other platforms as soon as libc
// provides bindings for them.
#[cfg(all(target_os = "linux",
          any(target_arch = "x86", target_arch = "x86_64")))]
pub mod ucontext;

pub mod unistd;

/*
 *
 * ===== Result / Error =====
 *
 */

use libc::c_char;
use std::{ptr, result};
use std::ffi::{CStr, OsStr};
use std::path::{Path, PathBuf};
use std::os::unix::ffi::OsStrExt;
use std::fmt;
use std::error;
use libc::PATH_MAX;

/// Nix Result Type
pub type Result<T> = result::Result<T, Error>;

/// Nix Error Type
///
/// The nix error type provides a common way of dealing with
/// various system system/libc calls that might fail.  Each
/// error has a corresponding errno (usually the one from the
/// underlying OS) to which it can be mapped in addition to
/// implementing other common traits.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Error {
    Sys(Errno),
    InvalidPath,
    /// The operation involved a conversion to Rust's native String type, which failed because the
    /// string did not contain all valid UTF-8.
    InvalidUtf8,
    /// The operation is not supported by Nix, in this instance either use the libc bindings or
    /// consult the module documentation to see if there is a more appropriate interface available.
    UnsupportedOperation,
}

impl Error {

    /// Create a nix Error from a given errno
    pub fn from_errno(errno: Errno) -> Error {
        Error::Sys(errno)
    }

    /// Get the current errno and convert it to a nix Error
    pub fn last() -> Error {
        Error::Sys(Errno::last())
    }

    /// Create a new invalid argument error (`EINVAL`)
    pub fn invalid_argument() -> Error {
        Error::Sys(Errno::EINVAL)
    }

}

impl From<Errno> for Error {
    fn from(errno: Errno) -> Error { Error::from_errno(errno) }
}

impl From<std::string::FromUtf8Error> for Error {
    fn from(_: std::string::FromUtf8Error) -> Error { Error::InvalidUtf8 }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::InvalidPath => "Invalid path",
            Error::InvalidUtf8 => "Invalid UTF-8 string",
            Error::UnsupportedOperation => "Unsupported Operation",
            Error::Sys(ref errno) => errno.desc(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::InvalidPath => write!(f, "Invalid path"),
            Error::InvalidUtf8 => write!(f, "Invalid UTF-8 string"),
            Error::UnsupportedOperation => write!(f, "Unsupported Operation"),
            Error::Sys(errno) => write!(f, "{:?}: {}", errno, errno.desc()),
        }
    }
}

pub trait NixPath {
    fn len(&self) -> usize;

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
        where F: FnOnce(&CStr) -> T;
}

impl NixPath for str {
    fn len(&self) -> usize {
        NixPath::len(OsStr::new(self))
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
        where F: FnOnce(&CStr) -> T {
            OsStr::new(self).with_nix_path(f)
        }
}

impl NixPath for OsStr {
    fn len(&self) -> usize {
        self.as_bytes().len()
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
        where F: FnOnce(&CStr) -> T {
            self.as_bytes().with_nix_path(f)
        }
}

impl NixPath for CStr {
    fn len(&self) -> usize {
        self.to_bytes().len()
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
            where F: FnOnce(&CStr) -> T {
        // Equivalence with the [u8] impl.
        if self.len() >= PATH_MAX as usize {
            return Err(Error::InvalidPath);
        }

        Ok(f(self))
    }
}

impl NixPath for [u8] {
    fn len(&self) -> usize {
        self.len()
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
            where F: FnOnce(&CStr) -> T {
        let mut buf = [0u8; PATH_MAX as usize];

        if self.len() >= PATH_MAX as usize {
            return Err(Error::InvalidPath);
        }

        match self.iter().position(|b| *b == 0) {
            Some(_) => Err(Error::InvalidPath),
            None => {
                unsafe {
                    // TODO: Replace with bytes::copy_memory. rust-lang/rust#24028
                    ptr::copy_nonoverlapping(self.as_ptr(), buf.as_mut_ptr(), self.len());
                    Ok(f(CStr::from_ptr(buf.as_ptr() as *const c_char)))
                }

            }
        }
    }
}

impl NixPath for Path {
    fn len(&self) -> usize {
        NixPath::len(self.as_os_str())
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
        self.as_os_str().with_nix_path(f)
    }
}

impl NixPath for PathBuf {
    fn len(&self) -> usize {
        NixPath::len(self.as_os_str())
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
        self.as_os_str().with_nix_path(f)
    }
}

/// Treats `None` as an empty string.
impl<'a, NP: ?Sized + NixPath>  NixPath for Option<&'a NP> {
    fn len(&self) -> usize {
        self.map_or(0, NixPath::len)
    }

    fn with_nix_path<T, F>(&self, f: F) -> Result<T> where F: FnOnce(&CStr) -> T {
        if let Some(nix_path) = *self {
            nix_path.with_nix_path(f)
        } else {
            unsafe { CStr::from_ptr("\0".as_ptr() as *const _).with_nix_path(f) }
        }
    }
}
<|endoftext|>