<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 a list of nodes matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/node/list`
    ///
    /// ## Inputs
    ///
    /// Any JSON that can be deserialized to a `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 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(&NodeRequest) -> Vec<Node>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/node/tag/$NodeId`
    fn put_node_tag(&NodeId, String) -> Result<(), Error>;

    /// Add a tag to an existing node.
    ///
    /// Tags can be used to locate nodes.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/node/tag/$NodeId`
    fn delete_node_tag(&NodeId, String) -> Result<(), Error>;

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

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate service.
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/tag/$ServiceId`
    fn put_service_tag(&ServiceId, String) -> Result<(), Error>;

    /// Add a tag to an existing service.
    ///
    /// Tags can be used to locate services.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/service/tag/$ServiceId`
    fn delete_service_tag(&ServiceId, String) -> Result<(), Error>;

    /// Read one value from an input enpoint
    ///
    /// # REST API
    ///
    /// GET /api/v1/service/value/$ServiceId
    fn get_service_value(&Service<Input>) -> Result<Value, Error>;

    /// Send one value to an output enpoint
    ///
    /// # REST API
    ///
    /// `PUT /api/v1/service/value/$ServiceId`
    fn put_service_value(&Service<Output>, Value) -> Result<(), Error>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/service/watch/$ServiceId`
    fn register_service_watch<F>(WatchOptions, cb: F)
                                 -> Result<Self::WatchGuard, Error>
        where F: FnMut(WatchEvent) + Send + Sync;

    /// 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,
}

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

    /// Restrict to input services in a given set. Note that if all
    /// the inputs do not have the same `ServiceKind`, the call to
    /// `register_service_watch` will fail.
    ///
    /// 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.  as it changes.
    pub fn with_inputs(self, req: InputRequest) -> Self {
        WatchOptions {
            source: self.source.and(req),
            ..self
        }
    }
}
<commit_msg>[fxbox/taxonomy] Refining API<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.
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/node/list`
    ///
    /// ## 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
    ///
    /// `PUT /api/v1/node/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 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/node/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/service/list`
    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
    ///
    /// `PUT /api/v1/service/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/node/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/service/value`
    fn get_service_value(&Vec<InputRequest>) -> Vec<(ServiceId, Result<Value, Error>)>;

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

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

    /// 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>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use itertools::Itertools;

use libimagstore::store::Entry;

use toml_query::read::TomlValueReadExt;
use toml_query::set::TomlValueSetExt;

use error::TagErrorKind;
use error::TagError as TE;
use error::ResultExt;
use error::Result;
use tag::{Tag, TagSlice};
use tag::is_tag_str;

use toml::Value;

pub trait Tagable {

    fn get_tags(&self) -> Result<Vec<Tag>>;
    fn set_tags(&mut self, ts: &[Tag]) -> Result<()>;

    fn add_tag(&mut self, t: Tag) -> Result<()>;
    fn remove_tag(&mut self, t: Tag) -> Result<()>;

    fn has_tag(&self, t: TagSlice) -> Result<bool>;
    fn has_tags(&self, ts: &[Tag]) -> Result<bool>;

}

impl Tagable for Value {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        let tags = try!(self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError));

        match tags {
            Some(&Value::Array(ref tags)) => {
                if !tags.iter().all(|t| is_match!(*t, Value::String(_))) {
                    return Err(TagErrorKind::TagTypeError.into());
                }
                if tags.iter().any(|t| match *t {
                    Value::String(ref s) => !is_tag_str(s).is_ok(),
                    _ => unreachable!()})
                {
                    return Err(TagErrorKind::NotATag.into());
                }

                Ok(tags.iter()
                    .cloned()
                    .map(|t| {
                        match t {
                           Value::String(s) => s,
                           _ => unreachable!(),
                        }
                    })
                    .collect())
            },
            None => Ok(vec![]),
            _ => Err(TagErrorKind::TagTypeError.into()),
        }
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        if ts.iter().any(|tag| !is_tag_str(tag).is_ok()) {
            debug!("Not a tag: '{}'", ts.iter().filter(|t| !is_tag_str(t).is_ok()).next().unwrap());
            return Err(TagErrorKind::NotATag.into());
        }

        let a = ts.iter().unique().map(|t| Value::String(t.clone())).collect();
        debug!("Setting tags = {:?}", a);
        self.set("tags", Value::Array(a))
            .map(|_| ())
            .chain_err(|| TagErrorKind::HeaderWriteError)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        if !try!(is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))) {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                tags.push(t);
                self.set_tags(&tags.into_iter().unique().collect::<Vec<_>>()[..])
            })
            .map(|_| ())
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        if !try!(is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))) {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                tags.retain(|tag| tag.clone() != t);
                self.set_tags(&tags[..])
            })
            .map(|_| ())
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        let tags = try!(self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError));

        if !tags.iter().all(|t| is_match!(*t, &Value::String(_))) {
            return Err(TagErrorKind::TagTypeError.into());
        }

        Ok(tags
           .iter()
           .any(|tag| {
               match *tag {
                   &Value::String(ref s) => { s == t },
                   _ => unreachable!()
               }
           }))
    }

    fn has_tags(&self, tags: &[Tag]) -> Result<bool> {
        let mut result = true;
        for tag in tags {
            result = result && try!(self.has_tag(tag));
        }

        Ok(result)
    }

}

impl Tagable for Entry {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        self.get_header().get_tags()
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        self.get_header_mut().set_tags(ts)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().add_tag(t)
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().remove_tag(t)
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        self.get_header().has_tag(t)
    }

    fn has_tags(&self, ts: &[Tag]) -> Result<bool> {
        self.get_header().has_tags(ts)
    }

}

<commit_msg>Switch from .set() to .insert()<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use itertools::Itertools;

use libimagstore::store::Entry;

use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;

use error::TagErrorKind;
use error::TagError as TE;
use error::ResultExt;
use error::Result;
use tag::{Tag, TagSlice};
use tag::is_tag_str;

use toml::Value;

pub trait Tagable {

    fn get_tags(&self) -> Result<Vec<Tag>>;
    fn set_tags(&mut self, ts: &[Tag]) -> Result<()>;

    fn add_tag(&mut self, t: Tag) -> Result<()>;
    fn remove_tag(&mut self, t: Tag) -> Result<()>;

    fn has_tag(&self, t: TagSlice) -> Result<bool>;
    fn has_tags(&self, ts: &[Tag]) -> Result<bool>;

}

impl Tagable for Value {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        let tags = try!(self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError));

        match tags {
            Some(&Value::Array(ref tags)) => {
                if !tags.iter().all(|t| is_match!(*t, Value::String(_))) {
                    return Err(TagErrorKind::TagTypeError.into());
                }
                if tags.iter().any(|t| match *t {
                    Value::String(ref s) => !is_tag_str(s).is_ok(),
                    _ => unreachable!()})
                {
                    return Err(TagErrorKind::NotATag.into());
                }

                Ok(tags.iter()
                    .cloned()
                    .map(|t| {
                        match t {
                           Value::String(s) => s,
                           _ => unreachable!(),
                        }
                    })
                    .collect())
            },
            None => Ok(vec![]),
            _ => Err(TagErrorKind::TagTypeError.into()),
        }
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        if ts.iter().any(|tag| !is_tag_str(tag).is_ok()) {
            debug!("Not a tag: '{}'", ts.iter().filter(|t| !is_tag_str(t).is_ok()).next().unwrap());
            return Err(TagErrorKind::NotATag.into());
        }

        let a = ts.iter().unique().map(|t| Value::String(t.clone())).collect();
        debug!("Setting tags = {:?}", a);
        self.insert("tag.values", Value::Array(a))
            .map(|_| ())
            .chain_err(|| TagErrorKind::HeaderWriteError)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        if !try!(is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))) {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                debug!("Pushing tag = {:?} to list = {:?}", t, tags);
                tags.push(t);
                self.set_tags(&tags.into_iter().unique().collect::<Vec<_>>()[..])
            })
            .map(|_| ())
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        if !try!(is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))) {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                tags.retain(|tag| tag.clone() != t);
                self.set_tags(&tags[..])
            })
            .map(|_| ())
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        let tags = try!(self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError));

        if !tags.iter().all(|t| is_match!(*t, &Value::String(_))) {
            return Err(TagErrorKind::TagTypeError.into());
        }

        Ok(tags
           .iter()
           .any(|tag| {
               match *tag {
                   &Value::String(ref s) => { s == t },
                   _ => unreachable!()
               }
           }))
    }

    fn has_tags(&self, tags: &[Tag]) -> Result<bool> {
        let mut result = true;
        for tag in tags {
            result = result && try!(self.has_tag(tag));
        }

        Ok(result)
    }

}

