<commit_before>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use requests::*;
use values::Value;

/// An error produced by one of the APIs in this module.
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(NodeId),

    /// There is no such service connected to the Foxbox, even indirectly.
    NoSuchService(ServiceId),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// An event during watching.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WatchEvent {
    /// A new value is available.
    Value {
        /// The service that sent the value.
        from: ServiceId,

        /// The actual value.
        value: Value
    },

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// removed. Payload is the id of the device that was removed.
    InputRemoved(ServiceId),

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// added. Payload is the id of the device that was added.
    InputAdded(ServiceId),
}

/// The public API.
pub trait API {
    /// Get the metadata on nodes matching some conditions.
    ///
    /// A call to `API::get_nodes(vec![req1, req2, ...])` will return
    /// the metadata on all nodes matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/nodes`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to a `Vec<NodeRequest>`. See
    /// the implementation of `NodeRequest` for details.
    ///
    /// ### Example
    ///
    /// Request all doors in the entrance (tags `door`, `entrance`)
    /// that support output service `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "inputs": [
    ///     {
    ///       "kind": {
    ///         "Exactly": {
    ///           "OpenClose": []
    ///         }
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    ///
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing an array of `Node`. See the implementation
    /// of `Node` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-node-id",
    ///   "inputs": [],
    ///   "outputs": [
    ///     "tags": [...],
    ///     "id": "some-service-id",
    ///     "node": "some-node-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Output":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_nodes(&Vec<NodeRequest>) -> Vec<Node>;

    /// Label a set of nodes with a set of tags.
    ///
    /// A call to `API::put_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the nodes matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of nodes
    /// matching any of the requests.
    ///
    /// Some of the nodes may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/nodes/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<NodeRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn put_node_tag(set: &Vec<NodeRequest>, tags: &Vec<String>) -> usize;

    /// Remove a set of tags from a set of nodes.
    ///
    /// A call to `API::delete_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the nodes matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of nodes
    /// matching any of the requests.
    ///
    /// Some of the nodes may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/nodes/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<NodeRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_node_tag(set: &Vec<NodeRequest>, tags: String) -> usize;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services`
    fn get_input_services(&Vec<InputRequest>) -> Vec<Service<Input>>;
    fn get_output_services(&Vec<OutputRequest>) -> Vec<Service<Output>>;

    /// Label a set of services with a set of tags.
    ///
    /// A call to `API::put_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the services matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of services
    /// matching any of the requests.
    ///
    /// Some of the services may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<InputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```rust
    /// {
    ///   set: Vec<OutputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn put_input_tag(&Vec<InputRequest>, &Vec<String>) -> usize;
    fn put_output_tag(&Vec<OutputRequest>, &Vec<String>) -> usize;

    /// Remove a set of tags from a set of services.
    ///
    /// A call to `API::delete_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the services matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of services
    /// matching any of the requests.
    ///
    /// Some of the services may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/services/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<InputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```rust
    /// {
    ///   set: Vec<OutputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_input_tag(&Vec<InputRequest>, &Vec<String>) -> usize;
    fn delete_output_tag(&Vec<InputRequest>, &Vec<String>) -> usize;

    /// Read the latest value from a set of services
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services/value`
    fn get_service_value(&Vec<InputRequest>) -> Vec<(ServiceId, Result<Value, Error>)>;

    /// Send one value to a set of services
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/value`
    fn put_service_value(&Vec<OutputRequest>, Value) -> Vec<(ServiceId, Result<(), Error>)>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/services/watch`
    fn register_service_watch<F>(Vec<WatchOptions>, cb: F) -> Self::WatchGuard
        where F: FnMut(WatchEvent) + Send + 'static;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}

/// Options for watching changes in one or more services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WatchOptions {
    /// The set of inputs to watch. Note that the actual inputs in the
    /// set may change over time.
    source: InputRequest,

    /// If `true`, watch as new values become available.
    should_watch_values: bool,

    /// If `true`, watch as nodes are connected/disconnected.
    should_watch_topology: bool,
}

impl WatchOptions {
    pub fn new() -> Self {
        WatchOptions {
            source: InputRequest::new(),
            should_watch_values: false,
            should_watch_topology: false,
        }
    }

    /// Restrict to input services in a given set.
    ///
    /// Also note that the actual input services that are part of the
    /// set may change with time, for instance if devices are added
    /// ore removed.  The request _is live_, i.e. the service watch
    /// will continue watching any input services that match `req`.
    pub fn with_inputs(self, req: InputRequest) -> Self {
        WatchOptions {
            source: self.source.and(req),
            ..self
        }
    }

    pub fn with_watch_values(self, should: bool) -> Self {
        WatchOptions {
            should_watch_values: should,
            ..self
        }
    }

    pub fn with_watch_topology(self, should: bool) -> Self {
        WatchOptions {
            should_watch_topology: should,
            ..self
        }
    }
}
<commit_msg>API::Error now supports Debug, Serialize, Deserialize, Clone<commit_after>//!
//! The API for communicating with devices.
//!
//! This API is provided as Traits to be implemented:
//!
//! - by the low-level layers of the FoxBox, including the adapters;
//! - by test suites and tools that need to simulate connected devices.
//!
//! In turn, this API is used to implement:
//!
//! - the public-facing REST and WebSocket API;
//! - the rules API (ThinkerBell).
//!
//!

use devices::*;
use requests::*;
use values::Value;

/// An error produced by one of the APIs in this module.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Error {
    /// There is no such node connected to the Foxbox, even indirectly.
    NoSuchNode(NodeId),

    /// There is no such service connected to the Foxbox, even indirectly.
    NoSuchService(ServiceId),

    /// Attempting to set a value with the wrong type
    TypeError,
}

/// An event during watching.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WatchEvent {
    /// A new value is available.
    Value {
        /// The service that sent the value.
        from: ServiceId,

        /// The actual value.
        value: Value
    },

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// removed. Payload is the id of the device that was removed.
    InputRemoved(ServiceId),

    /// The set of devices being watched has changed, typically either
    /// because a tag was edited or because a device was
    /// added. Payload is the id of the device that was added.
    InputAdded(ServiceId),
}

/// The public API.
pub trait API {
    /// Get the metadata on nodes matching some conditions.
    ///
    /// A call to `API::get_nodes(vec![req1, req2, ...])` will return
    /// the metadata on all nodes matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/nodes`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to a `Vec<NodeRequest>`. See
    /// the implementation of `NodeRequest` for details.
    ///
    /// ### Example
    ///
    /// Request all doors in the entrance (tags `door`, `entrance`)
    /// that support output service `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "inputs": [
    ///     {
    ///       "kind": {
    ///         "Exactly": {
    ///           "OpenClose": []
    ///         }
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    ///
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing an array of `Node`. See the implementation
    /// of `Node` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-node-id",
    ///   "inputs": [],
    ///   "outputs": [
    ///     "tags": [...],
    ///     "id": "some-service-id",
    ///     "node": "some-node-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Output":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_nodes(&Vec<NodeRequest>) -> Vec<Node>;

    /// Label a set of nodes with a set of tags.
    ///
    /// A call to `API::put_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the nodes matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of nodes
    /// matching any of the requests.
    ///
    /// Some of the nodes may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/nodes/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<NodeRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn put_node_tag(set: &Vec<NodeRequest>, tags: &Vec<String>) -> usize;

    /// Remove a set of tags from a set of nodes.
    ///
    /// A call to `API::delete_node_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the nodes matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of nodes
    /// matching any of the requests.
    ///
    /// Some of the nodes may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if nodes
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/nodes/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<NodeRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_node_tag(set: &Vec<NodeRequest>, tags: String) -> usize;

    /// Get a list of inputs matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services`
    fn get_input_services(&Vec<InputRequest>) -> Vec<Service<Input>>;
    fn get_output_services(&Vec<OutputRequest>) -> Vec<Service<Output>>;

    /// Label a set of services with a set of tags.
    ///
    /// A call to `API::put_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the services matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of services
    /// matching any of the requests.
    ///
    /// Some of the services may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<InputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```rust
    /// {
    ///   set: Vec<OutputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn put_input_tag(&Vec<InputRequest>, &Vec<String>) -> usize;
    fn put_output_tag(&Vec<OutputRequest>, &Vec<String>) -> usize;

    /// Remove a set of tags from a set of services.
    ///
    /// A call to `API::delete_{input, output}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the services matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of services
    /// matching any of the requests.
    ///
    /// Some of the services may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/services/tag`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```rust
    /// {
    ///   set: Vec<InputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    /// or
    /// ```rust
    /// {
    ///   set: Vec<OutputRequest>,
    ///   tags: Vec<String>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON representing a number.
    fn delete_input_tag(&Vec<InputRequest>, &Vec<String>) -> usize;
    fn delete_output_tag(&Vec<InputRequest>, &Vec<String>) -> usize;