impl Tagable for Entry {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        self.get_header().get_tags()
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        self.get_header_mut().set_tags(ts)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().add_tag(t)
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().remove_tag(t)
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        self.get_header().has_tag(t)
    }

    fn has_tags(&self, ts: &[Tag]) -> Result<bool> {
        self.get_header().has_tags(ts)
    }

}

<|endoftext|>


<commit_before>use synom::IResult;
use syn;
use syn::parse::{ident, string, boolean};

use hints::Hint;
use options::Options;

#[derive(PartialEq, Debug)]
pub struct MacroInput {
    pub name: String,
    pub sample_source: String,
    pub options: Options,
}

macro_rules! fail {
    ($base:expr, $input:expr) => {
        return Err(format!("{}, but remaining input was '{}'", $base, $input.trim()));
    };
}

named!(string_or_ident -> String,
    alt!(
        ident => { |ident: syn::Ident| ident.to_string() }
        |
        string => { |lit: syn::StrLit| lit.value }
    )
);

named!(comma_or_closing_brace -> &str,
    alt!(punct!(",") | punct!("}"))
);

pub fn full_macro(input: &str) -> Result<MacroInput, String> {
    let input = input.trim();

    let prefix = "json_typegen!(";
    if !input.starts_with(prefix) {
        fail!("Unable to parse macro. Expected 'json_typegen!('", input)
    }

    let suffix = ");";
    if !input.ends_with(suffix) {
        fail!("Unable to parse macro. Expected it to end with ');'", input)
    }

    let input = &input[prefix.len() .. input.len() - suffix.len()].trim();

    macro_input(input)
}

pub fn macro_input(input: &str) -> Result<MacroInput, String> {
    let (input, name) = match string(input) {
        IResult::Done(input, lit) => (input, lit.value),
        IResult::Error => {
            fail!("First argument must be a string literal", input)
        }
    };

    let input = skip(input, ",", "Expected a comma after first argument")?;

    let (input, sample_source) = match string(input) {
        IResult::Done(input, lit) => (input, lit.value),
        IResult::Error => {
            fail!("Second argument must be a string literal", input)
        }
    };

    if input.trim().is_empty() {
        return Ok(MacroInput { name, sample_source, options: Options::default() });
    }

    let input = skip(input, ",", "Expected a comma or end of input after second argument")?;

    let options = match string(input) {
        IResult::Done(_, lit) => options(&lit.value)?,
        IResult::Error => options(input)?,
    };

    Ok(MacroInput { name, sample_source, options })
}

pub fn options(input: &str) -> Result<Options, String> {
    let mut options = Options::default();

    let input_after_block = block(input, |remaining, option_name| {
        match option_name.as_ref() {
            "derives" => string_option(remaining, "derives", |val| {
                options.derives = val;
            }),
            "field_visibility" => string_option(remaining, "field_visibility", |val| {
                options.field_visibility = Some(val);
            }),
            "deny_unknown_fields" => boolean_option(remaining, "deny_unknown_fields", |val| {
                options.deny_unknown_fields = val;
            }),
            "use_default_for_missing_fields" => boolean_option(remaining, "use_default_for_missing_fields", |val| {
                options.use_default_for_missing_fields = val;
            }),
            "allow_option_vec" => boolean_option(remaining, "allow_option_vec", |val| {
                options.allow_option_vec = val;
            }),
            key if key.is_empty() || key.starts_with("/") => {
                let (rem, hints) = pointer_block(remaining)?;
                for hint in hints {
                    options.hints.push((key.to_string(), hint));
                }
                Ok(rem)
            },
            _ => Err(format!("Unknown option: {}", option_name)),
        }
    })?;

    if !input_after_block.trim().is_empty() {
        fail!("Expected no further tokens after options block", input);
    }

    Ok(options)
}

fn pointer_block(input: &str) -> Result<(&str, Vec<Hint>), String> {
    let mut hints = Vec::new();

    let input = skip_colon(input)?;

    let input_after_block = block(input, |input_after_key, key| {
        match key.as_ref() {
            "use_type" => string_option(input_after_key, "use_type", |val| {
                let hint = match val.as_ref() {
                    "map" => Hint::default_map(),
                    _ => Hint::opaque_type(val),
                };
                hints.push(hint);
            }),
            "type_name" => string_option(input_after_key, "type_name", |val| {
                hints.push(Hint::type_name(val));
            }),
            _ => return Err(format!("Unknown option: {}", key)),
        }
    })?;

    Ok((input_after_block, hints))
}

fn string_option<'a, F: FnMut(String)>(
    input: &'a str,
    name: &'static str,
    mut consumer: F,
) -> Result<&'a str, String> {
    let input = skip_colon(input)?;

    match string(input) {
        IResult::Done(rem, lit) => {
            consumer(lit.value);
            Ok(rem)
        }
        IResult::Error => fail!(
            format!("The argument to '{}' has to be a string literal", name),
            input
        ),
    }
}

fn boolean_option<'a, F: FnMut(bool)>(
    input: &'a str,
    name: &'static str,
    mut consumer: F,
) -> Result<&'a str, String> {

    // interpret { foo, bar } as { foo: true, bar: true }
    if let IResult::Done(_, _) = comma_or_closing_brace(input) {
        consumer(true);
        return Ok(input);
    }

    let input = skip_colon(input)?;

    match boolean(input) {
        IResult::Done(rem, val) => {
            consumer(val);
            Ok(rem)
        }
        IResult::Error => fail!(
            format!("The argument to '{}' has to be a boolean literal", name),
            input
        ),
    }
}

fn block<'input, F>(input: &'input str, mut field_parser: F) -> Result<&'input str, String>
    where F: FnMut(&'input str, String) -> Result<&'input str, String>
{
    let mut input = skip(input, "{", "Expected an opening brace")?;

    loop {
        if let IResult::Done(rem, _) = punct!(input, "}") {
            break Ok(rem);
        }

        let (remaining, key) = match string_or_ident(input) {
            IResult::Done(rem, value) => (rem, value),
            IResult::Error => fail!("Expected an option name", input),
        };

        let remaining = field_parser(remaining, key)?;

        if let IResult::Done(rem, _) = punct!(remaining, "}") {
            break Ok(rem);
        }

        input = skip(remaining, ",", "Expected a comma or a closing brace")?;
    }
}

fn skip_colon(input: &str) -> Result<&str, String> {
    skip(input, ":", "Expected a colon")
}

fn skip<'a>(input: &'a str, symbol: &'static str, msg: &str) -> Result<&'a str, String> {
    match punct!(input, symbol) {
        IResult::Done(rem, _) => Ok(rem),
        IResult::Error => {
            fail!(msg, input);
        }
    }
}

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

    #[test]
    fn barebones_input() {
        assert_eq!(macro_input(r#" "Bob", "{}" "#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }

    #[test]
    fn barebones_input_with_empty_options() {
        assert_eq!(macro_input(r#" "Bob", "{}", {} "#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }

    #[test]
    fn barebones_input_with_options_as_string_literal() {
        assert_eq!(macro_input(r#" "Bob", "{}", "{}" "#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }
}

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

    #[test]
    fn parses_derives() {
        let mut expected = Options::default();
        expected.derives = "Foo, Bar".into();

        assert_eq!(
            options(r#"{
                "derives": "Foo, Bar",
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn rejects_unknown_options() {
        let result = options(r#"{
            "foo_opt": {},
        }"#);

        assert!(result.is_err(), "Parse result was not Err, but:\n{:?}", result);
        if let Err(message) = result {
            assert!(message.contains("foo_opt"), "Error message was:\n'{}'", message);
        }
    }

    #[test]
    fn parses_empty_pointer_block() {
        let expected = Options::default();

        assert_eq!(
            options(r#"{
                "/foo/bar": {},
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_map_hint() {
        let mut expected = Options::default();
        expected.hints.push(("/foo/bar".to_string(), Hint::default_map()));

        assert_eq!(
            options(r#"{
                "/foo/bar": {
                    use_type: "map"
                },
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_opaque_type_hint() {
        let mut expected = Options::default();
        expected.hints.push(("/foo/bar".to_string(), Hint::opaque_type("FooBar")));

        assert_eq!(
            options(r#"{
                "/foo/bar": {
                    use_type: "FooBar"
                },
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_type_name_hint() {
        let mut expected = Options::default();
        expected.hints.push(("/baz".to_string(), Hint::type_name("SomeName")));

        assert_eq!(
            options(r#"{
                "/baz": {
                    type_name: "SomeName"
                },
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_pointer_to_root() {
        let expected = Options::default();

        assert_eq!(
            options(r#"{
                "": {},
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_keys_given_as_bare_identifiers() {
        let mut expected = Options::default();
        expected.derives = "Foo, Bar".into();

        assert_eq!(
            options(r#"{
                derives: "Foo, Bar",
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn trailing_comma_is_optional() {
        let mut expected = Options::default();
        expected.derives = "Foo, Bar".into();

        assert_eq!(
            options(r#"{
                "derives": "Foo, Bar"
            }"#),
            Ok(expected.clone())
        );

        assert_eq!(
            options(r#"{
                "derives": "Foo, Bar",
            }"#),
            Ok(expected)
        );
    }
}

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

    #[test]
    fn full_macro_accepts_barebones_macro() {
        assert_eq!(full_macro(r#"json_typegen!("Bob", "{}");"#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }

    #[test]
    fn full_macro_accepts_barebones_macro_with_options() {
        assert_eq!(full_macro(r#"json_typegen!("Bob", "{}", {});"#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }
}
<commit_msg>shared: Add basic doc comments to parse functions<commit_after>use synom::IResult;
use syn;
use syn::parse::{ident, string, boolean};

use hints::Hint;
use options::Options;

#[derive(PartialEq, Debug)]
pub struct MacroInput {
    pub name: String,
    pub sample_source: String,
    pub options: Options,
}

macro_rules! fail {
    ($base:expr, $input:expr) => {
        return Err(format!("{}, but remaining input was '{}'", $base, $input.trim()));
    };
}

named!(string_or_ident -> String,
    alt!(
        ident => { |ident: syn::Ident| ident.to_string() }
        |
        string => { |lit: syn::StrLit| lit.value }
    )
);

named!(comma_or_closing_brace -> &str,
    alt!(punct!(",") | punct!("}"))
);

/// Parses a full `json_typegen` macro invocation. E.g. something like
/// `json_typegen!("Foo", "http://example.com/sample.json", { deny_unknown_fields });`
pub fn full_macro(input: &str) -> Result<MacroInput, String> {
    let input = input.trim();

    let prefix = "json_typegen!(";
    if !input.starts_with(prefix) {
        fail!("Unable to parse macro. Expected 'json_typegen!('", input)
    }

    let suffix = ");";
    if !input.ends_with(suffix) {
        fail!("Unable to parse macro. Expected it to end with ');'", input)
    }

    let input = &input[prefix.len() .. input.len() - suffix.len()].trim();

    macro_input(input)
}

/// Parses the arguments to a `json_typegen` macro invocation. E.g. something like
/// `"Foo", "http://example.com/sample.json", { deny_unknown_fields }`
pub fn macro_input(input: &str) -> Result<MacroInput, String> {
    let (input, name) = match string(input) {
        IResult::Done(input, lit) => (input, lit.value),
        IResult::Error => {
            fail!("First argument must be a string literal", input)
        }
    };

    let input = skip(input, ",", "Expected a comma after first argument")?;

    let (input, sample_source) = match string(input) {
        IResult::Done(input, lit) => (input, lit.value),
        IResult::Error => {
            fail!("Second argument must be a string literal", input)
        }
    };

    if input.trim().is_empty() {
        return Ok(MacroInput { name, sample_source, options: Options::default() });
    }

    let input = skip(input, ",", "Expected a comma or end of input after second argument")?;

    let options = match string(input) {
        IResult::Done(_, lit) => options(&lit.value)?,
        IResult::Error => options(input)?,
    };

    Ok(MacroInput { name, sample_source, options })
}

/// Parses the options block of a `json_typegen` macro invocation. E.g. something like:
/// `{ deny_unknown_fields }`
pub fn options(input: &str) -> Result<Options, String> {
    let mut options = Options::default();

    let input_after_block = block(input, |remaining, option_name| {
        match option_name.as_ref() {
            "derives" => string_option(remaining, "derives", |val| {
                options.derives = val;
            }),
            "field_visibility" => string_option(remaining, "field_visibility", |val| {
                options.field_visibility = Some(val);
            }),
            "deny_unknown_fields" => boolean_option(remaining, "deny_unknown_fields", |val| {
                options.deny_unknown_fields = val;
            }),
            "use_default_for_missing_fields" => boolean_option(remaining, "use_default_for_missing_fields", |val| {
                options.use_default_for_missing_fields = val;
            }),
            "allow_option_vec" => boolean_option(remaining, "allow_option_vec", |val| {
                options.allow_option_vec = val;
            }),
            key if key.is_empty() || key.starts_with("/") => {
                let (rem, hints) = pointer_block(remaining)?;
                for hint in hints {
                    options.hints.push((key.to_string(), hint));
                }
                Ok(rem)
            },
            _ => Err(format!("Unknown option: {}", option_name)),
        }
    })?;

    if !input_after_block.trim().is_empty() {
        fail!("Expected no further tokens after options block", input);
    }

    Ok(options)
}

fn pointer_block(input: &str) -> Result<(&str, Vec<Hint>), String> {
    let mut hints = Vec::new();

    let input = skip_colon(input)?;

    let input_after_block = block(input, |input_after_key, key| {
        match key.as_ref() {
            "use_type" => string_option(input_after_key, "use_type", |val| {
                let hint = match val.as_ref() {
                    "map" => Hint::default_map(),
                    _ => Hint::opaque_type(val),
                };
                hints.push(hint);
            }),
            "type_name" => string_option(input_after_key, "type_name", |val| {
                hints.push(Hint::type_name(val));
            }),
            _ => return Err(format!("Unknown option: {}", key)),
        }
    })?;

    Ok((input_after_block, hints))
}

fn string_option<'a, F: FnMut(String)>(
    input: &'a str,
    name: &'static str,
    mut consumer: F,
) -> Result<&'a str, String> {
    let input = skip_colon(input)?;

    match string(input) {
        IResult::Done(rem, lit) => {
            consumer(lit.value);
            Ok(rem)
        }
        IResult::Error => fail!(
            format!("The argument to '{}' has to be a string literal", name),
            input
        ),
    }
}

fn boolean_option<'a, F: FnMut(bool)>(
    input: &'a str,
    name: &'static str,
    mut consumer: F,
) -> Result<&'a str, String> {

    // interpret { foo, bar } as { foo: true, bar: true }
    if let IResult::Done(_, _) = comma_or_closing_brace(input) {
        consumer(true);
        return Ok(input);
    }

    let input = skip_colon(input)?;

    match boolean(input) {
        IResult::Done(rem, val) => {
            consumer(val);
            Ok(rem)
        }
        IResult::Error => fail!(
            format!("The argument to '{}' has to be a boolean literal", name),
            input
        ),
    }
}

fn block<'input, F>(input: &'input str, mut field_parser: F) -> Result<&'input str, String>
    where F: FnMut(&'input str, String) -> Result<&'input str, String>
{
    let mut input = skip(input, "{", "Expected an opening brace")?;

    loop {
        if let IResult::Done(rem, _) = punct!(input, "}") {
            break Ok(rem);
        }

        let (remaining, key) = match string_or_ident(input) {
            IResult::Done(rem, value) => (rem, value),
            IResult::Error => fail!("Expected an option name", input),
        };

        let remaining = field_parser(remaining, key)?;

        if let IResult::Done(rem, _) = punct!(remaining, "}") {
            break Ok(rem);
        }

        input = skip(remaining, ",", "Expected a comma or a closing brace")?;
    }
}

fn skip_colon(input: &str) -> Result<&str, String> {
    skip(input, ":", "Expected a colon")
}

fn skip<'a>(input: &'a str, symbol: &'static str, msg: &str) -> Result<&'a str, String> {
    match punct!(input, symbol) {
        IResult::Done(rem, _) => Ok(rem),
        IResult::Error => {
            fail!(msg, input);
        }
    }
}

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

    #[test]
    fn barebones_input() {
        assert_eq!(macro_input(r#" "Bob", "{}" "#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }

    #[test]
    fn barebones_input_with_empty_options() {
        assert_eq!(macro_input(r#" "Bob", "{}", {} "#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }

    #[test]
    fn barebones_input_with_options_as_string_literal() {
        assert_eq!(macro_input(r#" "Bob", "{}", "{}" "#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }
}

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

    #[test]
    fn parses_derives() {
        let mut expected = Options::default();
        expected.derives = "Foo, Bar".into();

        assert_eq!(
            options(r#"{
                "derives": "Foo, Bar",
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn rejects_unknown_options() {
        let result = options(r#"{
            "foo_opt": {},
        }"#);

        assert!(result.is_err(), "Parse result was not Err, but:\n{:?}", result);
        if let Err(message) = result {
            assert!(message.contains("foo_opt"), "Error message was:\n'{}'", message);
        }
    }

    #[test]
    fn parses_empty_pointer_block() {
        let expected = Options::default();

        assert_eq!(
            options(r#"{
                "/foo/bar": {},
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_map_hint() {
        let mut expected = Options::default();
        expected.hints.push(("/foo/bar".to_string(), Hint::default_map()));

        assert_eq!(
            options(r#"{
                "/foo/bar": {
                    use_type: "map"
                },
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_opaque_type_hint() {
        let mut expected = Options::default();
        expected.hints.push(("/foo/bar".to_string(), Hint::opaque_type("FooBar")));

        assert_eq!(
            options(r#"{
                "/foo/bar": {
                    use_type: "FooBar"
                },
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_type_name_hint() {
        let mut expected = Options::default();
        expected.hints.push(("/baz".to_string(), Hint::type_name("SomeName")));

        assert_eq!(
            options(r#"{
                "/baz": {
                    type_name: "SomeName"
                },
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_pointer_to_root() {
        let expected = Options::default();

        assert_eq!(
            options(r#"{
                "": {},
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn parses_keys_given_as_bare_identifiers() {
        let mut expected = Options::default();
        expected.derives = "Foo, Bar".into();

        assert_eq!(
            options(r#"{
                derives: "Foo, Bar",
            }"#),
            Ok(expected)
        );
    }

    #[test]
    fn trailing_comma_is_optional() {
        let mut expected = Options::default();
        expected.derives = "Foo, Bar".into();

        assert_eq!(
            options(r#"{
                "derives": "Foo, Bar"
            }"#),
            Ok(expected.clone())
        );

        assert_eq!(
            options(r#"{
                "derives": "Foo, Bar",
            }"#),
            Ok(expected)
        );
    }
}

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

    #[test]
    fn full_macro_accepts_barebones_macro() {
        assert_eq!(full_macro(r#"json_typegen!("Bob", "{}");"#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }

    #[test]
    fn full_macro_accepts_barebones_macro_with_options() {
        assert_eq!(full_macro(r#"json_typegen!("Bob", "{}", {});"#), Ok(MacroInput {
            name: "Bob".to_string(),
            sample_source: "{}".to_string(),
            options: Options::default(),
        }));
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use graphics::{ Context, DrawState, Graphics, Viewport };
use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE;
use { gfx, Texture };

const POS_COMPONENTS: usize = 2;
const UV_COMPONENTS: usize = 2;

// Boiler plate for automatic attribute construction.
// Needs to be improved on gfx-rs side.
// For some reason, using ``*_COMPONENT` triggers some macros errors.

gfx_vertex!( PositionFormat {
    pos@ pos: [f32; 2],
});

gfx_vertex!( ColorFormat {
    color@ color: [f32; 4],
});

gfx_vertex!( TexCoordsFormat {
    uv@ uv: [f32; 2],
});

gfx_parameters!( Params {
    color@ color: [f32; 4],
});

gfx_parameters!( ParamsUV {
    color@ color: [f32; 4],
    s_texture@ texture: gfx::shade::TextureParam<R>,
});

/// The data used for drawing 2D graphics.
pub struct Gfx2d<R: gfx::Resources> {
    buffer_pos: gfx::handle::Buffer<R, f32>,
    buffer_uv: gfx::handle::Buffer<R, f32>,
    batch: gfx::batch::Full<Params<R>>,
    batch_uv: gfx::batch::Full<ParamsUV<R>>,
}

impl<R: gfx::Resources> Gfx2d<R> {
    /// Creates a new Gfx2d object.
    pub fn new<F>(factory: &mut F) -> Self
        where F: gfx::Factory<R>
    {
        use gfx::traits::*;
        use gfx::VertexFormat;
        use shaders::{ colored, textured };

        let program = {
            let vertex = gfx::ShaderSource {
                glsl_120: Some(colored::VERTEX_GLSL_120),
                glsl_150: Some(colored::VERTEX_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            let fragment = gfx::ShaderSource {
                glsl_120: Some(colored::FRAGMENT_GLSL_120),
                glsl_150: Some(colored::FRAGMENT_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            factory.link_program_source(
                vertex,
                fragment
            ).unwrap()
        };

        let program_uv = {
            let vertex = gfx::ShaderSource {
                glsl_120: Some(textured::VERTEX_GLSL_120),
                glsl_150: Some(textured::VERTEX_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            let fragment = gfx::ShaderSource {
                glsl_120: Some(textured::FRAGMENT_GLSL_120),
                glsl_150: Some(textured::FRAGMENT_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            factory.link_program_source(
                vertex,
                fragment
            ).unwrap()
        };

        let buffer_pos = factory.create_buffer_dynamic(
            POS_COMPONENTS * BUFFER_SIZE,
            gfx::BufferRole::Vertex
        );
        let buffer_uv = factory.create_buffer_dynamic(
            UV_COMPONENTS * BUFFER_SIZE,
            gfx::BufferRole::Vertex
        );

        let mut mesh = gfx::Mesh::new(BUFFER_SIZE as u32);
        mesh.attributes.extend(
            PositionFormat::generate(&buffer_pos)
        );

        // Reuse parameters from `mesh`.
        let mut mesh_uv = mesh.clone();
        mesh_uv.attributes.extend(
            TexCoordsFormat::generate(&buffer_uv)
        );

        let params = Params {
            color: [1.0; 4],
            _r: PhantomData,
        };
        let mut batch = gfx::batch::Full::new(
            mesh,
            program,
            params
        ).unwrap();

        let sampler = factory.create_sampler(
            gfx::tex::SamplerInfo::new(
                gfx::tex::FilterMethod::Trilinear,
                gfx::tex::WrapMode::Clamp
            )
        );

        let tex_handle = Texture::empty(factory).unwrap().handle();

        let params_uv = ParamsUV {
            color: [1.0; 4],
            texture: (tex_handle, Some(sampler)),
            _r: PhantomData,
        };
        let mut batch_uv = gfx::batch::Full::new(
            mesh_uv,
            program_uv,
            params_uv
        ).unwrap();

        // Disable culling.
        batch.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullFace::Nothing);
        batch_uv.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullFace::Nothing);

        Gfx2d {
            buffer_pos: buffer_pos.cast(),
            buffer_uv: buffer_uv.cast(),
            batch: batch,
            batch_uv: batch_uv,
        }
    }

    /// Renders graphics to a Gfx renderer.
    pub fn draw<C, O, F>(
        &mut self,
        renderer: &mut gfx::Renderer<R, C>,
        output: &O,
        viewport: Viewport,
        mut f: F
    )
        where C: gfx::CommandBuffer<R>,
              O: gfx::Output<R>,
              F: FnMut(Context, &mut GfxGraphics<R, C, O>)
    {
        let ref mut g = GfxGraphics::new(
            renderer,
            output,
            self
        );
        let c = Context::new_viewport(viewport);
        f(c, g);
    }
}

/// Used for rendering 2D graphics.
pub struct GfxGraphics<'a, R, C, O>
    where R: gfx::Resources + 'a,
          C: gfx::CommandBuffer<R> + 'a,
          O: gfx::Output<R> + 'a,
          R::Buffer: 'a,
          R::ArrayBuffer: 'a,
          R::Shader: 'a,
          R::Program: 'a,
          R::FrameBuffer: 'a,
          R::Surface: 'a,
          R::Texture: 'a,
          R::Sampler: 'a
{
    renderer: &'a mut gfx::Renderer<R, C>,
    output: &'a O,
    g2d: &'a mut Gfx2d<R>,
}

impl<'a, R, C, O> GfxGraphics<'a, R, C, O>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>,
          O: gfx::Output<R>
{
    /// Creates a new object for rendering 2D graphics.
    pub fn new(renderer: &'a mut gfx::Renderer<R, C>,
               output: &'a O,
               g2d: &'a mut Gfx2d<R>) -> Self {
        GfxGraphics {
            renderer: renderer,
            output: output,
            g2d: g2d,
        }
    }

    /// Returns true if texture has alpha channel.
    pub fn has_texture_alpha(&self, texture: &Texture<R>) -> bool {
        use gfx::tex::Components::RGBA;

        texture.handle().get_info().format.get_components() == Some(RGBA)
    }
}

impl<'a, R, C, O> Graphics for GfxGraphics<'a, R, C, O>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>,
          O: gfx::Output<R>,
          R::Buffer: 'a,
          R::ArrayBuffer: 'a,
          R::Shader: 'a,
          R::Program: 'a,
          R::FrameBuffer: 'a,
          R::Surface: 'a,
          R::Texture: 'a,
          R::Sampler: 'a
{
    type Texture = Texture<R>;

    fn clear_color(&mut self, color: [f32; 4]) {
        let &mut GfxGraphics {
            ref mut renderer,
            output,
            ..
        } = self;
        renderer.clear(
            gfx::ClearData {
                color: color,
                depth: 0.0,
                stencil: 0,
            },
            gfx::COLOR,
            output
        );
    }

    fn clear_stencil(&mut self, value: u8) {
        let &mut GfxGraphics {
            ref mut renderer,
            output,
            ..
        } = self;
        renderer.clear(
            gfx::ClearData {
                color: [0.0; 4],
                depth: 0.0,
                stencil: value,
            },
            gfx::STENCIL,
            output
        );
    }

    fn tri_list<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref output,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut batch,
                ..
            },
        } = self;

        batch.state = *draw_state;
        batch.params.color = *color;

        f(&mut |vertices: &[f32]| {
            renderer.update_buffer(&buffer_pos.raw(), vertices, 0).unwrap();

            let n = vertices.len() / POS_COMPONENTS;
            batch.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
            };
            let _ = renderer.draw(batch, None, *output);
        })
    }

    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref output,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_uv,
                ref mut batch_uv,
                ..
            },
        } = self;

        batch_uv.state = *draw_state;
        batch_uv.params.texture.0 = texture.handle();
        batch_uv.params.color = *color;

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            assert_eq!(
                vertices.len() * UV_COMPONENTS,
                texture_coords.len() * POS_COMPONENTS
            );
            renderer.update_buffer(&buffer_pos.raw(), vertices, 0).unwrap();
            renderer.update_buffer(&buffer_uv.raw(), texture_coords, 0).unwrap();

            let n = vertices.len() / POS_COMPONENTS;
            batch_uv.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
            };
            let _ = renderer.draw(batch_uv, None, *output);
        })
    }
}
<commit_msg>Make Gfx2d::draw take an FnOnce<commit_after>use std::marker::PhantomData;
use graphics::{ Context, DrawState, Graphics, Viewport };
use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE;
use { gfx, Texture };

const POS_COMPONENTS: usize = 2;
const UV_COMPONENTS: usize = 2;

// Boiler plate for automatic attribute construction.
// Needs to be improved on gfx-rs side.
// For some reason, using ``*_COMPONENT` triggers some macros errors.

gfx_vertex!( PositionFormat {
    pos@ pos: [f32; 2],
});

gfx_vertex!( ColorFormat {
    color@ color: [f32; 4],
});

gfx_vertex!( TexCoordsFormat {
    uv@ uv: [f32; 2],
});

gfx_parameters!( Params {
    color@ color: [f32; 4],
});

gfx_parameters!( ParamsUV {
    color@ color: [f32; 4],
    s_texture@ texture: gfx::shade::TextureParam<R>,
});

/// The data used for drawing 2D graphics.
pub struct Gfx2d<R: gfx::Resources> {
    buffer_pos: gfx::handle::Buffer<R, f32>,
    buffer_uv: gfx::handle::Buffer<R, f32>,
    batch: gfx::batch::Full<Params<R>>,
    batch_uv: gfx::batch::Full<ParamsUV<R>>,
}

impl<R: gfx::Resources> Gfx2d<R> {
    /// Creates a new Gfx2d object.
    pub fn new<F>(factory: &mut F) -> Self
        where F: gfx::Factory<R>
    {
        use gfx::traits::*;
        use gfx::VertexFormat;
        use shaders::{ colored, textured };

        let program = {
            let vertex = gfx::ShaderSource {
                glsl_120: Some(colored::VERTEX_GLSL_120),
                glsl_150: Some(colored::VERTEX_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            let fragment = gfx::ShaderSource {
                glsl_120: Some(colored::FRAGMENT_GLSL_120),
                glsl_150: Some(colored::FRAGMENT_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            factory.link_program_source(
                vertex,
                fragment
            ).unwrap()
        };

        let program_uv = {
            let vertex = gfx::ShaderSource {
                glsl_120: Some(textured::VERTEX_GLSL_120),
                glsl_150: Some(textured::VERTEX_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            let fragment = gfx::ShaderSource {
                glsl_120: Some(textured::FRAGMENT_GLSL_120),
                glsl_150: Some(textured::FRAGMENT_GLSL_150_CORE),
                .. gfx::ShaderSource::empty()
            };
            factory.link_program_source(
                vertex,
                fragment
            ).unwrap()
        };

        let buffer_pos = factory.create_buffer_dynamic(
            POS_COMPONENTS * BUFFER_SIZE,
            gfx::BufferRole::Vertex
        );
        let buffer_uv = factory.create_buffer_dynamic(
            UV_COMPONENTS * BUFFER_SIZE,
            gfx::BufferRole::Vertex
        );

        let mut mesh = gfx::Mesh::new(BUFFER_SIZE as u32);
        mesh.attributes.extend(
            PositionFormat::generate(&buffer_pos)
        );

        // Reuse parameters from `mesh`.
        let mut mesh_uv = mesh.clone();
        mesh_uv.attributes.extend(
            TexCoordsFormat::generate(&buffer_uv)
        );

        let params = Params {
            color: [1.0; 4],
            _r: PhantomData,
        };
        let mut batch = gfx::batch::Full::new(
            mesh,
            program,
            params
        ).unwrap();

        let sampler = factory.create_sampler(
            gfx::tex::SamplerInfo::new(
                gfx::tex::FilterMethod::Trilinear,
                gfx::tex::WrapMode::Clamp
            )
        );

        let tex_handle = Texture::empty(factory).unwrap().handle();

        let params_uv = ParamsUV {
            color: [1.0; 4],
            texture: (tex_handle, Some(sampler)),
            _r: PhantomData,
        };
        let mut batch_uv = gfx::batch::Full::new(
            mesh_uv,
            program_uv,
            params_uv
        ).unwrap();

        // Disable culling.
        batch.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullFace::Nothing);
        batch_uv.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullFace::Nothing);

        Gfx2d {
            buffer_pos: buffer_pos.cast(),
            buffer_uv: buffer_uv.cast(),
            batch: batch,
            batch_uv: batch_uv,
        }
    }

    /// Renders graphics to a Gfx renderer.
    pub fn draw<C, O, F>(
        &mut self,
        renderer: &mut gfx::Renderer<R, C>,
        output: &O,
        viewport: Viewport,
        f: F
    )
        where C: gfx::CommandBuffer<R>,
              O: gfx::Output<R>,
              F: FnOnce(Context, &mut GfxGraphics<R, C, O>)
    {
        let ref mut g = GfxGraphics::new(
            renderer,
            output,
            self
        );
        let c = Context::new_viewport(viewport);
        f(c, g);
    }
}

/// Used for rendering 2D graphics.
pub struct GfxGraphics<'a, R, C, O>
    where R: gfx::Resources + 'a,
          C: gfx::CommandBuffer<R> + 'a,
          O: gfx::Output<R> + 'a,
          R::Buffer: 'a,
          R::ArrayBuffer: 'a,
          R::Shader: 'a,
          R::Program: 'a,
          R::FrameBuffer: 'a,
          R::Surface: 'a,
          R::Texture: 'a,
          R::Sampler: 'a
{
    renderer: &'a mut gfx::Renderer<R, C>,
    output: &'a O,
    g2d: &'a mut Gfx2d<R>,
}

impl<'a, R, C, O> GfxGraphics<'a, R, C, O>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>,
          O: gfx::Output<R>
{
    /// Creates a new object for rendering 2D graphics.
    pub fn new(renderer: &'a mut gfx::Renderer<R, C>,
               output: &'a O,
               g2d: &'a mut Gfx2d<R>) -> Self {
        GfxGraphics {
            renderer: renderer,
            output: output,
            g2d: g2d,
        }
    }

    /// Returns true if texture has alpha channel.
    pub fn has_texture_alpha(&self, texture: &Texture<R>) -> bool {
        use gfx::tex::Components::RGBA;

        texture.handle().get_info().format.get_components() == Some(RGBA)
    }
}

impl<'a, R, C, O> Graphics for GfxGraphics<'a, R, C, O>
    where R: gfx::Resources,
          C: gfx::CommandBuffer<R>,
          O: gfx::Output<R>,
          R::Buffer: 'a,
          R::ArrayBuffer: 'a,
          R::Shader: 'a,
          R::Program: 'a,
          R::FrameBuffer: 'a,
          R::Surface: 'a,
          R::Texture: 'a,
          R::Sampler: 'a
{
    type Texture = Texture<R>;

    fn clear_color(&mut self, color: [f32; 4]) {
        let &mut GfxGraphics {
            ref mut renderer,
            output,
            ..
        } = self;
        renderer.clear(
            gfx::ClearData {
                color: color,
                depth: 0.0,
                stencil: 0,
            },
            gfx::COLOR,
            output
        );
    }

    fn clear_stencil(&mut self, value: u8) {
        let &mut GfxGraphics {
            ref mut renderer,
            output,
            ..
        } = self;
        renderer.clear(
            gfx::ClearData {
                color: [0.0; 4],
                depth: 0.0,
                stencil: value,
            },
            gfx::STENCIL,
            output
        );
    }

    fn tri_list<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref output,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut batch,
                ..
            },
        } = self;

        batch.state = *draw_state;
        batch.params.color = *color;

        f(&mut |vertices: &[f32]| {
            renderer.update_buffer(&buffer_pos.raw(), vertices, 0).unwrap();

            let n = vertices.len() / POS_COMPONENTS;
            batch.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
            };
            let _ = renderer.draw(batch, None, *output);
        })
    }

    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref output,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_uv,
                ref mut batch_uv,
                ..
            },
        } = self;

        batch_uv.state = *draw_state;
        batch_uv.params.texture.0 = texture.handle();
        batch_uv.params.color = *color;

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            assert_eq!(
                vertices.len() * UV_COMPONENTS,
                texture_coords.len() * POS_COMPONENTS
            );
            renderer.update_buffer(&buffer_pos.raw(), vertices, 0).unwrap();
            renderer.update_buffer(&buffer_uv.raw(), texture_coords, 0).unwrap();

            let n = vertices.len() / POS_COMPONENTS;
            batch_uv.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
            };
            let _ = renderer.draw(batch_uv, None, *output);
        })
    }
}
<|endoftext|>


<commit_before>use std::sync::{Arc,Mutex};
use std::collections::HashMap;
use std::io::BufReader;
use webpki;
use rustls::{ResolvesServerCert, SignatureScheme, PrivateKey};
use rustls::sign::{CertifiedKey, RSASigningKey};
use rustls::internal::pemfile;

use sozu_command::messages::{CertificateAndKey, CertFingerprint, AddCertificate, RemoveCertificate};
use sozu_command::certificate::calculate_fingerprint_from_der;

use network::trie::TrieNode;

struct TlsData {
  pub cert:     CertifiedKey,
  pub refcount: usize,
}

pub struct CertificateResolver {
  domains:      TrieNode<CertFingerprint>,
  certificates: HashMap<CertFingerprint, TlsData>,
}

impl CertificateResolver {
  pub fn new() -> CertificateResolver {
    CertificateResolver {
      domains:      TrieNode::root(),
      certificates: HashMap::new(),
    }
  }

  pub fn add_certificate(&mut self, add_certificate: AddCertificate) -> Option<CertFingerprint> {
    if let Some(certified_key) = generate_certified_key(add_certificate.certificate) {
      if add_certificate.names.is_empty() {
        //FIXME: waiting for https://github.com/briansmith/webpki/pull/65 to merge to get the DNS names
        // create a untrusted::Input
        // let input = untrusted::Input::from(&certs[0].0);
        // create an EndEntityCert
        // let ee = webpki::EndEntityCert::from(input).unwrap()
        // get names
        // let dns_names = ee.list_dns_names()
        // names.extend(dns_names.drain(..).map(|name| name.to_String()));
        unimplemented!("the rustls proxy cannot extract the names from the certificate");
      }

      let mut names = add_certificate.names;
      let fingerprint = calculate_fingerprint_from_der(&certified_key.cert[0].0);
      //info!("cert fingerprint: {:?}", fingerprint);

      let data = TlsData {
        cert:     certified_key,
        refcount: 0,
      };

      let fingerprint = CertFingerprint(fingerprint);
      self.certificates.insert(fingerprint.clone(), data);
      for name in names.drain(..) {
        self.domains.domain_insert(name.into_bytes(), fingerprint.clone());
      }

      Some(fingerprint)
    } else {
      None
    }
  }

  pub fn remove_certificate(&mut self, remove_certificate: RemoveCertificate) {
    let must_delete = self.certificates.get(&remove_certificate.fingerprint).map(|data| data.refcount == 0).unwrap_or(false);

    if let Some(data) = self.certificates.get(&remove_certificate.fingerprint) {
      let cert = &data.cert.cert[0];
      if remove_certificate.names.is_empty() {
        //FIXME: waiting for https://github.com/briansmith/webpki/pull/65 to merge to get the DNS names
        // create a untrusted::Input
        // let input = untrusted::Input::from(&certs[0].0);
        // create an EndEntityCert
        // let ee = webpki::EndEntityCert::from(input).unwrap()
        // get names
        // let dns_names = ee.list_dns_names()
        // names.extend(dns_names.drain(..).map(|name| name.to_String()));
        unimplemented!("the rustls proxy cannot extract the names from the certificate");
      }

      let mut names = remove_certificate.names;

      for name in names {
        self.domains.domain_remove(&name.into_bytes());
      }
    }

    if must_delete {
      self.certificates.remove(&remove_certificate.fingerprint);
    }
  }

  pub fn add_front(&mut self, fingerprint: &CertFingerprint) -> bool {
    if self.certificates.contains_key(fingerprint) {
      self.certificates.get_mut(fingerprint).map(|data| data.refcount += 1);
      true
    } else {
      false
    }
  }

  pub fn remove_front(&mut self, fingerprint: &CertFingerprint) {
    self.certificates.get_mut(fingerprint).map(|data| data.refcount -= 1);

    let must_delete = self.certificates.get(fingerprint).map(|data| data.refcount == 0).unwrap_or(false);
    if must_delete {
      self.certificates.remove(fingerprint);
    }
  }
}

pub struct CertificateResolverWrapper(Mutex<CertificateResolver>);

impl CertificateResolverWrapper {
  pub fn new() -> CertificateResolverWrapper {
    CertificateResolverWrapper(Mutex::new(CertificateResolver::new()))
  }

  pub fn add_certificate(&self, add_certificate: AddCertificate) -> Option<CertFingerprint> {
    if let Ok(ref mut resolver) = self.0.try_lock() {
      resolver.add_certificate(add_certificate)
    } else {
      None
    }
  }

  pub fn remove_certificate(&self, remove_certificate: RemoveCertificate) {
    if let Ok(ref mut resolver) = self.0.try_lock() {
      resolver.remove_certificate(remove_certificate)
    }

  }

  pub fn add_front(&self, fingerprint: &CertFingerprint) -> bool {
    if let Ok(ref mut resolver) = self.0.try_lock() {
      resolver.add_front(fingerprint)
    } else {
      false
    }
  }

  pub fn remove_front(&self, fingerprint: &CertFingerprint) {
    if let Ok(ref mut resolver) = self.0.try_lock() {
      resolver.remove_front(fingerprint)
    }
  }
}

impl ResolvesServerCert for CertificateResolverWrapper {
  fn resolve(
        &self,
        server_name: Option<webpki::DNSNameRef>,
        sigschemes: &[SignatureScheme]
    ) -> Option<CertifiedKey> {

    trace!("trying to resolve name: {:?} for signature scheme: {:?}", server_name, sigschemes);
    if let Ok(ref mut resolver) = self.0.try_lock() {
      if let Some(name) = server_name {
        //resolver.domains.print();
        let s: &str = name.into();
        if let Some(kv) = resolver.domains.domain_lookup(s.as_bytes()) {
           trace!("looking for certificate for {:?} with fingerprint {:?}", s, kv.1);
           return resolver.certificates.get(&kv.1).as_ref().map(|data| data.cert.clone());
        }
      }
    }

    None
  }
}

pub fn generate_certified_key(certificate_and_key: CertificateAndKey) -> Option<CertifiedKey> {
  let mut chain = Vec::new();

  let mut cert_reader = BufReader::new(certificate_and_key.certificate.as_bytes());
  let parsed_certs = pemfile::certs(&mut cert_reader);

  let fingerprint:Vec<u8> = Vec::new();
  if let Ok(certs) = parsed_certs {
    for cert in certs {
      chain.push(cert);
    }
  } else {
    return None;
  }

  for ref cert in certificate_and_key.certificate_chain.iter() {
    let mut chain_cert_reader = BufReader::new(cert.as_bytes());
    if let Ok(parsed_chain_certs) = pemfile::certs(&mut chain_cert_reader) {
      for cert in parsed_chain_certs {
        chain.push(cert);
      }
    }
  }

  let mut key_reader = BufReader::new(certificate_and_key.key.as_bytes());
  let parsed_key = pemfile::rsa_private_keys(&mut key_reader);

  if let Ok(keys) = parsed_key {
    if !keys.is_empty() {
      if let Ok(signing_key) = RSASigningKey::new(&keys[0]) {
        let certified = CertifiedKey::new(chain, Arc::new(Box::new(signing_key)));
        return Some(certified);
      }
    } else {
      let mut key_reader = BufReader::new(certificate_and_key.key.as_bytes());
      let parsed_key = pemfile::pkcs8_private_keys(&mut key_reader);
      if let Ok(keys) = parsed_key {
        if !keys.is_empty() {
          if let Ok(signing_key) = RSASigningKey::new(&keys[0]) {
            let certified = CertifiedKey::new(chain, Arc::new(Box::new(signing_key)));
            return Some(certified);
          }
        }
      }
    }
  } else {
    error!("could not parse private key: {:?}", parsed_key);
  }

  None
}
<commit_msg>more logs for SNI resolving failures in rustls<commit_after>use std::sync::{Arc,Mutex};
use std::collections::HashMap;
use std::io::BufReader;
use webpki;
use rustls::{ResolvesServerCert, SignatureScheme, PrivateKey};
use rustls::sign::{CertifiedKey, RSASigningKey};
use rustls::internal::pemfile;

use sozu_command::messages::{CertificateAndKey, CertFingerprint, AddCertificate, RemoveCertificate};
use sozu_command::certificate::calculate_fingerprint_from_der;

use network::trie::TrieNode;

struct TlsData {
  pub cert:     CertifiedKey,
  pub refcount: usize,
}

pub struct CertificateResolver {
  domains:      TrieNode<CertFingerprint>,
  certificates: HashMap<CertFingerprint, TlsData>,
}

impl CertificateResolver {
  pub fn new() -> CertificateResolver {
    CertificateResolver {
      domains:      TrieNode::root(),
      certificates: HashMap::new(),
    }
  }

  pub fn add_certificate(&mut self, add_certificate: AddCertificate) -> Option<CertFingerprint> {
    if let Some(certified_key) = generate_certified_key(add_certificate.certificate) {
      if add_certificate.names.is_empty() {
        //FIXME: waiting for https://github.com/briansmith/webpki/pull/65 to merge to get the DNS names
        // create a untrusted::Input
        // let input = untrusted::Input::from(&certs[0].0);
        // create an EndEntityCert
        // let ee = webpki::EndEntityCert::from(input).unwrap()
        // get names
        // let dns_names = ee.list_dns_names()
        // names.extend(dns_names.drain(..).map(|name| name.to_String()));
        unimplemented!("the rustls proxy cannot extract the names from the certificate");
      }

      let mut names = add_certificate.names;
      let fingerprint = calculate_fingerprint_from_der(&certified_key.cert[0].0);
      //info!("cert fingerprint: {:?}", fingerprint);

      let data = TlsData {
        cert:     certified_key,
        refcount: 0,
      };

      let fingerprint = CertFingerprint(fingerprint);
      self.certificates.insert(fingerprint.clone(), data);
      for name in names.drain(..) {
        self.domains.domain_insert(name.into_bytes(), fingerprint.clone());
      }

      Some(fingerprint)
    } else {
      None
    }
  }

  pub fn remove_certificate(&mut self, remove_certificate: RemoveCertificate) {
    let must_delete = self.certificates.get(&remove_certificate.fingerprint).map(|data| data.refcount == 0).unwrap_or(false);

    if let Some(data) = self.certificates.get(&remove_certificate.fingerprint) {
      let cert = &data.cert.cert[0];
      if remove_certificate.names.is_empty() {
        //FIXME: waiting for https://github.com/briansmith/webpki/pull/65 to merge to get the DNS names
        // create a untrusted::Input
        // let input = untrusted::Input::from(&certs[0].0);
        // create an EndEntityCert
        // let ee = webpki::EndEntityCert::from(input).unwrap()
        // get names
        // let dns_names = ee.list_dns_names()
        // names.extend(dns_names.drain(..).map(|name| name.to_String()));
        unimplemented!("the rustls proxy cannot extract the names from the certificate");
      }

      let mut names = remove_certificate.names;

      for name in names {
        self.domains.domain_remove(&name.into_bytes());
      }
    }

    if must_delete {
      self.certificates.remove(&remove_certificate.fingerprint);
    }
  }

  pub fn add_front(&mut self, fingerprint: &CertFingerprint) -> bool {
    if self.certificates.contains_key(fingerprint) {
      self.certificates.get_mut(fingerprint).map(|data| data.refcount += 1);
      true
    } else {
      false
    }
  }

  pub fn remove_front(&mut self, fingerprint: &CertFingerprint) {
    self.certificates.get_mut(fingerprint).map(|data| data.refcount -= 1);

    let must_delete = self.certificates.get(fingerprint).map(|data| data.refcount == 0).unwrap_or(false);
    if must_delete {
      self.certificates.remove(fingerprint);
    }
  }
}

pub struct CertificateResolverWrapper(Mutex<CertificateResolver>);

impl CertificateResolverWrapper {
  pub fn new() -> CertificateResolverWrapper {
    CertificateResolverWrapper(Mutex::new(CertificateResolver::new()))
  }

  pub fn add_certificate(&self, add_certificate: AddCertificate) -> Option<CertFingerprint> {
    if let Ok(ref mut resolver) = self.0.try_lock() {
      resolver.add_certificate(add_certificate)
    } else {
      None
    }
  }

  pub fn remove_certificate(&self, remove_certificate: RemoveCertificate) {
    if let Ok(ref mut resolver) = self.0.try_lock() {
      resolver.remove_certificate(remove_certificate)
    }

  }

  pub fn add_front(&self, fingerprint: &CertFingerprint) -> bool {
    if let Ok(ref mut resolver) = self.0.try_lock() {
      resolver.add_front(fingerprint)
    } else {
      false
    }
  }

  pub fn remove_front(&self, fingerprint: &CertFingerprint) {
    if let Ok(ref mut resolver) = self.0.try_lock() {
      resolver.remove_front(fingerprint)
    }
  }
}

impl ResolvesServerCert for CertificateResolverWrapper {
  fn resolve(
        &self,
        server_name: Option<webpki::DNSNameRef>,
        sigschemes: &[SignatureScheme]
    ) -> Option<CertifiedKey> {
    if server_name.is_none() {
      error!("cannot look up certificate: no SNI from session");
      return None;
    }
    let name: &str = server_name.unwrap().into();

    trace!("trying to resolve name: {:?} for signature scheme: {:?}", name, sigschemes);
    if let Ok(ref mut resolver) = self.0.try_lock() {
      //resolver.domains.print();
      if let Some(kv) = resolver.domains.domain_lookup(name.as_bytes()) {
         trace!("looking for certificate for {:?} with fingerprint {:?}", name, kv.1);
         return resolver.certificates.get(&kv.1).as_ref().map(|data| data.cert.clone());
      }
    }

    error!("could not look up a certificate for server name '{}'", name);
    None
  }
}

pub fn generate_certified_key(certificate_and_key: CertificateAndKey) -> Option<CertifiedKey> {
  let mut chain = Vec::new();

  let mut cert_reader = BufReader::new(certificate_and_key.certificate.as_bytes());
  let parsed_certs = pemfile::certs(&mut cert_reader);

  let fingerprint:Vec<u8> = Vec::new();
  if let Ok(certs) = parsed_certs {
    for cert in certs {
      chain.push(cert);
    }
  } else {
    return None;
  }

  for ref cert in certificate_and_key.certificate_chain.iter() {
    let mut chain_cert_reader = BufReader::new(cert.as_bytes());
    if let Ok(parsed_chain_certs) = pemfile::certs(&mut chain_cert_reader) {
      for cert in parsed_chain_certs {
        chain.push(cert);
      }
    }
  }

  let mut key_reader = BufReader::new(certificate_and_key.key.as_bytes());
  let parsed_key = pemfile::rsa_private_keys(&mut key_reader);

  if let Ok(keys) = parsed_key {
    if !keys.is_empty() {
      if let Ok(signing_key) = RSASigningKey::new(&keys[0]) {
        let certified = CertifiedKey::new(chain, Arc::new(Box::new(signing_key)));
        return Some(certified);
      }
    } else {
      let mut key_reader = BufReader::new(certificate_and_key.key.as_bytes());
      let parsed_key = pemfile::pkcs8_private_keys(&mut key_reader);
      if let Ok(keys) = parsed_key {
        if !keys.is_empty() {
          if let Ok(signing_key) = RSASigningKey::new(&keys[0]) {
            let certified = CertifiedKey::new(chain, Arc::new(Box::new(signing_key)));
            return Some(certified);
          }
        }
      }
    }
  } else {
    error!("could not parse private key: {:?}", parsed_key);
  }

  None
}
<|endoftext|>


<commit_before>//
// MSX CAS Packager
// Copyright (c) 2015 Alvaro Polo
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate docopt;
extern crate rustc_serialize;

mod args;
mod tape;

use std::fs::File;
use std::io::Write;

#[allow(dead_code)]
fn main() {
    let cmd = args::parse();
    match cmd {
        args::Command::Version => print_version(),
        args::Command::List(path) => list_files(&path[..]),
        args::Command::Extract(path) => extract_all(&path[..]),
    };
}

fn print_version() {
    println!("MSX CAS Packager (MCP) v0.1.0");
    println!("Copyright (C) 2015 Alvaro Polo");
    println!("");
    println!("This program is subject to the terms of the Mozilla Public License v2.0.");
    println!("");
}

macro_rules! open_tape {
    ($path: expr) => ({
        let mut tape_file = match File::open($path) {
            Ok(f) => f,
            Err(e) => {
                println!("Cannot open file '{}': {}", $path, e);
                return
            }
        };
        match tape::Tape::read(&mut tape_file) {
            Ok(f) => f,
            Err(e) => {
                println!("Cannot read file '{}': {}", $path, e);
                return
            }
        }
    })
}

macro_rules! create_file {
    ($path: expr) => ({
        match File::create($path) {
            Ok(f) => f,
            Err(e) => {
                println!("Cannot create file '{}': {}", $path, e);
                return
            }
        }
    })
}

macro_rules! write_file {
    ($name: expr, $file: expr, $data: expr) => ({
        match $file.write_all($data) {
            Ok(_) => {},
            Err(e) => {
                println!("Cannot write to file '{}': {}", $name, e);
            },
        };
    })
}

fn list_files(path: &str) {
    let tape = open_tape!(path);
    for file in tape.files() {
        match file {
            tape::File::Bin(name, begin, end, start, data) => {
                println!("bin    | {:6} | {:5} bytes | [0x{:x},0x{:x}]:0x{:x}",
                    name, data.len(), begin, end, start);
            },
            tape::File::Basic(name, data) => {
                println!("basic  | {:6} | {:5} bytes |", name, data.len());
            },
            tape::File::Ascii(name, data) => {
                let nbytes = data.iter().fold(0, |size, chunk| size + chunk.len());
                println!("ascii  | {:6} | {:5} bytes |", name, nbytes);
            },
            tape::File::Custom(data) => {
                println!("custom |        | {:5} bytes |", data.len());
            }
        };
    }
}

fn extract_all(path: &str) {
    let tape = open_tape!(path);
    let mut next_custom = 0;
    for file in tape.files() {
        let out_path = file.name()
            .map(|n| n.to_string())
            .unwrap_or_else(|| format!("custom.{:03}", { next_custom += 1; next_custom }));
        print!("Extracting {}... ", out_path);
        extract_file(&file, &out_path[..]);
        println!("Done");
    }
}

fn extract_file(file: &tape::File, out_path: &str) {
    let mut ofile = create_file!(out_path);
    match file {
        &tape::File::Bin(_, _, _, _, data) => {
            write_file!(out_path, ofile, data);
        },
        &tape::File::Basic(_, data) => {
            write_file!(out_path, ofile, data);
        },
        &tape::File::Ascii(_, ref chunks) => {
            for chunk in chunks {
                write_file!(out_path, ofile, chunk);
            }
        },
        &tape::File::Custom(ref data) => {
            write_file!(out_path, ofile, data);
        },
    }
}
<commit_msg>Remove trailing EOF bytes from ASCII files when exported<commit_after>//
// MSX CAS Packager
// Copyright (c) 2015 Alvaro Polo
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate docopt;
extern crate rustc_serialize;

mod args;
mod tape;

use std::fs::File;
use std::io::Write;

#[allow(dead_code)]
fn main() {
    let cmd = args::parse();
    match cmd {
        args::Command::Version => print_version(),
        args::Command::List(path) => list_files(&path[..]),
        args::Command::Extract(path) => extract_all(&path[..]),
    };
}

fn print_version() {
    println!("MSX CAS Packager (MCP) v0.1.0");
    println!("Copyright (C) 2015 Alvaro Polo");
    println!("");
    println!("This program is subject to the terms of the Mozilla Public License v2.0.");
    println!("");
}

macro_rules! open_tape {
    ($path: expr) => ({
        let mut tape_file = match File::open($path) {
            Ok(f) => f,
            Err(e) => {
                println!("Cannot open file '{}': {}", $path, e);
                return
            }
        };
        match tape::Tape::read(&mut tape_file) {
            Ok(f) => f,
            Err(e) => {
                println!("Cannot read file '{}': {}", $path, e);
                return
            }
        }
    })
}

macro_rules! create_file {
    ($path: expr) => ({
        match File::create($path) {
            Ok(f) => f,
            Err(e) => {
                println!("Cannot create file '{}': {}", $path, e);
                return
            }
        }
    })
}

macro_rules! write_file {
    ($name: expr, $file: expr, $data: expr) => ({
        match $file.write_all($data) {
            Ok(_) => {},
            Err(e) => {
                println!("Cannot write to file '{}': {}", $name, e);
            },
        };
    })
}

fn list_files(path: &str) {
    let tape = open_tape!(path);
    for file in tape.files() {
        match file {
            tape::File::Bin(name, begin, end, start, data) => {
                println!("bin    | {:6} | {:5} bytes | [0x{:x},0x{:x}]:0x{:x}",
                    name, data.len(), begin, end, start);
            },
            tape::File::Basic(name, data) => {
                println!("basic  | {:6} | {:5} bytes |", name, data.len());
            },
            tape::File::Ascii(name, data) => {
                let nbytes = data.iter().fold(0, |size, chunk| size + chunk.len());
                println!("ascii  | {:6} | {:5} bytes |", name, nbytes);
            },
            tape::File::Custom(data) => {
                println!("custom |        | {:5} bytes |", data.len());
            }
        };
    }
}

fn extract_all(path: &str) {
    let tape = open_tape!(path);
    let mut next_custom = 0;
    for file in tape.files() {
        let out_path = file.name()
            .map(|n| n.to_string())
            .unwrap_or_else(|| format!("custom.{:03}", { next_custom += 1; next_custom }));
        print!("Extracting {}... ", out_path);
        extract_file(&file, &out_path[..]);
        println!("Done");
    }
}

fn extract_file(file: &tape::File, out_path: &str) {
    let mut ofile = create_file!(out_path);
    match file {
        &tape::File::Bin(_, _, _, _, data) => {
            write_file!(out_path, ofile, data);
        },
        &tape::File::Basic(_, data) => {
            write_file!(out_path, ofile, data);
        },
        &tape::File::Ascii(_, ref chunks) => {
            for chunk in chunks {
                let last = chunk.iter().position(|b| *b == 0x1a).unwrap_or(chunk.len());
                write_file!(out_path, ofile, &chunk[..last]);
            }
        },
        &tape::File::Custom(ref data) => {
            write_file!(out_path, ofile, data);
        },
    }
}
<|endoftext|>


<commit_before>#![feature(globs)]

extern crate moon;
use std::io;

static color_normal: &'static str = "\x1B[0m";
static color_red: &'static str = "\x1B[31m";
static color_cyan: &'static str = "\x1B[36m";
static color_magenta: &'static str = "\x1B[35m";
static color_bold: &'static str = "\x1B[1m";

fn main() {
    let mut running = true;

    println!("moon alpha.");

    while running {
        print!("{:s}$moon{:s}{:s}>{:s} ", color_magenta, color_normal,
            color_bold, color_normal);
        for line in io::stdin().lines() {
            match line {
                Ok(line) => {
                    if line == "exit\n".to_string() {
                        running = false;
                        break;
                    }

                    print!("{}", line);
                },
                _ => {}
            }

            break;
        }
    }
}
<commit_msg>Adjusted the repl<commit_after>#![feature(globs)]
#![allow(dead_code)]

extern crate moon;
use std::io;

static color_normal: &'static str = "\x1B[0m";
static color_red: &'static str = "\x1B[31m";
static color_cyan: &'static str = "\x1B[36m";
static color_magenta: &'static str = "\x1B[35m";
static color_bold: &'static str = "\x1B[1m";

fn main() {
    let mut running = true;

    println!("{:s}Moon{:s} alpha. v0.1.0 \n", color_cyan, color_normal);

    while running {
        print!("{:s}$moon{:s}{:s}>{:s} ", color_magenta, color_normal,
            color_bold, color_normal);
        for line in io::stdin().lines() {
            match line {
                Ok(line) => {
                    if line == "exit\n".to_string() {
                        running = false;
                        break;
                    }

                    print!("{}", line);
                },
                _ => {}
            }

            break;
        }
    }
}
<|endoftext|>