    /// Read the latest value from a set of services
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services/value`
    fn get_service_value(&Vec<InputRequest>) -> Vec<(ServiceId, Result<Value, Error>)>;

    /// Send one value to a set of services
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/value`
    fn put_service_value(&Vec<OutputRequest>, Value) -> Vec<(ServiceId, Result<(), Error>)>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/services/watch`
    fn register_service_watch<F>(Vec<WatchOptions>, cb: F) -> Self::WatchGuard
        where F: FnMut(WatchEvent) + Send + 'static;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}

/// Options for watching changes in one or more services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WatchOptions {
    /// The set of inputs to watch. Note that the actual inputs in the
    /// set may change over time.
    source: InputRequest,

    /// If `true`, watch as new values become available.
    should_watch_values: bool,

    /// If `true`, watch as nodes are connected/disconnected.
    should_watch_topology: bool,
}

impl WatchOptions {
    pub fn new() -> Self {
        WatchOptions {
            source: InputRequest::new(),
            should_watch_values: false,
            should_watch_topology: false,
        }
    }

    /// Restrict to input services in a given set.
    ///
    /// Also note that the actual input services that are part of the
    /// set may change with time, for instance if devices are added
    /// ore removed.  The request _is live_, i.e. the service watch
    /// will continue watching any input services that match `req`.
    pub fn with_inputs(self, req: InputRequest) -> Self {
        WatchOptions {
            source: self.source.and(req),
            ..self
        }
    }

    pub fn with_watch_values(self, should: bool) -> Self {
        WatchOptions {
            should_watch_values: should,
            ..self
        }
    }

    pub fn with_watch_topology(self, should: bool) -> Self {
        WatchOptions {
            should_watch_topology: should,
            ..self
        }
    }
}
<|endoftext|>


<commit_before>extern crate errno;

extern crate libc;

use super::constants::*;
use super::ffi::*;

use std::ffi::CString;
use std::os::unix::raw::{gid_t, uid_t};
use std::ptr;
use std::result;
use std::str;

pub type Error = errno::Errno;
pub type Result<T> = result::Result<T, Error>;

fn check_call<T>(res: libc::c_long, value: T) -> Result<T> {
    if res == -1 {
        Err(errno::errno())
    } else {
        Ok(value)
    }
}

fn check_call_ret(res: libc::c_long) -> Result<libc::c_long> {
    if res == -1 {
        Err(errno::errno())
    } else {
        Ok(res)
    }
}

fn get_keyring(id: KeyringSerial, create: bool) -> Result<Keyring> {
    let res = unsafe { keyctl_get_keyring_ID(id, create as libc::c_int) };
    check_call(res as libc::c_long, Keyring { id: res, })
}

pub struct Keyring {
    id: KeyringSerial,
}

impl Keyring {
    pub fn set_default(keyring: DefaultKeyring) -> Result<()> {
        check_call(unsafe { keyctl_set_reqkey_keyring(keyring.serial()) }, ())
    }

    pub fn attach(id: KeyringSerial) -> Result<Self> {
        get_keyring(id, false)
    }

    pub fn attach_or_create(id: KeyringSerial) -> Result<Self> {
        get_keyring(id, true)
    }

    pub fn join_anonymous_session() -> Result<Self> {
        let res = unsafe { keyctl_join_session_keyring(ptr::null()) };
        check_call(res as libc::c_long, Keyring { id: res })
    }

    pub fn join_session(name: &str) -> Result<Self> {
        let nameptr = CString::new(name).unwrap().as_ptr();
        let res = unsafe { keyctl_join_session_keyring(nameptr) };
        check_call(res as libc::c_long, Keyring { id: res })
    }

    pub fn clear(&mut self) -> Result<()> {
        check_call(unsafe { keyctl_clear(self.id) }, ())
    }

    pub fn link(&mut self, key: &Key) -> Result<()> {
        check_call(unsafe { keyctl_link(key.id, self.id) }, ())
    }

    pub fn search(&mut self, type_: &str, description: &str) -> Result<Key> {
        let typeptr = CString::new(type_).unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { keyctl_search(self.id, typeptr, descptr, self.id) };
        check_call(res, Key { id: res as key_serial_t, })
    }

    pub fn read(&self) -> Result<Vec<Key>> {
        let sz = try!(check_call_ret(unsafe { keyctl_read(self.id, ptr::null_mut(), 0) }));
        let mut buffer = Vec::with_capacity(sz as usize);
        try!(check_call_ret(unsafe { keyctl_read(self.id, buffer.as_mut_ptr() as *mut libc::c_char, sz as usize) }));
        // TODO: turn bytestream into vec of keys
        unimplemented!()
    }

    pub fn attach_persistent(&mut self) -> Result<Keyring> {
        let res = unsafe { keyctl_get_persistent(-1, self.id) };
        check_call(res, Keyring { id: res as key_serial_t, })
    }

    pub fn add_key(&mut self, description: &str, payload: &[u8]) -> Result<Key> {
        let typeptr = CString::new("user").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { add_key(typeptr, descptr, payload.as_ptr() as *const libc::c_void, payload.len(), self.id) };
        check_call(res as libc::c_long, Key { id: res, })
    }

    pub fn add_keyring(&mut self) -> Result<Keyring> {
        let typeptr = CString::new("keyring").unwrap().as_ptr();
        let res = unsafe { add_key(typeptr, ptr::null(), ptr::null(), 0, self.id) };
        check_call(res as libc::c_long, Keyring { id: res, })
    }

    pub fn find_key(&mut self, type_: &str, description: &str) -> Result<Key> {
        let typeptr = CString::new(type_).unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { find_key_by_type_and_desc(typeptr, descptr, self.id) };
        check_call(res as libc::c_long, Key { id: res, })
    }

    pub fn request_key(&mut self, type_: &str, description: &str) -> Result<Key> {
        let typeptr = CString::new(type_).unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { request_key(typeptr, descptr, ptr::null(), self.id) };
        check_call(res as libc::c_long, Key { id: res, })
    }

    pub fn request_key_with_fallback(&mut self, type_: &str, description: &str, info: &str) -> Result<Key> {
        let typeptr = CString::new(type_).unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let infoptr = CString::new(info).unwrap().as_ptr();
        let res = unsafe { request_key(typeptr, descptr, infoptr, self.id) };
        check_call(res as libc::c_long, Key { id: res, })
    }
}

pub struct Key {
    id: KeyringSerial,
}

extern fn unlink_cb(
    parent:     key_serial_t,
    key:        key_serial_t,
    desc:       *mut libc::c_char,
    desc_len:   libc::c_int,
    data:       *mut libc::c_void)
    -> libc::c_int {
    unimplemented!()
}

impl Key {
    pub fn request(type_: &str, description: &str) -> Result<Key> {
        let mut keyring = Keyring { id: 0, };
        keyring.request_key(type_, description)
    }

    pub fn request_with_fallback(type_: &str, description: &str, info: &str) -> Result<Key> {
        let mut keyring = Keyring { id: 0, };
        keyring.request_key_with_fallback(type_, description, info)
    }

    pub fn find(type_: &str, description: &str) -> Result<Key> {
        let mut keyring = Keyring { id: 0, };
        keyring.find_key(type_, description)
    }

    pub fn search(type_: &str, description: &str) -> Result<Key> {
        let mut keyring = Keyring { id: 0, };
        keyring.search(type_, description)
    }

    pub fn keyring(&self) -> Result<Keyring> {
        get_keyring(self.id, false)
    }

    pub fn update(&mut self, data: &[u8]) -> Result<()> {
        check_call(unsafe { keyctl_update(self.id, data.as_ptr() as *const libc::c_void, data.len()) }, ())
    }

    pub fn unlink(self, keyring: &Keyring) -> Result<()> {
        check_call(unsafe { keyctl_unlink(self.id, keyring.id) }, ())
    }

    pub fn unlink_from_all(self, keyring: &mut Keyring) -> usize {
        let mut id_copy = self.id;
        let data: *mut KeyringSerial = &mut id_copy;
        let ret = unsafe { recursive_key_scan(keyring.id, unlink_cb, data as *mut libc::c_void) };
        ret as usize
    }

    pub fn unlink_from_session(self) -> usize {
        let mut id_copy = self.id;
        let data: *mut KeyringSerial = &mut id_copy;
        let ret = unsafe { recursive_session_key_scan(unlink_cb, data as *mut libc::c_void) };
        ret as usize
    }

    pub fn revoke(self) -> Result<()> {
        check_call(unsafe { keyctl_revoke(self.id) }, ())
    }

    pub fn chown(&mut self, uid: uid_t) -> Result<()> {
        check_call(unsafe { keyctl_chown(self.id, uid, -1) }, ())
    }

    pub fn chgrp(&mut self, gid: gid_t) -> Result<()> {
        check_call(unsafe { keyctl_chown(self.id, -1, gid) }, ())
    }

    pub fn set_permissions(&mut self, perms: KeyPermissions) -> Result<()> {
        check_call(unsafe { keyctl_setperm(self.id, perms) }, ())
    }

    pub fn description(&self) -> Result<KeyDescription> {
        self.description_raw().map(|desc| {
            KeyDescription::parse(desc)
        })
    }

    fn description_raw(&self) -> Result<String> {
        let sz = try!(check_call_ret(unsafe { keyctl_describe(self.id, ptr::null_mut(), 0) }));
        let mut buffer = Vec::with_capacity(sz as usize);
        let actual_sz = try!(check_call_ret(unsafe { keyctl_describe(self.id, buffer.as_mut_ptr() as *mut libc::c_char, sz as usize) }));
        unsafe { buffer.set_len(actual_sz as usize) };
        let str_slice = str::from_utf8(&buffer[..]).unwrap();
        Ok(str_slice.to_owned())
    }

    pub fn read(&self) -> Result<Vec<u8>> {
        let sz = try!(check_call_ret(unsafe { keyctl_read(self.id, ptr::null_mut(), 0) }));
        let mut buffer = Vec::with_capacity(sz as usize);
        let actual_sz = try!(check_call_ret(unsafe { keyctl_read(self.id, buffer.as_mut_ptr() as *mut libc::c_char, sz as usize) }));
        unsafe { buffer.set_len(actual_sz as usize) };
        Ok(buffer)
    }

    pub fn set_timeout(&mut self, timeout: u32) -> Result<()> {
        check_call(unsafe { keyctl_set_timeout(self.id, timeout) }, ())
    }

    pub fn get_security(&self) -> Result<String> {
        let sz = try!(check_call_ret(unsafe { keyctl_get_security(self.id, ptr::null_mut(), 0) }));
        let mut buffer = Vec::with_capacity(sz as usize);
        let actual_sz = try!(check_call_ret(unsafe { keyctl_get_security(self.id, buffer.as_mut_ptr() as *mut libc::c_char, sz as usize) }));
        unsafe { buffer.set_len(actual_sz as usize) };
        let str_slice = str::from_utf8(&buffer[..]).unwrap();
        Ok(str_slice.to_owned())
    }

    pub fn invalidate(self) -> Result<()> {
        check_call(unsafe { keyctl_invalidate(self.id) }, ())
    }

    pub fn manage(&mut self) -> Result<KeyManager> {
        check_call(unsafe { keyctl_assume_authority(self.id) }, KeyManager {
            key: Key {
                id: self.id,
            },
        })
    }
}

pub struct KeyDescription {
    pub type_:          String,
    pub uid:            uid_t,
    pub gid:            gid_t,
    pub perms:          KeyPermissions,
    pub description:    String,
}

impl KeyDescription {
    fn parse(desc: String) -> KeyDescription {
        unimplemented!()
    }
}

pub struct KeyManager {
    key: Key,
}

impl KeyManager {
    pub fn instantiate(self, keyring: &Keyring, payload: &[u8]) -> Result<()> {
        check_call(unsafe { keyctl_instantiate(self.key.id, payload.as_ptr() as *const libc::c_void, payload.len(), keyring.id) }, ())
    }

    pub fn reject(self, keyring: &Keyring, timeout: u32, error: errno::Errno) -> Result<()> {
        let errno::Errno(errval) = error;
        check_call(unsafe { keyctl_reject(self.key.id, timeout, errval as u32, keyring.id) }, ())
    }

    pub fn negate(self, keyring: &Keyring, timeout: u32) -> Result<()> {
        check_call(unsafe { keyctl_negate(self.key.id, timeout, keyring.id) }, ())
    }
}

#[test]
fn test_add_key() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::SessionKeyring.serial()).unwrap();

    // Create the key.
    let payload = "payload";
    let key = keyring.add_key("description", payload.as_bytes()).unwrap();
    assert_eq!(key.read().unwrap(), payload.as_bytes().iter().cloned().collect::<Vec<u8>>());

    // Update the key.
    let new_payload = "payload";
    let updated_key = keyring.add_key("description", new_payload.as_bytes()).unwrap();
    assert_eq!(key.read().unwrap(), new_payload.as_bytes().iter().cloned().collect::<Vec<u8>>());

    // Clean it up.
    key.unlink(&mut keyring);
}

#[test]
fn test_clear_keyring() {
    unimplemented!()
}

#[test]
fn test_describe_key() {
    unimplemented!()
}

#[test]
fn test_invalidate_key() {
    unimplemented!()
}

#[test]
fn test_link_keyring() {
    unimplemented!()
}

#[test]
fn test_read_keyring() {
    unimplemented!()
}

#[test]
fn test_read_key() {
    unimplemented!()
}

#[test]
fn test_create_keyring() {
    unimplemented!()
}

#[test]
fn test_chmod_keyring() {
    unimplemented!()
}

#[test]
fn test_request_key() {
}

#[test]
fn test_revoke_key() {
    unimplemented!()
}

#[test]
fn test_search_key() {
    unimplemented!()
}

#[test]
fn test_key_timeout() {
    unimplemented!()
}

#[test]
fn test_unlink_key() {
    unimplemented!()
}

#[test]
fn test_update_key() {
    unimplemented!()
}
<commit_msg>api: split _key methods<commit_after>extern crate errno;

extern crate libc;

use super::constants::*;
use super::ffi::*;

use std::ffi::CString;
use std::os::unix::raw::{gid_t, uid_t};
use std::ptr;
use std::result;
use std::str;

pub type Error = errno::Errno;
pub type Result<T> = result::Result<T, Error>;

fn check_call<T>(res: libc::c_long, value: T) -> Result<T> {
    if res == -1 {
        Err(errno::errno())
    } else {
        Ok(value)
    }
}

fn check_call_ret(res: libc::c_long) -> Result<libc::c_long> {
    if res == -1 {
        Err(errno::errno())
    } else {
        Ok(res)
    }
}

fn get_keyring(id: KeyringSerial, create: bool) -> Result<Keyring> {
    let res = unsafe { keyctl_get_keyring_ID(id, create as libc::c_int) };
    check_call(res as libc::c_long, Keyring { id: res, })
}

pub struct Keyring {
    id: KeyringSerial,
}

impl Keyring {
    pub fn set_default(keyring: DefaultKeyring) -> Result<()> {
        check_call(unsafe { keyctl_set_reqkey_keyring(keyring.serial()) }, ())
    }

    pub fn attach(id: KeyringSerial) -> Result<Self> {
        get_keyring(id, false)
    }

    pub fn attach_or_create(id: KeyringSerial) -> Result<Self> {
        get_keyring(id, true)
    }

    pub fn join_anonymous_session() -> Result<Self> {
        let res = unsafe { keyctl_join_session_keyring(ptr::null()) };
        check_call(res as libc::c_long, Keyring { id: res })
    }

    pub fn join_session(name: &str) -> Result<Self> {
        let nameptr = CString::new(name).unwrap().as_ptr();
        let res = unsafe { keyctl_join_session_keyring(nameptr) };
        check_call(res as libc::c_long, Keyring { id: res })
    }

    pub fn clear(&mut self) -> Result<()> {
        check_call(unsafe { keyctl_clear(self.id) }, ())
    }

    pub fn link(&mut self, key: &Key) -> Result<()> {
        check_call(unsafe { keyctl_link(key.id, self.id) }, ())
    }

    pub fn search_for_key(&mut self, description: &str) -> Result<Key> {
        let typeptr = CString::new("user").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { keyctl_search(self.id, typeptr, descptr, self.id) };
        check_call(res, Key { id: res as key_serial_t, })
    }

    pub fn search_for_keyring(&mut self, description: &str) -> Result<Keyring> {
        let typeptr = CString::new("keyring").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { keyctl_search(self.id, typeptr, descptr, self.id) };
        check_call(res, Keyring { id: res as key_serial_t, })
    }

    pub fn read(&self) -> Result<Vec<Key>> {
        let sz = try!(check_call_ret(unsafe { keyctl_read(self.id, ptr::null_mut(), 0) }));
        let mut buffer = Vec::with_capacity(sz as usize);
        try!(check_call_ret(unsafe { keyctl_read(self.id, buffer.as_mut_ptr() as *mut libc::c_char, sz as usize) }));
        // TODO: turn bytestream into vec of keys
        unimplemented!()
    }

    pub fn attach_persistent(&mut self) -> Result<Keyring> {
        let res = unsafe { keyctl_get_persistent(-1, self.id) };
        check_call(res, Keyring { id: res as key_serial_t, })
    }

    pub fn add_key(&mut self, description: &str, payload: &[u8]) -> Result<Key> {
        let typeptr = CString::new("user").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { add_key(typeptr, descptr, payload.as_ptr() as *const libc::c_void, payload.len(), self.id) };
        check_call(res as libc::c_long, Key { id: res, })
    }

    pub fn add_keyring(&mut self) -> Result<Keyring> {
        let typeptr = CString::new("keyring").unwrap().as_ptr();
        let res = unsafe { add_key(typeptr, ptr::null(), ptr::null(), 0, self.id) };
        check_call(res as libc::c_long, Keyring { id: res, })
    }

    pub fn find_key(&mut self, description: &str) -> Result<Key> {
        let typeptr = CString::new("user").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { find_key_by_type_and_desc(typeptr, descptr, self.id) };
        check_call(res as libc::c_long, Key { id: res, })
    }

    pub fn find_keyring(&mut self, description: &str) -> Result<Keyring> {
        let typeptr = CString::new("keyring").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { find_key_by_type_and_desc(typeptr, descptr, self.id) };
        check_call(res as libc::c_long, Keyring { id: res, })
    }

    pub fn request_key(&mut self, description: &str) -> Result<Key> {
        let typeptr = CString::new("user").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { request_key(typeptr, descptr, ptr::null(), self.id) };
        check_call(res as libc::c_long, Key { id: res, })
    }

    pub fn request_keyring(&mut self, description: &str) -> Result<Keyring> {
        let typeptr = CString::new("keyring").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let res = unsafe { request_key(typeptr, descptr, ptr::null(), self.id) };
        check_call(res as libc::c_long, Keyring { id: res, })
    }

    pub fn request_key_with_fallback(&mut self, description: &str, info: &str) -> Result<Key> {
        let typeptr = CString::new("user").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let infoptr = CString::new(info).unwrap().as_ptr();
        let res = unsafe { request_key(typeptr, descptr, infoptr, self.id) };
        check_call(res as libc::c_long, Key { id: res, })
    }

    pub fn request_keyring_with_fallback(&mut self, description: &str, info: &str) -> Result<Keyring> {
        let typeptr = CString::new("keyring").unwrap().as_ptr();
        let descptr = CString::new(description).unwrap().as_ptr();
        let infoptr = CString::new(info).unwrap().as_ptr();
        let res = unsafe { request_key(typeptr, descptr, infoptr, self.id) };
        check_call(res as libc::c_long, Keyring { id: res, })
    }
}

pub struct Key {
    id: KeyringSerial,
}

extern fn unlink_cb(
    parent:     key_serial_t,
    key:        key_serial_t,
    desc:       *mut libc::c_char,
    desc_len:   libc::c_int,
    data:       *mut libc::c_void)
    -> libc::c_int {
    unimplemented!()
}

impl Key {
    pub fn request(description: &str) -> Result<Key> {
        let mut keyring = Keyring { id: 0, };
        keyring.request_key(description)
    }

    pub fn request_with_fallback(description: &str, info: &str) -> Result<Key> {
        let mut keyring = Keyring { id: 0, };
        keyring.request_key_with_fallback(description, info)
    }

    pub fn find(description: &str) -> Result<Key> {
        let mut keyring = Keyring { id: 0, };
        keyring.find_key(description)
    }

    pub fn search(description: &str) -> Result<Key> {
        let mut keyring = Keyring { id: 0, };
        keyring.search_for_key(description)
    }

    pub fn keyring(&self) -> Result<Keyring> {
        get_keyring(self.id, false)
    }

    pub fn update(&mut self, data: &[u8]) -> Result<()> {
        check_call(unsafe { keyctl_update(self.id, data.as_ptr() as *const libc::c_void, data.len()) }, ())
    }

    pub fn unlink(self, keyring: &Keyring) -> Result<()> {
        check_call(unsafe { keyctl_unlink(self.id, keyring.id) }, ())
    }

    pub fn unlink_from_all(self, keyring: &mut Keyring) -> usize {
        let mut id_copy = self.id;
        let data: *mut KeyringSerial = &mut id_copy;
        let ret = unsafe { recursive_key_scan(keyring.id, unlink_cb, data as *mut libc::c_void) };
        ret as usize
    }

    pub fn unlink_from_session(self) -> usize {
        let mut id_copy = self.id;
        let data: *mut KeyringSerial = &mut id_copy;
        let ret = unsafe { recursive_session_key_scan(unlink_cb, data as *mut libc::c_void) };
        ret as usize
    }

    pub fn revoke(self) -> Result<()> {
        check_call(unsafe { keyctl_revoke(self.id) }, ())
    }

    pub fn chown(&mut self, uid: uid_t) -> Result<()> {
        check_call(unsafe { keyctl_chown(self.id, uid, -1) }, ())
    }

    pub fn chgrp(&mut self, gid: gid_t) -> Result<()> {
        check_call(unsafe { keyctl_chown(self.id, -1, gid) }, ())
    }

    pub fn set_permissions(&mut self, perms: KeyPermissions) -> Result<()> {
        check_call(unsafe { keyctl_setperm(self.id, perms) }, ())
    }

    pub fn description(&self) -> Result<KeyDescription> {
        self.description_raw().map(|desc| {
            KeyDescription::parse(desc)
        })
    }

    fn description_raw(&self) -> Result<String> {
        let sz = try!(check_call_ret(unsafe { keyctl_describe(self.id, ptr::null_mut(), 0) }));
        let mut buffer = Vec::with_capacity(sz as usize);
        let actual_sz = try!(check_call_ret(unsafe { keyctl_describe(self.id, buffer.as_mut_ptr() as *mut libc::c_char, sz as usize) }));
        unsafe { buffer.set_len(actual_sz as usize) };
        let str_slice = str::from_utf8(&buffer[..]).unwrap();
        Ok(str_slice.to_owned())
    }

    pub fn read(&self) -> Result<Vec<u8>> {
        let sz = try!(check_call_ret(unsafe { keyctl_read(self.id, ptr::null_mut(), 0) }));
        let mut buffer = Vec::with_capacity(sz as usize);
        let actual_sz = try!(check_call_ret(unsafe { keyctl_read(self.id, buffer.as_mut_ptr() as *mut libc::c_char, sz as usize) }));
        unsafe { buffer.set_len(actual_sz as usize) };
        Ok(buffer)
    }

    pub fn set_timeout(&mut self, timeout: u32) -> Result<()> {
        check_call(unsafe { keyctl_set_timeout(self.id, timeout) }, ())
    }

    pub fn get_security(&self) -> Result<String> {
        let sz = try!(check_call_ret(unsafe { keyctl_get_security(self.id, ptr::null_mut(), 0) }));
        let mut buffer = Vec::with_capacity(sz as usize);
        let actual_sz = try!(check_call_ret(unsafe { keyctl_get_security(self.id, buffer.as_mut_ptr() as *mut libc::c_char, sz as usize) }));
        unsafe { buffer.set_len(actual_sz as usize) };
        let str_slice = str::from_utf8(&buffer[..]).unwrap();
        Ok(str_slice.to_owned())
    }

    pub fn invalidate(self) -> Result<()> {
        check_call(unsafe { keyctl_invalidate(self.id) }, ())
    }

    pub fn manage(&mut self) -> Result<KeyManager> {
        check_call(unsafe { keyctl_assume_authority(self.id) }, KeyManager {
            key: Key {
                id: self.id,
            },
        })
    }
}

pub struct KeyDescription {
    pub type_:          String,
    pub uid:            uid_t,
    pub gid:            gid_t,
    pub perms:          KeyPermissions,
    pub description:    String,
}

impl KeyDescription {
    fn parse(desc: String) -> KeyDescription {
        unimplemented!()
    }
}

pub struct KeyManager {
    key: Key,
}

impl KeyManager {
    pub fn instantiate(self, keyring: &Keyring, payload: &[u8]) -> Result<()> {
        check_call(unsafe { keyctl_instantiate(self.key.id, payload.as_ptr() as *const libc::c_void, payload.len(), keyring.id) }, ())
    }

    pub fn reject(self, keyring: &Keyring, timeout: u32, error: errno::Errno) -> Result<()> {
        let errno::Errno(errval) = error;
        check_call(unsafe { keyctl_reject(self.key.id, timeout, errval as u32, keyring.id) }, ())
    }

    pub fn negate(self, keyring: &Keyring, timeout: u32) -> Result<()> {
        check_call(unsafe { keyctl_negate(self.key.id, timeout, keyring.id) }, ())
    }
}

#[test]
fn test_add_key() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::SessionKeyring.serial()).unwrap();

    // Create the key.
    let payload = "payload";
    let key = keyring.add_key("description", payload.as_bytes()).unwrap();
    assert_eq!(key.read().unwrap(), payload.as_bytes().iter().cloned().collect::<Vec<u8>>());

    // Update the key.
    let new_payload = "payload";
    let updated_key = keyring.add_key("description", new_payload.as_bytes()).unwrap();
    assert_eq!(key.read().unwrap(), new_payload.as_bytes().iter().cloned().collect::<Vec<u8>>());

    // Clean it up.
    key.unlink(&mut keyring);
}

#[test]
fn test_clear_keyring() {
    unimplemented!()
}

#[test]
fn test_describe_key() {
    unimplemented!()
}

#[test]
fn test_invalidate_key() {
    unimplemented!()
}

#[test]
fn test_link_keyring() {
    unimplemented!()
}

#[test]
fn test_read_keyring() {
    unimplemented!()
}

#[test]
fn test_read_key() {
    unimplemented!()
}

#[test]
fn test_create_keyring() {
    unimplemented!()
}

#[test]
fn test_chmod_keyring() {
    unimplemented!()
}

#[test]
fn test_request_key() {
}

#[test]
fn test_revoke_key() {
    unimplemented!()
}

#[test]
fn test_search_key() {
    unimplemented!()
}

#[test]
fn test_key_timeout() {
    unimplemented!()
}

#[test]
fn test_unlink_key() {
    unimplemented!()
}

#[test]
fn test_update_key() {
    unimplemented!()
}
<|endoftext|>


<commit_before><commit_msg>handle four byte ASNs in Aggregator path attribute<commit_after><|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;

use std::collections::HashMap;
use std::io::{self, Write};
use std::sync::Mutex;
use std::fmt;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Department {
    Engeneering,
    Sales,
    CEO,
    HR,
    IT,
    Support,
    Inventory,
    Finance,
}

impl fmt::Display for Department {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Department::Engeneering => write!(f, "Engeneering"),
            Department::Sales => write!(f, "Sales"),
            Department::CEO => write!(f, "CEO"),
            Department::HR => write!(f, "HR"),
            Department::IT => write!(f, "IT"),
            Department::Support => write!(f, "Support"),
            Department::Inventory => write!(f, "Inventory"),
            Department::Finance => write!(f, "Finance"),
        }
    }
}

type Name = String;

lazy_static!{
    static ref COMPANY_DEPARTMENTS: Mutex<HashMap<Department, Vec<Name>>> = {
        Mutex::new(HashMap::new())
    };
}

fn main() {
    text_interface();
}

fn text_interface() {
    loop {
        let mut command = String::new();

        print!("Command: ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut command).expect("failed to read line");

        let command: Vec<&str> = command.trim().split(" ").collect();

        match command.get(0) {
            Some(&"exit") => std::process::exit(0),
            Some(&"list") => {
                match command.get(1) {
                    Some(department) => list_employees(department),
                    None => list_employees(""),
                }
            }
            Some(&"add") => {
                match (command.get(1), command.get(3)) {
                    (Some(name), Some(department)) => {
                        add_employee(name, department);
                    }
                    _ => println!("failed to read name or department"),
                }
            }
            _ => print_help_message(),
        }
    }
}

fn parse_department(department: &str) -> Option<Department> {
    match String::from(department).to_lowercase().as_ref() {
        "engeneering" => Some(Department::Engeneering),
        "sales" => Some(Department::Sales),
        "ceo" => Some(Department::CEO),
        "hr" => Some(Department::HR),
        "it" => Some(Department::IT),
        "support" => Some(Department::Support),
        "inventory" => Some(Department::Inventory),
        "finance" => Some(Department::Finance),
        _ => None,
    }
}

fn list_employees(department: &str) {
    let company_departments = COMPANY_DEPARTMENTS.lock().unwrap();

    if let Some(department) = parse_department(department) {
        if let Some(department_employees) = company_departments.get(&department) {
            println!("Employees of {} Department:", department);

            for employee in department_employees {
                println!("{}", employee);
            }
        }
    } else {
        for (department, department_employees) in company_departments.iter() {
            println!("Employees of {} Department:", department);

            for employee in department_employees {
                println!("{}", employee);
            }
        }
    }
}

fn add_employee(name: &str, department: &str) {
    let name = String::from(name);

    if let Some(department) = parse_department(department) {
        let mut company_departments = COMPANY_DEPARTMENTS.lock().unwrap();

        match company_departments.get_mut(&department) {
            Some(department_employees) => {
                if let None = department_employees.iter().position(|n| n == &name) {
                    department_employees.push(name);
                }
                return;
            }
            None => {}
        }

        company_departments.insert(department, vec![name]);
    } else {
        println!("unknown department: {}", department);
        return;
    }
}

fn print_help_message() {
    println!("\nUsage:\n\tCommand: <command>\n");
    println!("Commands:");
    println!("\tadd <name> to <department>  Add employee to a department in the company");
    println!("\tlist                        List all employees by their departments");
    println!("\tlist <department>           List all employees of the department");
    println!("\thelp                        Display this help message");
    println!("\texit                        Exit an application");
}
<commit_msg>Update department exercise<commit_after>#[macro_use]
extern crate lazy_static;

use std::collections::HashMap;
use std::io::{self, Write};
use std::sync::Mutex;
use std::fmt;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Department {
    Engeneering,
    Sales,
    CEO,
    HR,
    IT,
    Support,
    Inventory,
    Finance,
}

impl fmt::Display for Department {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Department::Engeneering => write!(f, "Engeneering"),
            Department::Sales => write!(f, "Sales"),
            Department::CEO => write!(f, "CEO"),
            Department::HR => write!(f, "HR"),
            Department::IT => write!(f, "IT"),
            Department::Support => write!(f, "Support"),
            Department::Inventory => write!(f, "Inventory"),
            Department::Finance => write!(f, "Finance"),
        }
    }
}

type Name = String;

lazy_static!{
    static ref COMPANY_DEPARTMENTS: Mutex<HashMap<Department, Vec<Name>>> = {
        Mutex::new(HashMap::new())
    };
}

fn main() {
    text_interface();
}

fn text_interface() {
    loop {
        let mut command = String::new();

        print!("Command: ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut command).expect("failed to read line");

        let command: Vec<&str> = command.trim().split(" ").collect();

        match command.get(0) {
            Some(&"exit") => std::process::exit(0),
            Some(&"list") => {
                match command.get(1) {
                    Some(department) => list_department(department),
                    None => list_department(""),
                }
            }
            Some(&"add") => {
                match (command.get(1), command.get(3)) {
                    (Some(name), Some(department)) => {
                        add_employee(name, department);
                    }
                    _ => println!("failed to read name or department"),
                }
            }
            _ => print_help_message(),
        }
    }
}

fn parse_department(department: &str) -> Option<Department> {
    match String::from(department).to_lowercase().as_ref() {
        "engeneering" => Some(Department::Engeneering),
        "sales" => Some(Department::Sales),
        "ceo" => Some(Department::CEO),
        "hr" => Some(Department::HR),
        "it" => Some(Department::IT),
        "support" => Some(Department::Support),
        "inventory" => Some(Department::Inventory),
        "finance" => Some(Department::Finance),
        _ => None,
    }
}

fn list_employees(department: &Department, department_employees: &Vec<Name>) {
    println!("Employees of {} Department:", department);

    for employee in department_employees {
        println!("{}", employee);
    }
}

fn list_department(department: &str) {
    let company_departments = COMPANY_DEPARTMENTS.lock().unwrap();

    if let Some(department) = parse_department(department) {
        if let Some(department_employees) = company_departments.get(&department) {
            list_employees(&department, department_employees);
        }
    } else {
        for (department, department_employees) in company_departments.iter() {
            list_employees(&department, department_employees);
        }
    }
}

fn add_employee(name: &str, department: &str) {
    let name = String::from(name);

    if let Some(department) = parse_department(department) {
        let mut company_departments = COMPANY_DEPARTMENTS.lock().unwrap();

        match company_departments.get_mut(&department) {
            Some(department_employees) => {
                if let None = department_employees.iter().position(|n| n == &name) {
                    department_employees.push(name);
                }
                return;
            }
            None => {}
        }

        company_departments.insert(department, vec![name]);
    } else {
        println!("unknown department: {}", department);
        return;
    }
}

fn print_help_message() {
    println!("\nUsage:\n\tCommand: <command>\n");
    println!("Commands:");
    println!("\tadd <name> to <department>  Add employee to a department in the company");
    println!("\tlist                        List all employees by their departments");
    println!("\tlist <department>           List all employees of the department");
    println!("\thelp                        Display this help message");
    println!("\texit                        Exit an application");
}
<|endoftext|>


<commit_before>use std::fmt::{Display, Error, Formatter};

#[derive(Debug, PartialEq, Eq)]
pub enum BinOp {
    Star,
    Plus,
    Minus,
}

impl Display for BinOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match self {
            &BinOp::Star => write!(fmt, "*"),
            &BinOp::Plus => write!(fmt, "+"),
            &BinOp::Minus => write!(fmt, "-"),
        }
    }
}

#[derive(Debug)]
pub enum Exp {
    Int(i64),
    Float(f64),
    BinExp(Box<Exp>, BinOp, Box<Exp>),
}

impl Display for Exp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match self {
            &Exp::Int(i) => write!(fmt, "{}", i),
            &Exp::Float(f) => write!(fmt, "{}", f),
            &Exp::BinExp(ref e1, ref o, ref e2) => write!(fmt, "{} {} {}", e1, o, e2),
        }
    }
}
<commit_msg>added predecedence to facilitate proper printing<commit_after>use std::fmt::{Display, Error, Formatter};

#[derive(Debug, PartialEq, Eq)]
pub enum BinOp {
    Star,
    Plus,
    Minus,
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Precedence {
    Const = 100,
    Mult = 60,
    Add = 50,
}

impl BinOp {
    pub fn precedence(&self) -> Precedence {
        match self {
            &BinOp::Star => Precedence::Mult,
            &BinOp::Plus | &BinOp::Minus => Precedence::Add,
        }
    }
}

impl Display for BinOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match self {
            &BinOp::Star => write!(fmt, "*"),
            &BinOp::Plus => write!(fmt, "+"),
            &BinOp::Minus => write!(fmt, "-"),
        }
    }
}

#[derive(Debug)]
pub enum Exp {
    Int(i64),
    Float(f64),
    BinExp(Box<Exp>, BinOp, Box<Exp>),
}

impl Exp {
    pub fn precedence(&self) -> Precedence {
        match self {
            &Exp::Int(_) | &Exp::Float(_) => Precedence::Const,
            &Exp::BinExp(_, ref o, _) => o.precedence(),
        }
    }
}

impl Display for Exp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match self {
            &Exp::Int(i) => write!(fmt, "{}", i),
            &Exp::Float(f) => write!(fmt, "{}", f),
            &Exp::BinExp(ref e1, ref o, ref e2) => {
                let prec = self.precedence();

                let left = if prec > e1.precedence() {
                    format!("({})", e1)
                } else {
                    format!("{}", e1)
                };

                let right = if prec > e2.precedence() {
                    format!("({})", e2)
                } else {
                    format!("{}", e2)
                };

                write!(fmt, "{} {} {}", left, o, right)
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate dent;
extern crate term;
extern crate term_size;

use clap::{App, Arg};
use dent::plot;
use dent::summary::Summary;
use dent::t_test::{TTest, welch_t_test};

use std::error;
use std::fs::File;
use std::io::{self, BufRead, BufReader};

mod fmt;
mod log;

macro_rules! ok {
    ($r: expr) => {
        match $r {
            Ok(t) => t,
            Err(e) => {
                log::error(&format!("{}", e));
                std::process::exit(1);
            }
        }
    }
}

fn print_summary(s: &Summary, outliers: bool) {
    let width = 10;
    let size_width = 6;

    if outliers {
        println!(
            "{n:>nw$}  {min:>w$}  {q1:>w$}  {med:>w$}  {q3:>w$}  {max:>w$}  {mean:>w$}  {std:>w$}",
            w = width,
            nw = size_width,
            n = "Size",
            min = "Min",
            q1 = "Q1",
            med = "Median",
            q3 = "Q3",
            max = "Max",
            mean = "Mean",
            std = "Std Dev",
        );
        println!(
            "{n:>nw$}  {min:>w$}  {q1:>w$}  {med:>w$}  {q3:>w$}  {max:>w$}  {mean:>w$}  {std:>w$}",
            w = width,
            nw = size_width,
            n = fmt::f(s.size(), width),
            min = fmt::f(s.min(), width),
            q1 = fmt::f(s.lower_quartile(), width),
            med = fmt::f(s.median(), width),
            q3 = fmt::f(s.upper_quartile(), width),
            max = fmt::f(s.max(), width),
            mean = fmt::f(s.mean(), width),
            std = fmt::f(s.standard_deviation(), width),
        );
    } else {
        println!(
            "{n:>nw$}  {min:>w$}  {q1:>w$}  {med:>w$}  {q3:>w$}  {max:>w$}  {mean:>w$}  {std:>w$}",
            w = width,
            nw = size_width,
            n = "Size",
            min = "Min Adj",
            q1 = "Q1",
            med = "Median",
            q3 = "Q3",
            max = "Max Adj",
            mean = "Mean",
            std = "Std Dev",
        );
        println!(
            "{n:>nw$}  {min:>w$}  {q1:>w$}  {med:>w$}  {q3:>w$}  {max:>w$}  {mean:>w$}  {std:>w$}",
            w = width,
            nw = size_width,
            n = fmt::f(s.size(), width),
            min = fmt::f(s.min_adjacent(), width),
            q1 = fmt::f(s.lower_quartile(), width),
            med = fmt::f(s.median(), width),
            q3 = fmt::f(s.upper_quartile(), width),
            max = fmt::f(s.max_adjacent(), width),
            mean = fmt::f(s.mean(), width),
            std = fmt::f(s.standard_deviation(), width),
        );
    }
}

fn print_t_test(t_test: &TTest, s1: &Summary, s2: &Summary) {
    let width = 12;

    let m1 = s1.mean();
    let m2 = s2.mean();
    let se1 = s1.standard_error();
    let se2 = s1.standard_error();

    let del = m2 - m1;
    let se_del = (se1.powi(2) + se1.powi(2)).sqrt();

    println!("{l:>w$} = {v} ± {se}", w = width, l = "m₁ ± SE", v = m1, se = se1);
    println!("{l:>w$} = {v} ± {se}", w = width, l = "m₂ ± SE", v = m2, se = se2);
    println!("{l:>w$} = {v} ± {se}", w = width, l = "m₂ - m₁ ± SE", v = del, se = se_del);
    println!("{l:>w$} = {v}", w = width, l = "p", v = t_test.p);
    println!("{l:>w$} = {v}", w = width, l = "t", v = t_test.t);
    println!("{l:>w$} = {v}", w = width, l = "DF", v = t_test.df);
}

fn summarize_file(path: &str, lax_parsing: bool) -> Result<Summary, Box<error::Error>> {
    let f = File::open(path).or_else(|e| {
        log::error(&format!("Could not open file: {:?}", path));
        Err(e)
    })?;
    let reader = BufReader::new(f);

    let data = read_data(reader, lax_parsing)?;

    Ok(Summary::new(&data)?)
}

fn read_data<R>(reader: R, lax_parsing: bool) -> Result<Vec<f64>, Box<error::Error>>
    where R: BufRead {
    let mut data: Vec<f64> = vec![];

    for l in reader.lines() {
        let s = l?.trim().to_string();

        if s.is_empty() {
            continue;
        }

        match s.parse() {
            Ok(d) => data.push(d),
            err => if !lax_parsing { err?; }
        }
    }

    Ok(data)
}

fn summarize_stdin(lax_parsing: bool) -> Result<Summary, Box<error::Error>> {
    let stdin = io::stdin();
    let data = read_data(stdin.lock(), lax_parsing)?;

    Ok(Summary::new(&data)?)
}

fn display_t_test(
    summary1: &Summary,
    summary2: &Summary,
    draw_plot: bool,
    width: usize,
    ascii: bool,
    outliers: bool,
) {
    let t_test = ok!(welch_t_test(&summary1, &summary2));

    if draw_plot {
        let p = ok!(plot::comparison_plot(&[summary1, summary2], width, ascii, true, outliers));
        println!("{}\n", p);
    }

    print_summary(&summary1, outliers);
    println!();
    print_summary(&summary2, outliers);
    println!();
    print_t_test(&t_test, &summary1, &summary2);
}

fn display_summaries(
    summaries: &[Summary],
    draw_plot: bool,
    width: usize,
    ascii: bool,
    outliers: bool,
) {
    if draw_plot {
        let summary_refs: Vec<&Summary> = summaries
            .iter()
            .collect();

        let plot = ok!(plot::comparison_plot(&summary_refs, width, ascii, true, true));
        println!("{}\n", plot);
    }

    for i in 0..summaries.len() {
        if i > 0 {
            println!();
        }
        print_summary(&summaries[i], outliers);
    }
}

fn display_summaries_tsv(summaries: &[Summary], sources: &[&str]) {
    let parts = vec![
        "Source",
        "Size",
        "Mean",
        "Median",
        "StandardDeviation",
        "Variance",
        "StandardError",
        "Min",
        "Max",
        "Range",
        "LowerQuartile",
        "UpperQuartile",
        "IQR",
        "MinAdjacent",
        "MaxAdjacent",
    ];
    let header = parts.join("\t");
    println!("{}", header);

    for (summ, src) in summaries.iter().zip(sources) {
        print_summary_tsv(summ, src);
    }
}

fn print_summary_tsv(summary: &Summary, source: &str) {
    let values = vec![
        summary.size(),
        summary.mean(),
        summary.median(),
        summary.standard_deviation(),
        summary.unbiased_variance(),
        summary.standard_error(),
        summary.min(),
        summary.max(),
        summary.range(),
        summary.lower_quartile(),
        summary.upper_quartile(),
        summary.iqr(),
        summary.min_adjacent(),
        summary.max_adjacent(),
    ];
    let fields: Vec<String> = values.iter().map(|x| format!("{}", x)).collect();
    println!("{}\t{}", source, fields.join("\t"));
}

fn main() {
    let matches = App::new("dent")
        .version("0.3.0")
        .author("Joe Ranweiler <joe@lemma.co>")
        .about("A tiny tool for t-tests &c.")
        .arg(Arg::with_name("stdin")
             .short("s")
             .long("stdin")
             .help("Read and summarize data from stdin"))
        .arg(Arg::with_name("files")
             .multiple(true)
             .value_name("FILES")
             .takes_value(true)
             .required_unless("stdin")
             .help("Path to one or more files of sample data"))
        .arg(Arg::with_name("lax")
             .long("lax")
             .help("Ignore non-numeric input lines"))
        .arg(Arg::with_name("tsv")
             .long("tsv")
             .help("Print summary data to stdout in TSV format"))
        .arg(Arg::with_name("plot_outliers")
             .long("outliers")
             .help("Include outliers and use min/max for outer fences of boxplot"))
        .arg(Arg::with_name("plot")
             .short("p")
             .long("plot")
             .help("Print standard boxplots"))
        .arg(Arg::with_name("ascii")
             .long("ascii")
             .help("Use only ASCII characters in boxplots"))
        .arg(Arg::with_name("width")
             .short("w")
             .long("width")
             .value_name("WIDTH")
             .takes_value(true)
             .help("Width of boxplot"))
        .get_matches();

    let ascii = matches.is_present("ascii");
    let lax_parsing = matches.is_present("lax");
    let draw_plot = matches.is_present("plot");
    let use_stdin = matches.is_present("stdin");
    let outliers = matches.is_present("plot_outliers");
    let tsv = matches.is_present("tsv");

    let width = matches
        .value_of("width")
        .and_then(|w| w.parse::<usize>().ok())
        .or(term_size::dimensions().map(|(w, _)| w))
        .unwrap_or(80);

    let (sources, summaries) = if use_stdin {
        (vec!["stdin"], vec![ok!(summarize_stdin(lax_parsing))])
    } else {
        // Required if `stdin` is not present, so we can unwrap.
        let files = matches
            .values_of("files")
            .unwrap_or_else(|| unreachable!());

        let summaries = files.clone().map(|f| ok!(summarize_file(f, lax_parsing))).collect();
        (files.collect(), summaries)
    };

    if tsv {
        return display_summaries_tsv(&summaries, &sources);
    }

    match summaries.len() {
        0 => unreachable!(),
        // We want match 1 with the case `len()` > 2.
        2 => {
            display_t_test(
                &summaries[0],
                &summaries[1],
                draw_plot,
                width,
                ascii,
                outliers,
            );
        }
        _ => {
            display_summaries(
                &summaries,
                draw_plot,
                width,
                ascii,
                outliers,
            );
        },
    };
}
<commit_msg>Automatically set exe version from crate<commit_after>#[macro_use] extern crate clap;
extern crate dent;
extern crate term;
extern crate term_size;

use clap::{App, Arg};
use dent::plot;
use dent::summary::Summary;
use dent::t_test::{TTest, welch_t_test};

use std::error;
use std::fs::File;
use std::io::{self, BufRead, BufReader};

mod fmt;
mod log;

macro_rules! ok {
    ($r: expr) => {
        match $r {
            Ok(t) => t,
            Err(e) => {
                log::error(&format!("{}", e));
                std::process::exit(1);
            }
        }
    }
}

fn print_summary(s: &Summary, outliers: bool) {
    let width = 10;
    let size_width = 6;

    if outliers {
        println!(
            "{n:>nw$}  {min:>w$}  {q1:>w$}  {med:>w$}  {q3:>w$}  {max:>w$}  {mean:>w$}  {std:>w$}",
            w = width,
            nw = size_width,
            n = "Size",
            min = "Min",
            q1 = "Q1",
            med = "Median",
            q3 = "Q3",
            max = "Max",
            mean = "Mean",
            std = "Std Dev",
        );
        println!(
            "{n:>nw$}  {min:>w$}  {q1:>w$}  {med:>w$}  {q3:>w$}  {max:>w$}  {mean:>w$}  {std:>w$}",
            w = width,
            nw = size_width,
            n = fmt::f(s.size(), width),
            min = fmt::f(s.min(), width),
            q1 = fmt::f(s.lower_quartile(), width),
            med = fmt::f(s.median(), width),
            q3 = fmt::f(s.upper_quartile(), width),
            max = fmt::f(s.max(), width),
            mean = fmt::f(s.mean(), width),
            std = fmt::f(s.standard_deviation(), width),
        );
    } else {
        println!(
            "{n:>nw$}  {min:>w$}  {q1:>w$}  {med:>w$}  {q3:>w$}  {max:>w$}  {mean:>w$}  {std:>w$}",
            w = width,
            nw = size_width,
            n = "Size",
            min = "Min Adj",
            q1 = "Q1",
            med = "Median",
            q3 = "Q3",
            max = "Max Adj",
            mean = "Mean",
            std = "Std Dev",
        );
        println!(
            "{n:>nw$}  {min:>w$}  {q1:>w$}  {med:>w$}  {q3:>w$}  {max:>w$}  {mean:>w$}  {std:>w$}",
            w = width,
            nw = size_width,
            n = fmt::f(s.size(), width),
            min = fmt::f(s.min_adjacent(), width),
            q1 = fmt::f(s.lower_quartile(), width),
            med = fmt::f(s.median(), width),
            q3 = fmt::f(s.upper_quartile(), width),
            max = fmt::f(s.max_adjacent(), width),
            mean = fmt::f(s.mean(), width),
            std = fmt::f(s.standard_deviation(), width),
        );
    }
}

fn print_t_test(t_test: &TTest, s1: &Summary, s2: &Summary) {
    let width = 12;

    let m1 = s1.mean();
    let m2 = s2.mean();
    let se1 = s1.standard_error();
    let se2 = s1.standard_error();

    let del = m2 - m1;
    let se_del = (se1.powi(2) + se1.powi(2)).sqrt();

    println!("{l:>w$} = {v} ± {se}", w = width, l = "m₁ ± SE", v = m1, se = se1);
    println!("{l:>w$} = {v} ± {se}", w = width, l = "m₂ ± SE", v = m2, se = se2);
    println!("{l:>w$} = {v} ± {se}", w = width, l = "m₂ - m₁ ± SE", v = del, se = se_del);
    println!("{l:>w$} = {v}", w = width, l = "p", v = t_test.p);
    println!("{l:>w$} = {v}", w = width, l = "t", v = t_test.t);
    println!("{l:>w$} = {v}", w = width, l = "DF", v = t_test.df);
}

fn summarize_file(path: &str, lax_parsing: bool) -> Result<Summary, Box<error::Error>> {
    let f = File::open(path).or_else(|e| {
        log::error(&format!("Could not open file: {:?}", path));
        Err(e)
    })?;
    let reader = BufReader::new(f);

    let data = read_data(reader, lax_parsing)?;

    Ok(Summary::new(&data)?)
}

fn read_data<R>(reader: R, lax_parsing: bool) -> Result<Vec<f64>, Box<error::Error>>
    where R: BufRead {
    let mut data: Vec<f64> = vec![];

    for l in reader.lines() {
        let s = l?.trim().to_string();

        if s.is_empty() {
            continue;
        }

        match s.parse() {
            Ok(d) => data.push(d),
            err => if !lax_parsing { err?; }
        }
    }

    Ok(data)
}

fn summarize_stdin(lax_parsing: bool) -> Result<Summary, Box<error::Error>> {
    let stdin = io::stdin();
    let data = read_data(stdin.lock(), lax_parsing)?;

    Ok(Summary::new(&data)?)
}

fn display_t_test(
    summary1: &Summary,
    summary2: &Summary,
    draw_plot: bool,
    width: usize,
    ascii: bool,
    outliers: bool,
) {
    let t_test = ok!(welch_t_test(&summary1, &summary2));

    if draw_plot {
        let p = ok!(plot::comparison_plot(&[summary1, summary2], width, ascii, true, outliers));
        println!("{}\n", p);
    }

    print_summary(&summary1, outliers);
    println!();
    print_summary(&summary2, outliers);
    println!();
    print_t_test(&t_test, &summary1, &summary2);
}

fn display_summaries(
    summaries: &[Summary],
    draw_plot: bool,
    width: usize,
    ascii: bool,
    outliers: bool,
) {
    if draw_plot {
        let summary_refs: Vec<&Summary> = summaries
            .iter()
            .collect();

        let plot = ok!(plot::comparison_plot(&summary_refs, width, ascii, true, true));
        println!("{}\n", plot);
    }

    for i in 0..summaries.len() {
        if i > 0 {
            println!();
        }
        print_summary(&summaries[i], outliers);
    }
}

fn display_summaries_tsv(summaries: &[Summary], sources: &[&str]) {
    let parts = vec![
        "Source",
        "Size",
        "Mean",
        "Median",
        "StandardDeviation",
        "Variance",
        "StandardError",
        "Min",
        "Max",
        "Range",
        "LowerQuartile",
        "UpperQuartile",
        "IQR",
        "MinAdjacent",
        "MaxAdjacent",
    ];
    let header = parts.join("\t");
    println!("{}", header);

    for (summ, src) in summaries.iter().zip(sources) {
        print_summary_tsv(summ, src);
    }
}

fn print_summary_tsv(summary: &Summary, source: &str) {
    let values = vec![
        summary.size(),
        summary.mean(),
        summary.median(),
        summary.standard_deviation(),
        summary.unbiased_variance(),
        summary.standard_error(),
        summary.min(),
        summary.max(),
        summary.range(),
        summary.lower_quartile(),
        summary.upper_quartile(),
        summary.iqr(),
        summary.min_adjacent(),
        summary.max_adjacent(),
    ];
    let fields: Vec<String> = values.iter().map(|x| format!("{}", x)).collect();
    println!("{}\t{}", source, fields.join("\t"));
}

fn main() {
    let matches = App::new("dent")
        .version(crate_version!())
        .author("Joe Ranweiler <joe@lemma.co>")
        .about("A tiny tool for t-tests &c.")
        .arg(Arg::with_name("stdin")
             .short("s")
             .long("stdin")
             .help("Read and summarize data from stdin"))
        .arg(Arg::with_name("files")
             .multiple(true)
             .value_name("FILES")
             .takes_value(true)
             .required_unless("stdin")
             .help("Path to one or more files of sample data"))
        .arg(Arg::with_name("lax")
             .long("lax")
             .help("Ignore non-numeric input lines"))
        .arg(Arg::with_name("tsv")
             .long("tsv")
             .help("Print summary data to stdout in TSV format"))
        .arg(Arg::with_name("plot_outliers")
             .long("outliers")
             .help("Include outliers and use min/max for outer fences of boxplot"))
        .arg(Arg::with_name("plot")
             .short("p")
             .long("plot")
             .help("Print standard boxplots"))
        .arg(Arg::with_name("ascii")
             .long("ascii")
             .help("Use only ASCII characters in boxplots"))
        .arg(Arg::with_name("width")
             .short("w")
             .long("width")
             .value_name("WIDTH")
             .takes_value(true)
             .help("Width of boxplot"))
        .get_matches();

    let ascii = matches.is_present("ascii");
    let lax_parsing = matches.is_present("lax");
    let draw_plot = matches.is_present("plot");
    let use_stdin = matches.is_present("stdin");
    let outliers = matches.is_present("plot_outliers");
    let tsv = matches.is_present("tsv");

    let width = matches
        .value_of("width")
        .and_then(|w| w.parse::<usize>().ok())
        .or(term_size::dimensions().map(|(w, _)| w))
        .unwrap_or(80);

    let (sources, summaries) = if use_stdin {
        (vec!["stdin"], vec![ok!(summarize_stdin(lax_parsing))])
    } else {
        // Required if `stdin` is not present, so we can unwrap.
        let files = matches
            .values_of("files")
            .unwrap_or_else(|| unreachable!());

        let summaries = files.clone().map(|f| ok!(summarize_file(f, lax_parsing))).collect();
        (files.collect(), summaries)
    };

    if tsv {
        return display_summaries_tsv(&summaries, &sources);
    }

    match summaries.len() {
        0 => unreachable!(),
        // We want match 1 with the case `len()` > 2.
        2 => {
            display_t_test(
                &summaries[0],
                &summaries[1],
                draw_plot,
                width,
                ascii,
                outliers,
            );
        }
        _ => {
            display_summaries(
                &summaries,
                draw_plot,
                width,
                ascii,
                outliers,
            );
        },
    };
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate tokio_process;

use self::futures::Stream;
use self::futures::future::Future;
use self::hyper::header::ContentLength;
use self::hyper::header::ContentType;
use self::hyper::server::{Request, Response};
use cgi::tokio_process::CommandExt;
use std::io;
use std::io::BufRead;
use std::io::Read;
use std::io::Write;
use std::process::Command;
use std::process::Stdio;
use std::str::FromStr;

pub fn do_cgi(
    req: Request,
    cmd: Command,
    handle: tokio_core::reactor::Handle,
) -> Box<Future<Item = Response, Error = hyper::Error>>
{
    let mut cmd = cmd;
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::inherit());
    cmd.stdin(Stdio::piped());
    println!("REQUEST_METHOD {:?}", req.method());
    cmd.env("SERVER_SOFTWARE", "hyper")
       .env("SERVER_NAME", "localhost")            // TODO
       .env("GATEWAY_INTERFACE", "CGI/1.1")
       .env("SERVER_PROTOCOL", "HTTP/1.1")         // TODO
       .env("SERVER_PORT", "80")                   // TODO
       .env("REQUEST_METHOD", format!("{}",req.method()))
       .env("SCRIPT_NAME", "")                     // TODO
       .env("QUERY_STRING", req.query().unwrap_or(""))
       .env("REMOTE_ADDR", "")                     // TODO
       .env("AUTH_TYPE", "")                       // TODO
       .env("REMOTE_USER", "")                     // TODO
       .env("CONTENT_TYPE",
           &format!("{}", req.headers().get().unwrap_or(&ContentType::plaintext())))
       .env("CONTENT_LENGTH",
           &format!("{}", req.headers().get().unwrap_or(&ContentLength(0))));

    let mut child = cmd.spawn_async(&handle).expect("can't spawn CGI command");

    Box::new(req.body().concat2().and_then(move |body| {
        println!("REQUEST body {:?}", &body.as_str());
        child
            .stdin()
            .take()
            .unwrap()
            .write_all(&body)
            .expect("can't write command output");

        Box::new(
            child
                .wait_with_output()
                .map(|command_result| {
                    /* let mut stderr = io::BufReader::new(command_result.stderr.as_slice()); */
                    /* println!("STDERR of git-http-backend:"); */
                    /* for line in stderr.by_ref().lines() { */
                    /*     println!("{:?}", line); */
                    /* } */
                    /* println!("end STDERR"); */
                    let mut stdout = io::BufReader::new(command_result.stdout.as_slice());

                    let mut data = vec![];
                    let mut response = Response::new();

                    for line in stdout.by_ref().lines() {
                        println!("STDOUT line: {:?}", line);
                        if line.as_ref().unwrap().is_empty() {
                            break;
                        }
                        let l: Vec<&str> =
                            line.as_ref().unwrap().as_str().splitn(2, ": ").collect();
                        if l[0] == "Status" {
                            response.set_status(hyper::StatusCode::Unregistered(
                                u16::from_str(l[1].split(" ").next().unwrap()).unwrap(),
                            ));
                        } else {
                            response
                                .headers_mut()
                                .set_raw(l[0].to_string(), l[1].to_string());
                        }
                    }
                    stdout
                        .read_to_end(&mut data)
                        .expect("can't read command output");
                    println!("BODY: {:?}", &data);
                    response.set_body(hyper::Chunk::from(data));
                    response
                })
                .map_err(|e| e.into()),
        )
    }))
}
<commit_msg>Forward HTTP_CONTENT_ENCODING when doing CGI<commit_after>extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate tokio_process;

use self::futures::Stream;
use self::futures::future::Future;
use self::hyper::header::ContentLength;
use self::hyper::header::ContentType;
use self::hyper::header::ContentEncoding;
use self::hyper::header::Encoding;
use self::hyper::server::{Request, Response};
use cgi::tokio_process::CommandExt;
use std::io;
use std::io::BufRead;
use std::io::Read;
use std::io::Write;
use std::process::Command;
use std::process::Stdio;
use std::str::FromStr;

pub fn do_cgi(
    req: Request,
    cmd: Command,
    handle: tokio_core::reactor::Handle,
) -> Box<Future<Item = Response, Error = hyper::Error>>
{
    let mut cmd = cmd;
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::inherit());
    cmd.stdin(Stdio::piped());
    println!("REQUEST_METHOD {:?}", req.method());
    cmd.env("SERVER_SOFTWARE", "hyper")
       .env("SERVER_NAME", "localhost")            // TODO
       .env("GATEWAY_INTERFACE", "CGI/1.1")
       .env("SERVER_PROTOCOL", "HTTP/1.1")         // TODO
       .env("SERVER_PORT", "80")                   // TODO
       .env("REQUEST_METHOD", format!("{}",req.method()))
       .env("SCRIPT_NAME", "")                     // TODO
       .env("QUERY_STRING", req.query().unwrap_or(""))
       .env("REMOTE_ADDR", "")                     // TODO
       .env("AUTH_TYPE", "")                       // TODO
       .env("REMOTE_USER", "")                     // TODO
       .env("CONTENT_TYPE",
           &format!("{}", req.headers().get().unwrap_or(&ContentType::plaintext())))
       .env("HTTP_CONTENT_ENCODING",
           &format!("{}", req.headers().get().unwrap_or(&ContentEncoding(vec![]))))
       .env("CONTENT_LENGTH",
           &format!("{}", req.headers().get().unwrap_or(&ContentLength(0))));

    let mut child = cmd.spawn_async(&handle).expect("can't spawn CGI command");

    Box::new(req.body().concat2().and_then(move |body| {
        child
            .stdin()
            .take()
            .unwrap()
            .write_all(&body)
            .expect("can't write command output");

        Box::new(
            child
                .wait_with_output()
                .map(|command_result| {
                    /* let mut stderr = io::BufReader::new(command_result.stderr.as_slice()); */
                    /* println!("STDERR of git-http-backend:"); */
                    /* for line in stderr.by_ref().lines() { */
                    /*     println!("{:?}", line); */
                    /* } */
                    /* println!("end STDERR"); */
                    let mut stdout = io::BufReader::new(command_result.stdout.as_slice());

                    let mut data = vec![];
                    let mut response = Response::new();

                    for line in stdout.by_ref().lines() {
                        println!("STDOUT line: {:?}", line);
                        if line.as_ref().unwrap().is_empty() {
                            break;
                        }
                        let l: Vec<&str> =
                            line.as_ref().unwrap().as_str().splitn(2, ": ").collect();
                        if l[0] == "Status" {
                            response.set_status(hyper::StatusCode::Unregistered(
                                u16::from_str(l[1].split(" ").next().unwrap()).unwrap(),
                            ));
                        } else {
                            response
                                .headers_mut()
                                .set_raw(l[0].to_string(), l[1].to_string());
                        }
                    }
                    stdout
                        .read_to_end(&mut data)
                        .expect("can't read command output");
                    println!("BODY: {:?}", &data);
                    response.set_body(hyper::Chunk::from(data));
                    response
                })
                .map_err(|e| e.into()),
        )
    }))
}
<|endoftext|>