<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 services::*;
use selector::*;
use values::{Value, Range, TypeError};
use util::{Exactly, Id};

/// An error that took place while communicating with either an adapter or the mechanism that
/// handles registeration of adapters.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum AdapterError {
    DuplicateGetter(Id<Getter>),
    NoSuchGetter(Id<Getter>),
    GetterDoesNotSupportPolling(Id<Getter>),
    GetterDoesNotSupportWatching(Id<Getter>),
    GetterRequiresThresholdForWatching(Id<Getter>),

    DuplicateSetter(Id<Setter>),
    NoSuchSetter(Id<Setter>),

    DuplicateService(Id<ServiceId>),
    NoSuchService(Id<ServiceId>),
    TypeError(TypeError),

    DuplicateAdapter(Id<AdapterId>),
    ConflictingAdapter(Id<AdapterId>, Id<AdapterId>),
    NoSuchAdapter(Id<AdapterId>),
    InvalidValue
}

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

    /// There is no such getter channel connected to the Foxbox, even indirectly.
    NoSuchGetter(Id<Getter>),

    /// There is no such setter channel connected to the Foxbox, even indirectly.
    NoSuchSetter(Id<Setter>),

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

    /// An error arose when talking to the adapter.
    AdapterError(AdapterError),
}

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

        /// 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.
    GetterRemoved(Id<Getter>),

    /// 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.
    GetterAdded(Id<Getter>),

    /// One of the channels encountered an error during initialization.
    /// This channel will not be watched, but other channels will remain
    /// watched.
    InitializationError {
        channel: Id<Getter>,
        error: AdapterError
    },
}

/// A bunch of results coming from different sources.
pub type ResultMap<K, T, E> = Vec<(K, Result<T, E>)>;

/// A bunch of instructions, going to different targets.
pub type TargetMap<K, T> = Vec<(Vec<K>, T)>;

/// A handle to the public API.
pub trait API {
    /// Get the metadata on services matching some conditions.
    ///
    /// A call to `API::get_services(vec![req1, req2, ...])` will return
    /// the metadata on all services matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to a `Vec<ServiceSelector>`. See
    /// the implementation of `ServiceSelector` for details.
    ///
    /// ### Example
    ///
    /// Selector all doors in the entrance (tags `door`, `entrance`)
    /// that support setter channel `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "getters": [
    ///     {
    ///       "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 `Service`. See the implementation
    /// of `Service` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-service-id",
    ///   "getters": [],
    ///   "setters": [
    ///     "tags": [...],
    ///     "id": "some-channel-id",
    ///     "service": "some-service-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Setter":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_services(&self, &[ServiceSelector]) -> Vec<Service>;

    /// Label a set of services with a set of tags.
    ///
    /// A call to `API::put_service_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 selectors.
    ///
    /// Some of the services may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/tag`
    ///
    /// ## Getters
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<ServiceSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn add_service_tags(&self, selectors: &[ServiceSelector], tags: &[Id<TagId>]) -> usize;

    /// Remove a set of tags from a set of services.
    ///
    /// A call to `API::delete_service_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 selectors.
    ///
    /// Some of the services may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/services/tag`
    ///
    /// ## Getters
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<ServiceSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## 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 remove_service_tags(&self, selectors: &[ServiceSelector], tags: &[Id<TagId>]) -> usize;

    /// Get a list of getters matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels`
    fn get_getter_channels(&self, selectors: &[GetterSelector]) -> Vec<Channel<Getter>>;
    fn get_setter_channels(&self, selectors: &[SetterSelector]) -> Vec<Channel<Setter>>;

    /// Label a set of channels with a set of tags.
    ///
    /// A call to `API::put_{getter, setter}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the channels matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels 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 channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/tag`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## 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 add_getter_tags(&self, selectors: &[GetterSelector], tags: &[Id<TagId>]) -> usize;
    fn add_setter_tags(&self, selectors: &[SetterSelector], tags: &[Id<TagId>]) -> usize;

    /// Remove a set of tags from a set of channels.
    ///
    /// A call to `API::delete_{getter, setter}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the channels matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels 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 channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/channels/tag`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## 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 remove_getter_tags(&self, selectors: &[GetterSelector], tags: &[Id<TagId>]) -> usize;
    fn remove_setter_tags(&self, selectors: &[SetterSelector], tags: &[Id<TagId>]) -> usize;

    /// Read the latest value from a set of channels
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels/value`
    fn fetch_values(&self, &[GetterSelector]) -> ResultMap<Id<Getter>, Option<Value>, Error>;

    /// Send a bunch of values to a set of channels
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/value`
    fn send_values(&self, TargetMap<SetterSelector, Value>) -> ResultMap<Id<Setter>, (), Error>;

    /// Watch for changes from channels.
    ///
    /// This method registers a closure to watch over events on a set of channels. Argument `watch`
    /// specifies which channels to watch and which events are of interest.
    ///
    /// - If argument `Exactly<Range>` is `Exactly::Exactly(range)`, the watch is interested in
    /// values coming from these channels, if they fall within `range`. This is the most common
    /// case. In this case, `on_event` receives `WatcherEvent::GetterAdded`,
    /// `WatcherEvent::GetterRemoved` and `WatcherEvent::Value`, whenever a new value is available
    /// in the range. Values that do not have the same type as `range` are dropped silently.
    ///
    /// - If argument `Exactly<Range>` is `Exactly::Never`, the watch is not interested in the
    /// values coming from these channels, only in connection/disconnection events. Argument
    /// `on_event` receives `WatchEvent::GetterAdded` and `WatchEvent::GetterRemoved`.
    ///
    /// - If the `Exactly<Range>` argument is `Exactly::Always`, the watch is interested in
    /// receiving *every single value coming from the channels*. This is very rarely a good idea.
    /// Many devices may reject such requests.
    ///
    /// The watcher is disconnected once the `WatchGuard` returned by this method is dropped.
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/channels/watch`
    fn register_channel_watch(&self, watch: TargetMap<GetterSelector, Exactly<Range>>,
            on_event: Box<Fn(WatchEvent) + Send>) -> Self::WatchGuard;

    /// A value that causes a disconnection once it is dropped.
    type WatchGuard;
}
<commit_msg>WatchEvent now offers EnterRange and ExitRange<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 services::*;
use selector::*;
use values::{Value, Range, TypeError};
use util::{Exactly, Id};

/// An error that took place while communicating with either an adapter or the mechanism that
/// handles registeration of adapters.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum AdapterError {
    DuplicateGetter(Id<Getter>),
    NoSuchGetter(Id<Getter>),
    GetterDoesNotSupportPolling(Id<Getter>),
    GetterDoesNotSupportWatching(Id<Getter>),
    GetterRequiresThresholdForWatching(Id<Getter>),

    DuplicateSetter(Id<Setter>),
    NoSuchSetter(Id<Setter>),

    DuplicateService(Id<ServiceId>),
    NoSuchService(Id<ServiceId>),
    TypeError(TypeError),

    DuplicateAdapter(Id<AdapterId>),
    ConflictingAdapter(Id<AdapterId>, Id<AdapterId>),
    NoSuchAdapter(Id<AdapterId>),
    InvalidValue
}

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

    /// There is no such getter channel connected to the Foxbox, even indirectly.
    NoSuchGetter(Id<Getter>),

    /// There is no such setter channel connected to the Foxbox, even indirectly.
    NoSuchSetter(Id<Setter>),

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

    /// An error arose when talking to the adapter.
    AdapterError(AdapterError),
}

/// An event during watching.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum WatchEvent {
    /// If a range was specified when we registered for watching, `EnterRange` is fired whenever
    /// we enter this range. If `Always` was specified, `EnterRange` is fired whenever a new value
    /// is available. Otherwise, never fired.
    EnterRange {
        /// The channel that sent the value.
        from: Id<Getter>,

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

    /// If a range was specified when we registered for watching, `ExitRange` is fired whenever
    /// we exit this range. Otherwise, never fired.
    ExitRange {
        /// The channel that sent the value.
        from: Id<Getter>,

        /// 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.
    GetterRemoved(Id<Getter>),

    /// 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.
    GetterAdded(Id<Getter>),

    /// One of the channels encountered an error during initialization.
    /// This channel will not be watched, but other channels will remain
    /// watched.
    InitializationError {
        channel: Id<Getter>,
        error: AdapterError
    },
}

/// A bunch of results coming from different sources.
pub type ResultMap<K, T, E> = Vec<(K, Result<T, E>)>;

/// A bunch of instructions, going to different targets.
pub type TargetMap<K, T> = Vec<(Vec<K>, T)>;

/// A handle to the public API.
pub trait API {
    /// Get the metadata on services matching some conditions.
    ///
    /// A call to `API::get_services(vec![req1, req2, ...])` will return
    /// the metadata on all services matching _either_ `req1` or `req2`
    /// or ...
    ///
    /// # REST API
    ///
    /// `GET /api/v1/services`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to a `Vec<ServiceSelector>`. See
    /// the implementation of `ServiceSelector` for details.
    ///
    /// ### Example
    ///
    /// Selector all doors in the entrance (tags `door`, `entrance`)
    /// that support setter channel `OpenClose`
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door"],
    ///   "getters": [
    ///     {
    ///       "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 `Service`. See the implementation
    /// of `Service` for details.
    ///
    /// ### Example
    ///
    /// ```json
    /// [{
    ///   "tags": ["entrance", "door", "somevendor"],
    ///   "id: "some-service-id",
    ///   "getters": [],
    ///   "setters": [
    ///     "tags": [...],
    ///     "id": "some-channel-id",
    ///     "service": "some-service-id",
    ///     "last_seen": "some-date",
    ///     "mechanism": {
    ///       "Setter":  {
    ///         "kind": {
    ///           "OnOff": []
    ///         },
    ///         "push": [5000],
    ///         "updated": "some-date",
    ///       }
    ///     }
    ///   ]
    /// }]
    /// ```
    fn get_services(&self, &[ServiceSelector]) -> Vec<Service>;

    /// Label a set of services with a set of tags.
    ///
    /// A call to `API::put_service_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 selectors.
    ///
    /// Some of the services may already be labelled with `tag1`, or
    /// `tag2`, ... They will not change state. They are counted in
    /// the resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/services/tag`
    ///
    /// ## Getters
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<ServiceSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## Errors
    ///
    /// In case of syntax error, Error 400, accompanied with a
    /// somewhat human-readable JSON string detailing the error.
    ///
    /// ## Success
    ///
    /// A JSON string representing a number.
    fn add_service_tags(&self, selectors: &[ServiceSelector], tags: &[Id<TagId>]) -> usize;

    /// Remove a set of tags from a set of services.
    ///
    /// A call to `API::delete_service_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 selectors.
    ///
    /// Some of the services may not be labelled with `tag1`, or `tag2`,
    /// ... They will not change state. They are counted in the
    /// resulting `usize` nevertheless.
    ///
    /// Note that this call is _not live_. In other words, if services
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/services/tag`
    ///
    /// ## Getters
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<ServiceSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## 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 remove_service_tags(&self, selectors: &[ServiceSelector], tags: &[Id<TagId>]) -> usize;

    /// Get a list of getters matching some conditions
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels`
    fn get_getter_channels(&self, selectors: &[GetterSelector]) -> Vec<Channel<Getter>>;
    fn get_setter_channels(&self, selectors: &[SetterSelector]) -> Vec<Channel<Setter>>;

    /// Label a set of channels with a set of tags.
    ///
    /// A call to `API::put_{getter, setter}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will label all the channels matching _either_ `req1` or
    /// `req2` or ... with `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels 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 channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/tag`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## 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 add_getter_tags(&self, selectors: &[GetterSelector], tags: &[Id<TagId>]) -> usize;
    fn add_setter_tags(&self, selectors: &[SetterSelector], tags: &[Id<TagId>]) -> usize;

    /// Remove a set of tags from a set of channels.
    ///
    /// A call to `API::delete_{getter, setter}_tag(vec![req1, req2, ...], vec![tag1,
    /// ...])` will remove from all the channels matching _either_ `req1` or
    /// `req2` or ... all of the tags `tag1`, ... and return the number of channels
    /// matching any of the selectors.
    ///
    /// Some of the channels 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 channels
    /// are added after the call, they will not be affected.
    ///
    /// # REST API
    ///
    /// `DELETE /api/v1/channels/tag`
    ///
    /// ## Requests
    ///
    /// Any JSON that can be deserialized to
    ///
    /// ```ignore
    /// {
    ///   set: Vec<GetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    /// or
    /// ```ignore
    /// {
    ///   set: Vec<SetterSelector>,
    ///   tags: Vec<Id<TagId>>,
    /// }
    /// ```
    ///
    /// ## 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 remove_getter_tags(&self, selectors: &[GetterSelector], tags: &[Id<TagId>]) -> usize;
    fn remove_setter_tags(&self, selectors: &[SetterSelector], tags: &[Id<TagId>]) -> usize;

    /// Read the latest value from a set of channels
    ///
    /// # REST API
    ///
    /// `GET /api/v1/channels/value`
    fn fetch_values(&self, &[GetterSelector]) -> ResultMap<Id<Getter>, Option<Value>, Error>;

    /// Send a bunch of values to a set of channels
    ///
    /// # REST API
    ///
    /// `POST /api/v1/channels/value`
    fn send_values(&self, TargetMap<SetterSelector, Value>) -> ResultMap<Id<Setter>, (), Error>;

    /// Watch for changes from channels.
    ///
    /// This method registers a closure to watch over events on a set of channels. Argument `watch`
    /// specifies which channels to watch and which events are of interest.
    ///
    /// - If argument `Exactly<Range>` is `Exactly::Exactly(range)`, the watch is interested in
    /// values coming from these channels, if they fall within `range`. This is the most common
    /// case. In this case, `on_event` receives `WatcherEvent::GetterAdded`,
    /// `WatcherEvent::GetterRemoved` and `WatcherEvent::Value`, whenever a new value is available
    /// in the range. Values that do not have the same type as `range` are dropped silently.
    ///
    /// - If argument `Exactly<Range>` is `Exactly::Never`, the watch is not interested in the
    /// values coming from these channels, only in connection/disconnection events. Argument
    /// `on_event` receives `WatchEvent::GetterAdded` and `WatchEvent::GetterRemoved`.
    ///
    /// - If the `Exactly<Range>` argument is `Exactly::Always`, the watch is interested in
    /// receiving *every single value coming from the channels*. This is very rarely a good idea.
    /// Many devices may reject such requests.
    ///
    /// The watcher is disconnected once the `WatchGuard` returned by this method is dropped.
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/channels/watch`
    fn register_channel_watch(&self, watch: TargetMap<GetterSelector, Exactly<Range>>,
            on_event: Box<Fn(WatchEvent) + Send>) -> Self::WatchGuard;

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


<commit_before>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}
struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    pub fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        let result = Exec::cmd("sshfs")
            .arg("-o")
            .arg("allow_other")
            .arg(RSYNC_DOT_NET_REMOTE_DIR)
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "sshfs failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("fusermount")
            .arg("-u")
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "fusermount -u failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let result = Exec::cmd("sudo")
            .arg("losetup")
            .arg("-l")
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to list loop devices: ``{}''",
                result.stderr_str()
            )));
        }

        for line in result.stdout_str().lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("loop-setup")
            .arg("-f")
            .arg(&self.backing)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to attach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        let result = Exec::cmd("udisksctl")
            .arg("loop-delete")
            .arg("-b")
            .arg(&attached_loop)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to detach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

fn main_impl() -> Result<(), AuricError> {
    println!("Hello there!");
    Ok(())
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<commit_msg>apricot-auric: Begin implementing LuksManager<commit_after>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}
struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    pub fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        let result = Exec::cmd("sshfs")
            .arg("-o")
            .arg("allow_other")
            .arg(RSYNC_DOT_NET_REMOTE_DIR)
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "sshfs failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("fusermount")
            .arg("-u")
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "fusermount -u failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let result = Exec::cmd("sudo")
            .arg("losetup")
            .arg("-l")
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to list loop devices: ``{}''",
                result.stderr_str()
            )));
        }

        for line in result.stdout_str().lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("loop-setup")
            .arg("-f")
            .arg(&self.backing)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to attach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        let result = Exec::cmd("udisksctl")
            .arg("loop-delete")
            .arg("-b")
            .arg(&attached_loop)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to detach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    // `locked_device` is necessary as an external argument because no
    // device mapping exists in a pre-unlock world, so we need to target
    // the loop device directly.
    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unlock")
            .arg("-b")
            .arg(locked_device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unlock {}: ``{}''",
                locked_device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // No extra argument is needed here (c.f. `self::unlock()`) because
    // we can go straight after the well-known device available in
    // /dev/disk/by-label.
    pub fn lock(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("lock")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to lock {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

fn main_impl() -> Result<(), AuricError> {
    println!("Hello there!");
    Ok(())
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<|endoftext|>


<commit_before>use crate::attr;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{
    Data, DataEnum, DataStruct, DeriveInput, Error, Field, Fields, Ident, Index, Member, Result,
    Type,
};

pub fn derive(input: &DeriveInput) -> Result<TokenStream> {
    match &input.data {
        Data::Struct(data) => impl_struct(input, data),
        Data::Enum(data) => impl_enum(input, data),
        Data::Union(_) => Err(Error::new_spanned(
            input,
            "union as errors are not supported",
        )),
    }
}

fn impl_struct(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let source = source_member(&data.fields)?;
    let source_method = source.map(|source| {
        let member = quote_spanned!(source.span()=> self.#source);
        quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                std::option::Option::Some(#member.as_dyn_error())
            }
        }
    });

    let backtrace = backtrace_member(&data.fields)?;
    let backtrace_method = backtrace.map(|backtrace| {
        quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                std::option::Option::Some(&self.#backtrace)
            }
        }
    });

    let struct_attrs = attr::get(&input.attrs)?;
    let display = struct_attrs.display.map(|display| {
        let pat = match &data.fields {
            Fields::Named(fields) => {
                let var = fields.named.iter().map(|field| &field.ident);
                quote!(Self { #(#var),* })
            }
            Fields::Unnamed(fields) => {
                let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
                quote!(Self(#(#var),*))
            }
            Fields::Unit => quote!(_),
        };
        quote! {
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #[allow(unused_variables)]
                    let #pat = self;
                    #display
                }
            }
        }
    });

    Ok(quote! {
        impl #impl_generics std::error::Error for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display
    })
}

fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let variant_fields: Vec<&Fields> = data
        .variants
        .iter()
        .map(|variant| &variant.fields)
        .collect();

    let sources: Vec<Option<Member>> = variant_fields
        .iter()
        .cloned()
        .map(source_member)
        .collect::<Result<_>>()?;

    let backtraces: Vec<Option<Member>> = variant_fields
        .iter()
        .cloned()
        .map(backtrace_member)
        .collect::<Result<_>>()?;

    let source_method = if sources.iter().any(Option::is_some) {
        let arms = data.variants.iter().zip(sources).map(|(variant, source)| {
            let ident = &variant.ident;
            match source {
                Some(source) => quote! {
                    #ty::#ident {#source: source, ..} => std::option::Option::Some(source.as_dyn_error()),
                },
                None => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
        Some(quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let backtrace_method = if backtraces.iter().any(Option::is_some) {
        let arms = data.variants.iter().zip(backtraces).map(|(variant, backtrace)| {
            let ident = &variant.ident;
            match backtrace {
                Some(backtrace) => quote! {
                    #ty::#ident {#backtrace: backtrace, ..} => std::option::Option::Some(backtrace),
                },
                None => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
        Some(quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let variant_attrs = data
        .variants
        .iter()
        .map(|variant| attr::get(&variant.attrs))
        .collect::<Result<Vec<_>>>()?;
    let display = if variant_attrs.iter().any(|attrs| attrs.display.is_some()) {
        let arms = data
            .variants
            .iter()
            .zip(variant_attrs)
            .map(|(variant, attrs)| {
                let display = attrs.display.ok_or_else(|| {
                    Error::new_spanned(variant, "missing #[error(\"...\")] display attribute")
                })?;
                let ident = &variant.ident;
                Ok(match &variant.fields {
                    Fields::Named(fields) => {
                        let var = fields.named.iter().map(|field| &field.ident);
                        quote!(#ty::#ident { #(#var),* } => #display)
                    }
                    Fields::Unnamed(fields) => {
                        let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i));
                        quote!(#ty::#ident(#(#var),*) => #display)
                    }
                    Fields::Unit => quote!(#ty::#ident => #display),
                })
            })
            .collect::<Result<Vec<_>>>()?;
        Some(quote! {
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #[allow(unused_variables)]
                    match self {
                        #(#arms,)*
                    }
                }
            }
        })
    } else {
        None
    };

    Ok(quote! {
        impl #impl_generics std::error::Error for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display
    })
}

fn source_member<'a>(fields: impl IntoIterator<Item = &'a Field>) -> Result<Option<Member>> {
    for (i, field) in fields.into_iter().enumerate() {
        let attrs = attr::get(&field.attrs)?;
        if attrs.source {
            return Ok(Some(member(i, &field.ident)));
        }
    }
    Ok(None)
}

fn backtrace_member<'a>(fields: impl IntoIterator<Item = &'a Field>) -> Result<Option<Member>> {
    for (i, field) in fields.into_iter().enumerate() {
        if type_is_backtrace(&field.ty) {
            return Ok(Some(member(i, &field.ident)));
        }
    }
    Ok(None)
}

fn type_is_backtrace(ty: &Type) -> bool {
    let path = match ty {
        Type::Path(ty) => &ty.path,
        _ => return false,
    };

    let last = path.segments.last().unwrap();
    last.ident == "Backtrace" && last.arguments.is_empty()
}

fn member(i: usize, ident: &Option<Ident>) -> Member {
    match ident {
        Some(ident) => Member::Named(ident.clone()),
        None => Member::Unnamed(Index::from(i)),
    }
}
<commit_msg>Switch expansion to use new ast<commit_after>use crate::ast::{Enum, Field, Input, Struct};
use crate::attr::Attrs;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::spanned::Spanned;
use syn::{DeriveInput, Error, Member, Result, Type};

pub fn derive(node: &DeriveInput) -> Result<TokenStream> {
    let input = Input::from_syn(node)?;
    match input {
        Input::Struct(input) => impl_struct(input),
        Input::Enum(input) => impl_enum(input),
    }
}

fn impl_struct(input: Struct) -> Result<TokenStream> {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let source = source_member(&input.fields);
    let source_method = source.map(|source| {
        let member = quote_spanned!(source.span()=> self.#source);
        quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                std::option::Option::Some(#member.as_dyn_error())
            }
        }
    });

    let backtrace = backtrace_member(&input.fields);
    let backtrace_method = backtrace.map(|backtrace| {
        quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                std::option::Option::Some(&self.#backtrace)
            }
        }
    });

    let display = input.attrs.display.as_ref().map(|display| {
        let pat = fields_pat(&input.fields);
        quote! {
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #[allow(unused_variables)]
                    let Self #pat = self;
                    #display
                }
            }
        }
    });

    Ok(quote! {
        impl #impl_generics std::error::Error for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display
    })
}

fn impl_enum(input: Enum) -> Result<TokenStream> {
    let ty = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let sources: Vec<Option<&Member>> = input
        .variants
        .iter()
        .map(|variant| source_member(&variant.fields))
        .collect();

    let backtraces: Vec<Option<&Member>> = input
        .variants
        .iter()
        .map(|variant| backtrace_member(&variant.fields))
        .collect();

    let source_method = if sources.iter().any(Option::is_some) {
        let arms = input.variants.iter().zip(sources).map(|(variant, source)| {
            let ident = &variant.ident;
            match source {
                Some(source) => quote! {
                    #ty::#ident {#source: source, ..} => std::option::Option::Some(source.as_dyn_error()),
                },
                None => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
        Some(quote! {
            fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
                use thiserror::private::AsDynError;
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let backtrace_method = if backtraces.iter().any(Option::is_some) {
        let arms = input.variants.iter().zip(backtraces).map(|(variant, backtrace)| {
            let ident = &variant.ident;
            match backtrace {
                Some(backtrace) => quote! {
                    #ty::#ident {#backtrace: backtrace, ..} => std::option::Option::Some(backtrace),
                },
                None => quote! {
                    #ty::#ident {..} => std::option::Option::None,
                },
            }
        });
        Some(quote! {
            fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
                match self {
                    #(#arms)*
                }
            }
        })
    } else {
        None
    };

    let variant_attrs: Vec<&Attrs> = input
        .variants
        .iter()
        .map(|variant| &variant.attrs)
        .collect();
    let display = if variant_attrs.iter().any(|attrs| attrs.display.is_some()) {
        let arms = input
            .variants
            .iter()
            .zip(variant_attrs)
            .map(|(variant, attrs)| {
                let display = attrs.display.as_ref().ok_or_else(|| {
                    Error::new_spanned(
                        &variant.ident,
                        "missing #[error(\"...\")] display attribute",
                    )
                })?;
                let ident = &variant.ident;
                let pat = fields_pat(&variant.fields);
                Ok(quote! {
                    #ty::#ident #pat => #display
                })
            })
            .collect::<Result<Vec<_>>>()?;
        Some(quote! {
            impl #impl_generics std::fmt::Display for #ty #ty_generics #where_clause {
                fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    #[allow(unused_variables)]
                    match self {
                        #(#arms,)*
                    }
                }
            }
        })
    } else {
        None
    };

    Ok(quote! {
        impl #impl_generics std::error::Error for #ty #ty_generics #where_clause {
            #source_method
            #backtrace_method
        }
        #display
    })
}

fn source_member<'a>(fields: &'a [Field]) -> Option<&'a Member> {
    for field in fields {
        if field.attrs.source {
            return Some(&field.member);
        }
    }
    None
}

fn backtrace_member<'a>(fields: &'a [Field]) -> Option<&'a Member> {
    for field in fields {
        if type_is_backtrace(&field.ty) {
            return Some(&field.member);
        }
    }
    None
}

fn type_is_backtrace(ty: &Type) -> bool {
    let path = match ty {
        Type::Path(ty) => &ty.path,
        _ => return false,
    };

    let last = path.segments.last().unwrap();
    last.ident == "Backtrace" && last.arguments.is_empty()
}

fn fields_pat(fields: &[Field]) -> TokenStream {
    let mut members = fields.iter().map(|field| &field.member).peekable();
    match members.peek() {
        Some(Member::Named(_)) => quote!({ #(#members),* }),
        Some(Member::Unnamed(_)) => {
            let vars = members.map(|member| match member {
                Member::Unnamed(member) => format_ident!("_{}", member.index),
                Member::Named(_) => unreachable!(),
            });
            quote!((#(#vars),*))
        }
        None => quote!({}),
    }
}
<|endoftext|>


<commit_before>// xfail-stage0
// xfail-stage1
// tests that the pred in a claim isn't actually eval'd
use std;
import std::str::*;
import std::uint::*;

pred fails(uint a) -> bool {
  fail;
}

fn main() {
  let uint a = 5u;
  let uint b = 4u;
  claim fails(b);
}<commit_msg>remove an unused variable<commit_after>// xfail-stage0
// xfail-stage1
// tests that the pred in a claim isn't actually eval'd
use std;
import std::str::*;
import std::uint::*;

pred fails(uint a) -> bool {
  fail;
}

fn main() {
  let uint b = 4u;
  claim fails(b);
}<|endoftext|>


<commit_before>use std::collections::HashMap;

pub struct Translator
{
	rules: HashMap<(char, char), char>,
}

impl Translator
{
	pub fn new() -> Translator
	{
		Translator
		{
			rules: HashMap::new(),
		}
	}

	pub fn default() -> Translator
	{
		Translator
		{
			rules: get_default_rules(),
		}
	}

	fn translate_char(&self, current : char, previous : char) -> char
	{
		match self.rules.get(&(current, previous))
		{
			Some(result) => *result,
			None => match self.rules.get(&(current, ' '))
			{
				Some(result) => *result,
				None => current,
			}
		}
	}

	pub fn translate(&self, text : &str) -> String
	{
		let mut result = String::with_capacity(text.len());

		let mut previous = ' ';
		for c in text.chars()
		{
			previous = self.translate_char(c, previous);
			result.push(previous);
		}

		result
	}
}

fn get_default_rules() -> HashMap<(char, char), char>
{
	let mut result = HashMap::with_capacity(50);

	result.insert(('a', ' '), 'a');

	result
}
<commit_msg>Core refactoring<commit_after>use std::collections::HashMap;

pub struct Translator
{
	rules: HashMap<(char, char), char>,
}

impl Translator
{
	pub fn new() -> Translator
	{
		Translator
		{
			rules: get_default_rules(),
		}
	}

	fn translate_char(&self, current : char, previous : char) -> char
	{
		match self.rules.get(&(current, previous))
		{
			Some(result) => *result,
			None => match self.rules.get(&(current, ' '))
			{
				Some(result) => *result,
				None => current,
			}
		}
	}

	pub fn translate(&self, text : &str) -> String
	{
		let mut result = String::with_capacity(text.len());

		let mut previous = ' ';
		for c in text.chars()
		{
			previous = self.translate_char(c, previous);
			result.push(previous);
		}

		result
	}
}

fn get_default_rules() -> HashMap<(char, char), char>
{
	let mut result = HashMap::with_capacity(50);

	result.insert(('a', ' '), 'a');

	result
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An implementation of set for items that implement `StorageKey` trait.
use std::marker::PhantomData;

use super::{BaseIndex, BaseIndexIter, Snapshot, Fork, StorageKey};

/// A set of items that implement `StorageKey` trait.
///
/// `KeySetIndex` implements a set, storing the elements as keys with empty values.
/// `KeySetIndex` requires that the elements implement the [`StorageKey`] trait.
/// [`StorageKey`]: ../trait.StorageKey.html
#[derive(Debug)]
pub struct KeySetIndex<T, K> {
    base: BaseIndex<T>,
    _k: PhantomData<K>,
}

/// An iterator over the items of a `KeySetIndex`.
///
/// This struct is created by the [`iter`] or
/// [`iter_from`] methods on [`KeySetIndex`]. See its documentation for more.
///
/// [`iter`]: struct.KeySetIndex.html#method.iter
/// [`iter_from`]: struct.KeySetIndex.html#method.iter_from
/// [`KeySetIndex`]: struct.KeySetIndex.html
#[derive(Debug)]
pub struct KeySetIndexIter<'a, K> {
    base_iter: BaseIndexIter<'a, K, ()>,
}

impl<T, K> KeySetIndex<T, K> {
    /// Creates a new index representation based on the common prefix of its keys and storage view.
    ///
    /// Storage view can be specified as [`&Snapshot`] or [`&mut Fork`]. In the first case only
    /// immutable methods are available. In the second case both immutable and mutable methods are
    /// available.
    /// [`&Snapshot`]: ../trait.Snapshot.html
    /// [`&mut Fork`]: ../struct.Fork.html
    pub fn new(prefix: Vec<u8>, view: T) -> Self {
        KeySetIndex {
            base: BaseIndex::new(prefix, view),
            _k: PhantomData,
        }
    }
}

impl<T, K> KeySetIndex<T, K>
where
    T: AsRef<Snapshot>,
    K: StorageKey,
{
    /// Returns `true` if the set contains a value.
    pub fn contains(&self, item: &K) -> bool {
        self.base.contains(item)
    }

    /// An iterator visiting all elements in ascending order. The iterator element type is K.
    pub fn iter(&self) -> KeySetIndexIter<K> {
        KeySetIndexIter { base_iter: self.base.iter(&()) }
    }

    /// An iterator visiting all elements in arbitrary order starting from the specified value.
    /// The iterator element type is K.
    pub fn iter_from(&self, from: &K) -> KeySetIndexIter<K> {
        KeySetIndexIter { base_iter: self.base.iter_from(&(), from) }
    }
}

impl<'a, K> KeySetIndex<&'a mut Fork, K>
where
    K: StorageKey,
{
    /// Adds a value to the set.
    pub fn insert(&mut self, item: K) {
        self.base.put(&item, ())
    }

    /// Removes a value from the set.
    pub fn remove(&mut self, item: &K) {
        self.base.remove(item)
    }

    /// Clears the set, removing all values.
    ///
    /// # Notes
    /// Currently this method is not optimized to delete large set of data. During the execution of
    /// this method the amount of allocated memory is linearly dependent on the number of elements
    /// in the index.
    pub fn clear(&mut self) {
        self.base.clear()
    }
}

impl<'a, T, K> ::std::iter::IntoIterator for &'a KeySetIndex<T, K>
where
    T: AsRef<Snapshot>,
    K: StorageKey,
{
    type Item = K;
    type IntoIter = KeySetIndexIter<'a, K>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a, K> Iterator for KeySetIndexIter<'a, K>
where
    K: StorageKey,
{
    type Item = K;

    fn next(&mut self) -> Option<Self::Item> {
        self.base_iter.next().map(|(k, ..)| k)
    }
}
<commit_msg>'KeySetIndex' examples<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An implementation of set for items that implement `StorageKey` trait.
use std::marker::PhantomData;

use super::{BaseIndex, BaseIndexIter, Snapshot, Fork, StorageKey};

/// A set of items that implement `StorageKey` trait.
///
/// `KeySetIndex` implements a set, storing the elements as keys with empty values.
/// `KeySetIndex` requires that the elements implement the [`StorageKey`] trait.
/// [`StorageKey`]: ../trait.StorageKey.html
#[derive(Debug)]
pub struct KeySetIndex<T, K> {
    base: BaseIndex<T>,
    _k: PhantomData<K>,
}

/// An iterator over the items of a `KeySetIndex`.
///
/// This struct is created by the [`iter`] or
/// [`iter_from`] methods on [`KeySetIndex`]. See its documentation for more.
///
/// [`iter`]: struct.KeySetIndex.html#method.iter
/// [`iter_from`]: struct.KeySetIndex.html#method.iter_from
/// [`KeySetIndex`]: struct.KeySetIndex.html
#[derive(Debug)]
pub struct KeySetIndexIter<'a, K> {
    base_iter: BaseIndexIter<'a, K, ()>,
}

impl<T, K> KeySetIndex<T, K> {
    /// Creates a new index representation based on the common prefix of its keys and storage view.
    ///
    /// Storage view can be specified as [`&Snapshot`] or [`&mut Fork`]. In the first case only
    /// immutable methods are available. In the second case both immutable and mutable methods are
    /// available.
    /// [`&Snapshot`]: ../trait.Snapshot.html
    /// [`&mut Fork`]: ../struct.Fork.html
    ///
    /// # Examples
    ///
    /// ```
    /// use exonum::storage::{MemoryDB, Database, KeySetIndex};
    ///
    /// let db = MemoryDB::new();
    /// let snapshot = db.snapshot();
    /// let prefix = vec![1, 2, 3];
    /// let index: KeySetIndex<_, u8> = KeySetIndex::new(prefix, &snapshot);
    /// # drop(index);
    /// ```
    pub fn new(prefix: Vec<u8>, view: T) -> Self {
        KeySetIndex {
            base: BaseIndex::new(prefix, view),
            _k: PhantomData,
        }
    }
}

impl<T, K> KeySetIndex<T, K>
where
    T: AsRef<Snapshot>,
    K: StorageKey,
{
    /// Returns `true` if the set contains a value.
    ///
    /// # Examples
    ///
    /// ```
    /// use exonum::storage::{MemoryDB, Database, KeySetIndex};
    ///
    /// let db = MemoryDB::new();
    /// let fork = db.fork();
    /// let prefix = vec![1, 2, 3];
    /// let mut index: = KeySetIndex::new(prefix, &mut fork);
    /// assert!(!index.contains(&1));
    /// index.insert(1);
    /// assert!(index.contains(&1));
    /// ```
    pub fn contains(&self, item: &K) -> bool {
        self.base.contains(item)
    }

    /// An iterator visiting all elements in ascending order. The iterator element type is K.
    ///
    /// # Examples
    ///
    /// ```
    /// use exonum::storage::{MemoryDB, Database, KeySetIndex};
    ///
    /// let db = MemoryDB::new();
    /// let fork = db.fork();
    /// let prefix = vec![1, 2, 3];
    /// let mut index: = KeySetIndex::new(prefix, &mut fork);
    /// index.insert(1);
    /// index.insert(2);
    /// index.insert(3);
    /// for val in index.iter() {
    ///     println!("{}", val);
    /// }
    /// ```
    pub fn iter(&self) -> KeySetIndexIter<K> {
        KeySetIndexIter { base_iter: self.base.iter(&()) }
    }

    /// An iterator visiting all elements in arbitrary order starting from the specified value.
    /// The iterator element type is K.
    ///
    /// # Examples
    ///
    /// ```
    /// use exonum::storage::{MemoryDB, Database, KeySetIndex};
    ///
    /// let db = MemoryDB::new();
    /// let fork = db.fork();
    /// let prefix = vec![1, 2, 3];
    /// let mut index: = KeySetIndex::new(prefix, &mut fork);
    /// index.insert(1);
    /// index.insert(2);
    /// index.insert(3);
    /// for val in index.iter_from(&2) {
    ///     println!("{}", val);
    /// }
    /// ```
    pub fn iter_from(&self, from: &K) -> KeySetIndexIter<K> {
        KeySetIndexIter { base_iter: self.base.iter_from(&(), from) }
    }
}

impl<'a, K> KeySetIndex<&'a mut Fork, K>
where
    K: StorageKey,
{
    /// Adds a value to the set.
    ///
    /// # Examples
    ///
    /// ```
    /// use exonum::storage::{MemoryDB, Database, KeySetIndex};
    ///
    /// let db = MemoryDB::new();
    /// let fork = db.fork();
    /// let prefix = vec![1, 2, 3];
    /// let mut index: = KeySetIndex::new(prefix, &mut fork);
    /// index.insert(1);
    /// assert!(index.contains(&1));
    /// ```
    pub fn insert(&mut self, item: K) {
        self.base.put(&item, ())
    }

    /// Removes a value from the set.
    ///
    /// # Examples
    ///
    /// ```
    /// use exonum::storage::{MemoryDB, Database, KeySetIndex};
    ///
    /// let db = MemoryDB::new();
    /// let fork = db.fork();
    /// let prefix = vec![1, 2, 3];
    /// let mut index: = KeySetIndex::new(prefix, &mut fork);
    /// index.insert(1);
    /// assert!(index.contains(&1));
    /// index.remove(&1);
    /// assert!(!index.contains(&1));
    /// ```
    pub fn remove(&mut self, item: &K) {
        self.base.remove(item)
    }

    /// Clears the set, removing all values.
    ///
    /// # Notes
    /// Currently this method is not optimized to delete large set of data. During the execution of
    /// this method the amount of allocated memory is linearly dependent on the number of elements
    /// in the index.
    ///
    /// # Examples
    ///
    /// ```
    /// use exonum::storage::{MemoryDB, Database, KeySetIndex};
    ///
    /// let db = MemoryDB::new();
    /// let fork = db.fork();
    /// let prefix = vec![1, 2, 3];
    /// let mut index: = KeySetIndex::new(prefix, &mut fork);
    /// index.insert(1);
    /// assert!(index.contains(&1));
    /// index.clear();
    /// assert!(!index.contains(&1));
    /// ```
    pub fn clear(&mut self) {
        self.base.clear()
    }
}

impl<'a, T, K> ::std::iter::IntoIterator for &'a KeySetIndex<T, K>
where
    T: AsRef<Snapshot>,
    K: StorageKey,
{
    type Item = K;
    type IntoIter = KeySetIndexIter<'a, K>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<'a, K> Iterator for KeySetIndexIter<'a, K>
where
    K: StorageKey,
{
    type Item = K;

    fn next(&mut self) -> Option<Self::Item> {
        self.base_iter.next().map(|(k, ..)| k)
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use prelude::v1::*;

use ffi::{OsStr, OsString};
use io::{self, ErrorKind};
use libc;
use mem;
use num::Int;
use old_io::{self, IoResult, IoError};
use os::windows::{OsStrExt, OsStringExt};
use path::PathBuf;
use sync::{Once, ONCE_INIT};

macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => (
    static $name: Helper<$m> = Helper {
        lock: ::sync::MUTEX_INIT,
        cond: ::sync::CONDVAR_INIT,
        chan: ::cell::UnsafeCell { value: 0 as *mut ::sync::mpsc::Sender<$m> },
        signal: ::cell::UnsafeCell { value: 0 },
        initialized: ::cell::UnsafeCell { value: false },
        shutdown: ::cell::UnsafeCell { value: false },
    };
) }

pub mod backtrace;
pub mod c;
pub mod condvar;
pub mod ext;
pub mod fs;
pub mod fs2;
pub mod handle;
pub mod helper_signal;
pub mod mutex;
pub mod net;
pub mod os;
pub mod os_str;
pub mod pipe;
pub mod pipe2;
pub mod process;
pub mod process2;
pub mod rwlock;
pub mod stack_overflow;
pub mod sync;
pub mod tcp;
pub mod thread;
pub mod thread_local;
pub mod time;
pub mod timer;
pub mod tty;
pub mod udp;

pub mod addrinfo {
    pub use sys_common::net::get_host_addresses;
    pub use sys_common::net::get_address_name;
}

// FIXME: move these to c module
pub type sock_t = libc::SOCKET;
pub type wrlen = libc::c_int;
pub type msglen_t = libc::c_int;
pub unsafe fn close_sock(sock: sock_t) { let _ = libc::closesocket(sock); }

// windows has zero values as errors
fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
    if ret == 0 {
        Err(last_error())
    } else {
        Ok(())
    }
}

pub fn last_error() -> IoError {
    let errno = os::errno() as i32;
    let mut err = decode_error(errno);
    err.detail = Some(os::error_string(errno));
    err
}

pub fn last_net_error() -> IoError {
    let errno = unsafe { c::WSAGetLastError() as i32 };
    let mut err = decode_error(errno);
    err.detail = Some(os::error_string(errno));
    err
}

pub fn last_gai_error(_errno: i32) -> IoError {
    last_net_error()
}

/// Convert an `errno` value into a high-level error variant and description.
pub fn decode_error(errno: i32) -> IoError {
    let (kind, desc) = match errno {
        libc::EOF => (old_io::EndOfFile, "end of file"),
        libc::ERROR_NO_DATA => (old_io::BrokenPipe, "the pipe is being closed"),
        libc::ERROR_FILE_NOT_FOUND => (old_io::FileNotFound, "file not found"),
        libc::ERROR_INVALID_NAME => (old_io::InvalidInput, "invalid file name"),
        libc::WSAECONNREFUSED => (old_io::ConnectionRefused, "connection refused"),
        libc::WSAECONNRESET => (old_io::ConnectionReset, "connection reset"),
        libc::ERROR_ACCESS_DENIED | libc::WSAEACCES =>
            (old_io::PermissionDenied, "permission denied"),
        libc::WSAEWOULDBLOCK => {
            (old_io::ResourceUnavailable, "resource temporarily unavailable")
        }
        libc::WSAENOTCONN => (old_io::NotConnected, "not connected"),
        libc::WSAECONNABORTED => (old_io::ConnectionAborted, "connection aborted"),
        libc::WSAEADDRNOTAVAIL => (old_io::ConnectionRefused, "address not available"),
        libc::WSAEADDRINUSE => (old_io::ConnectionRefused, "address in use"),
        libc::ERROR_BROKEN_PIPE => (old_io::EndOfFile, "the pipe has ended"),
        libc::ERROR_OPERATION_ABORTED =>
            (old_io::TimedOut, "operation timed out"),
        libc::WSAEINVAL => (old_io::InvalidInput, "invalid argument"),
        libc::ERROR_CALL_NOT_IMPLEMENTED =>
            (old_io::IoUnavailable, "function not implemented"),
        libc::ERROR_INVALID_HANDLE =>
            (old_io::MismatchedFileTypeForOperation,
             "invalid handle provided to function"),
        libc::ERROR_NOTHING_TO_TERMINATE =>
            (old_io::InvalidInput, "no process to kill"),
        libc::ERROR_ALREADY_EXISTS =>
            (old_io::PathAlreadyExists, "path already exists"),

        // libuv maps this error code to EISDIR. we do too. if it is found
        // to be incorrect, we can add in some more machinery to only
        // return this message when ERROR_INVALID_FUNCTION after certain
        // Windows calls.
        libc::ERROR_INVALID_FUNCTION => (old_io::InvalidInput,
                                         "illegal operation on a directory"),

        _ => (old_io::OtherIoError, "unknown error")
    };
    IoError { kind: kind, desc: desc, detail: None }
}

pub fn decode_error_detailed(errno: i32) -> IoError {
    let mut err = decode_error(errno);
    err.detail = Some(os::error_string(errno));
    err
}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    match errno as libc::c_int {
        libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied,
        libc::ERROR_ALREADY_EXISTS => ErrorKind::PathAlreadyExists,
        libc::ERROR_BROKEN_PIPE => ErrorKind::BrokenPipe,
        libc::ERROR_FILE_NOT_FOUND => ErrorKind::FileNotFound,
        libc::ERROR_INVALID_FUNCTION => ErrorKind::InvalidInput,
        libc::ERROR_INVALID_HANDLE => ErrorKind::MismatchedFileTypeForOperation,
        libc::ERROR_INVALID_NAME => ErrorKind::InvalidInput,
        libc::ERROR_NOTHING_TO_TERMINATE => ErrorKind::InvalidInput,
        libc::ERROR_NO_DATA => ErrorKind::BrokenPipe,
        libc::ERROR_OPERATION_ABORTED => ErrorKind::TimedOut,

        libc::WSAEACCES => ErrorKind::PermissionDenied,
        libc::WSAEADDRINUSE => ErrorKind::ConnectionRefused,
        libc::WSAEADDRNOTAVAIL => ErrorKind::ConnectionRefused,
        libc::WSAECONNABORTED => ErrorKind::ConnectionAborted,
        libc::WSAECONNREFUSED => ErrorKind::ConnectionRefused,
        libc::WSAECONNRESET => ErrorKind::ConnectionReset,
        libc::WSAEINVAL => ErrorKind::InvalidInput,
        libc::WSAENOTCONN => ErrorKind::NotConnected,
        libc::WSAEWOULDBLOCK => ErrorKind::ResourceUnavailable,

        _ => ErrorKind::Other,
    }
}

#[inline]
pub fn retry<I, F>(f: F) -> I where F: FnOnce() -> I { f() } // PR rust-lang/rust/#17020

pub fn ms_to_timeval(ms: u64) -> libc::timeval {
    libc::timeval {
        tv_sec: (ms / 1000) as libc::c_long,
        tv_usec: ((ms % 1000) * 1000) as libc::c_long,
    }
}

pub fn wouldblock() -> bool {
    let err = os::errno();
    err == libc::WSAEWOULDBLOCK as i32
}

pub fn set_nonblocking(fd: sock_t, nb: bool) {
    let mut set = nb as libc::c_ulong;
    if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) } != 0 {
        // The above function should not return an error unless we passed it
        // invalid parameters. Panic on errors.
        Err(last_error()).unwrap();
    }
}

pub fn init_net() {
    unsafe {
        static START: Once = ONCE_INIT;

        START.call_once(|| {
            let mut data: c::WSADATA = mem::zeroed();
            let ret = c::WSAStartup(0x202, // version 2.2
                                    &mut data);
            assert_eq!(ret, 0);
        });
    }
}

pub fn unimpl() -> IoError {
    IoError {
        kind: old_io::IoUnavailable,
        desc: "operation is not implemented",
        detail: None,
    }
}

fn to_utf16(s: Option<&str>) -> IoResult<Vec<u16>> {
    match s {
        Some(s) => Ok(to_utf16_os(OsStr::from_str(s))),
        None => Err(IoError {
            kind: old_io::InvalidInput,
            desc: "valid unicode input required",
            detail: None,
        }),
    }
}

fn to_utf16_os(s: &OsStr) -> Vec<u16> {
    let mut v: Vec<_> = s.encode_wide().collect();
    v.push(0);
    v
}

// Many Windows APIs follow a pattern of where we hand the a buffer and then
// they will report back to us how large the buffer should be or how many bytes
// currently reside in the buffer. This function is an abstraction over these
// functions by making them easier to call.
//
// The first callback, `f1`, is yielded a (pointer, len) pair which can be
// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case).
// The closure is expected to return what the syscall returns which will be
// interpreted by this function to determine if the syscall needs to be invoked
// again (with more buffer space).
//
// Once the syscall has completed (errors bail out early) the second closure is
// yielded the data which has been read from the syscall. The return value
// from this closure is then the return value of the function.
fn fill_utf16_buf_base<F1, F2, T>(mut f1: F1, f2: F2) -> Result<T, ()>
    where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
          F2: FnOnce(&[u16]) -> T
{
    // Start off with a stack buf but then spill over to the heap if we end up
    // needing more space.
    let mut stack_buf = [0u16; 512];
    let mut heap_buf = Vec::new();
    unsafe {
        let mut n = stack_buf.len();
        loop {
            let buf = if n <= stack_buf.len() {
                &mut stack_buf[..]
            } else {
                let extra = n - heap_buf.len();
                heap_buf.reserve(extra);
                heap_buf.set_len(n);
                &mut heap_buf[..]
            };

            // This function is typically called on windows API functions which
            // will return the correct length of the string, but these functions
            // also return the `0` on error. In some cases, however, the
            // returned "correct length" may actually be 0!
            //
            // To handle this case we call `SetLastError` to reset it to 0 and
            // then check it again if we get the "0 error value". If the "last
            // error" is still 0 then we interpret it as a 0 length buffer and
            // not an actual error.
            c::SetLastError(0);
            let k = match f1(buf.as_mut_ptr(), n as libc::DWORD) {
                0 if libc::GetLastError() == 0 => 0,
                0 => return Err(()),
                n => n,
            } as usize;
            if k == n && libc::GetLastError() ==
                            libc::ERROR_INSUFFICIENT_BUFFER as libc::DWORD {
                n *= 2;
            } else if k >= n {
                n = k;
            } else {
                return Ok(f2(&buf[..k]))
            }
        }
    }
}

fn fill_utf16_buf<F1, F2, T>(f1: F1, f2: F2) -> IoResult<T>
    where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
          F2: FnOnce(&[u16]) -> T
{
    fill_utf16_buf_base(f1, f2).map_err(|()| IoError::last_error())
}

fn fill_utf16_buf_new<F1, F2, T>(f1: F1, f2: F2) -> io::Result<T>
    where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
          F2: FnOnce(&[u16]) -> T
{
    fill_utf16_buf_base(f1, f2).map_err(|()| io::Error::last_os_error())
}

fn os2path(s: &[u16]) -> PathBuf {
    let os = <OsString as OsStringExt>::from_wide(s);
    // FIXME(#22751) should consume `os`
    PathBuf::new(&os)
}

pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
    match v.iter().position(|c| *c == 0) {
        // don't include the 0
        Some(i) => &v[..i],
        None => v
    }
}

fn cvt<I: Int>(i: I) -> io::Result<I> {
    if i == Int::zero() {
        Err(io::Error::last_os_error())
    } else {
        Ok(i)
    }
}

fn ms_to_filetime(ms: u64) -> libc::FILETIME {
    // A FILETIME is a count of 100 nanosecond intervals, so we multiply by
    // 10000 b/c there are 10000 intervals in 1 ms
    let ms = ms * 10000;
    libc::FILETIME {
        dwLowDateTime: ms as u32,
        dwHighDateTime: (ms >> 32) as u32,
    }
}
<commit_msg>Fix type inference error (fixup #22739)<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use prelude::v1::*;

use ffi::{OsStr, OsString};
use io::{self, ErrorKind};
use libc;
use mem;
use num::Int;
use old_io::{self, IoResult, IoError};
use os::windows::{OsStrExt, OsStringExt};
use path::PathBuf;
use sync::{Once, ONCE_INIT};

macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => (
    static $name: Helper<$m> = Helper {
        lock: ::sync::MUTEX_INIT,
        cond: ::sync::CONDVAR_INIT,
        chan: ::cell::UnsafeCell { value: 0 as *mut ::sync::mpsc::Sender<$m> },
        signal: ::cell::UnsafeCell { value: 0 },
        initialized: ::cell::UnsafeCell { value: false },
        shutdown: ::cell::UnsafeCell { value: false },
    };
) }

pub mod backtrace;
pub mod c;
pub mod condvar;
pub mod ext;
pub mod fs;
pub mod fs2;
pub mod handle;
pub mod helper_signal;
pub mod mutex;
pub mod net;
pub mod os;
pub mod os_str;
pub mod pipe;
pub mod pipe2;
pub mod process;
pub mod process2;
pub mod rwlock;
pub mod stack_overflow;
pub mod sync;
pub mod tcp;
pub mod thread;
pub mod thread_local;
pub mod time;
pub mod timer;
pub mod tty;
pub mod udp;

pub mod addrinfo {
    pub use sys_common::net::get_host_addresses;
    pub use sys_common::net::get_address_name;
}

// FIXME: move these to c module
pub type sock_t = libc::SOCKET;
pub type wrlen = libc::c_int;
pub type msglen_t = libc::c_int;
pub unsafe fn close_sock(sock: sock_t) { let _ = libc::closesocket(sock); }

// windows has zero values as errors
fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
    if ret == 0 {
        Err(last_error())
    } else {
        Ok(())
    }
}

pub fn last_error() -> IoError {
    let errno = os::errno() as i32;
    let mut err = decode_error(errno);
    err.detail = Some(os::error_string(errno));
    err
}

pub fn last_net_error() -> IoError {
    let errno = unsafe { c::WSAGetLastError() as i32 };
    let mut err = decode_error(errno);
    err.detail = Some(os::error_string(errno));
    err
}

pub fn last_gai_error(_errno: i32) -> IoError {
    last_net_error()
}

/// Convert an `errno` value into a high-level error variant and description.
pub fn decode_error(errno: i32) -> IoError {
    let (kind, desc) = match errno {
        libc::EOF => (old_io::EndOfFile, "end of file"),
        libc::ERROR_NO_DATA => (old_io::BrokenPipe, "the pipe is being closed"),
        libc::ERROR_FILE_NOT_FOUND => (old_io::FileNotFound, "file not found"),
        libc::ERROR_INVALID_NAME => (old_io::InvalidInput, "invalid file name"),
        libc::WSAECONNREFUSED => (old_io::ConnectionRefused, "connection refused"),
        libc::WSAECONNRESET => (old_io::ConnectionReset, "connection reset"),
        libc::ERROR_ACCESS_DENIED | libc::WSAEACCES =>
            (old_io::PermissionDenied, "permission denied"),
        libc::WSAEWOULDBLOCK => {
            (old_io::ResourceUnavailable, "resource temporarily unavailable")
        }
        libc::WSAENOTCONN => (old_io::NotConnected, "not connected"),
        libc::WSAECONNABORTED => (old_io::ConnectionAborted, "connection aborted"),
        libc::WSAEADDRNOTAVAIL => (old_io::ConnectionRefused, "address not available"),
        libc::WSAEADDRINUSE => (old_io::ConnectionRefused, "address in use"),
        libc::ERROR_BROKEN_PIPE => (old_io::EndOfFile, "the pipe has ended"),
        libc::ERROR_OPERATION_ABORTED =>
            (old_io::TimedOut, "operation timed out"),
        libc::WSAEINVAL => (old_io::InvalidInput, "invalid argument"),
        libc::ERROR_CALL_NOT_IMPLEMENTED =>
            (old_io::IoUnavailable, "function not implemented"),
        libc::ERROR_INVALID_HANDLE =>
            (old_io::MismatchedFileTypeForOperation,
             "invalid handle provided to function"),
        libc::ERROR_NOTHING_TO_TERMINATE =>
            (old_io::InvalidInput, "no process to kill"),
        libc::ERROR_ALREADY_EXISTS =>
            (old_io::PathAlreadyExists, "path already exists"),

        // libuv maps this error code to EISDIR. we do too. if it is found
        // to be incorrect, we can add in some more machinery to only
        // return this message when ERROR_INVALID_FUNCTION after certain
        // Windows calls.
        libc::ERROR_INVALID_FUNCTION => (old_io::InvalidInput,
                                         "illegal operation on a directory"),

        _ => (old_io::OtherIoError, "unknown error")
    };
    IoError { kind: kind, desc: desc, detail: None }
}

pub fn decode_error_detailed(errno: i32) -> IoError {
    let mut err = decode_error(errno);
    err.detail = Some(os::error_string(errno));
    err
}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    match errno as libc::c_int {
        libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied,
        libc::ERROR_ALREADY_EXISTS => ErrorKind::PathAlreadyExists,
        libc::ERROR_BROKEN_PIPE => ErrorKind::BrokenPipe,
        libc::ERROR_FILE_NOT_FOUND => ErrorKind::FileNotFound,
        libc::ERROR_INVALID_FUNCTION => ErrorKind::InvalidInput,
        libc::ERROR_INVALID_HANDLE => ErrorKind::MismatchedFileTypeForOperation,
        libc::ERROR_INVALID_NAME => ErrorKind::InvalidInput,
        libc::ERROR_NOTHING_TO_TERMINATE => ErrorKind::InvalidInput,
        libc::ERROR_NO_DATA => ErrorKind::BrokenPipe,
        libc::ERROR_OPERATION_ABORTED => ErrorKind::TimedOut,

        libc::WSAEACCES => ErrorKind::PermissionDenied,
        libc::WSAEADDRINUSE => ErrorKind::ConnectionRefused,
        libc::WSAEADDRNOTAVAIL => ErrorKind::ConnectionRefused,
        libc::WSAECONNABORTED => ErrorKind::ConnectionAborted,
        libc::WSAECONNREFUSED => ErrorKind::ConnectionRefused,
        libc::WSAECONNRESET => ErrorKind::ConnectionReset,
        libc::WSAEINVAL => ErrorKind::InvalidInput,
        libc::WSAENOTCONN => ErrorKind::NotConnected,
        libc::WSAEWOULDBLOCK => ErrorKind::ResourceUnavailable,

        _ => ErrorKind::Other,
    }
}

#[inline]
pub fn retry<I, F>(f: F) -> I where F: FnOnce() -> I { f() } // PR rust-lang/rust/#17020

pub fn ms_to_timeval(ms: u64) -> libc::timeval {
    libc::timeval {
        tv_sec: (ms / 1000) as libc::c_long,
        tv_usec: ((ms % 1000) * 1000) as libc::c_long,
    }
}

pub fn wouldblock() -> bool {
    let err = os::errno();
    err == libc::WSAEWOULDBLOCK as i32
}

pub fn set_nonblocking(fd: sock_t, nb: bool) {
    let mut set = nb as libc::c_ulong;
    if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) } != 0 {
        // The above function should not return an error unless we passed it
        // invalid parameters. Panic on errors.
        panic!("set_nonblocking called with invalid parameters: {}", last_error());
    }
}

pub fn init_net() {
    unsafe {
        static START: Once = ONCE_INIT;

        START.call_once(|| {
            let mut data: c::WSADATA = mem::zeroed();
            let ret = c::WSAStartup(0x202, // version 2.2
                                    &mut data);
            assert_eq!(ret, 0);
        });
    }
}

pub fn unimpl() -> IoError {
    IoError {
        kind: old_io::IoUnavailable,
        desc: "operation is not implemented",
        detail: None,
    }
}

fn to_utf16(s: Option<&str>) -> IoResult<Vec<u16>> {
    match s {
        Some(s) => Ok(to_utf16_os(OsStr::from_str(s))),
        None => Err(IoError {
            kind: old_io::InvalidInput,
            desc: "valid unicode input required",
            detail: None,
        }),
    }
}

fn to_utf16_os(s: &OsStr) -> Vec<u16> {
    let mut v: Vec<_> = s.encode_wide().collect();
    v.push(0);
    v
}

// Many Windows APIs follow a pattern of where we hand the a buffer and then
// they will report back to us how large the buffer should be or how many bytes
// currently reside in the buffer. This function is an abstraction over these
// functions by making them easier to call.
//
// The first callback, `f1`, is yielded a (pointer, len) pair which can be
// passed to a syscall. The `ptr` is valid for `len` items (u16 in this case).
// The closure is expected to return what the syscall returns which will be
// interpreted by this function to determine if the syscall needs to be invoked
// again (with more buffer space).
//
// Once the syscall has completed (errors bail out early) the second closure is
// yielded the data which has been read from the syscall. The return value
// from this closure is then the return value of the function.
fn fill_utf16_buf_base<F1, F2, T>(mut f1: F1, f2: F2) -> Result<T, ()>
    where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
          F2: FnOnce(&[u16]) -> T
{
    // Start off with a stack buf but then spill over to the heap if we end up
    // needing more space.
    let mut stack_buf = [0u16; 512];
    let mut heap_buf = Vec::new();
    unsafe {
        let mut n = stack_buf.len();
        loop {
            let buf = if n <= stack_buf.len() {
                &mut stack_buf[..]
            } else {
                let extra = n - heap_buf.len();
                heap_buf.reserve(extra);
                heap_buf.set_len(n);
                &mut heap_buf[..]
            };

            // This function is typically called on windows API functions which
            // will return the correct length of the string, but these functions
            // also return the `0` on error. In some cases, however, the
            // returned "correct length" may actually be 0!
            //
            // To handle this case we call `SetLastError` to reset it to 0 and
            // then check it again if we get the "0 error value". If the "last
            // error" is still 0 then we interpret it as a 0 length buffer and
            // not an actual error.
            c::SetLastError(0);
            let k = match f1(buf.as_mut_ptr(), n as libc::DWORD) {
                0 if libc::GetLastError() == 0 => 0,
                0 => return Err(()),
                n => n,
            } as usize;
            if k == n && libc::GetLastError() ==
                            libc::ERROR_INSUFFICIENT_BUFFER as libc::DWORD {
                n *= 2;
            } else if k >= n {
                n = k;
            } else {
                return Ok(f2(&buf[..k]))
            }
        }
    }
}

fn fill_utf16_buf<F1, F2, T>(f1: F1, f2: F2) -> IoResult<T>
    where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
          F2: FnOnce(&[u16]) -> T
{
    fill_utf16_buf_base(f1, f2).map_err(|()| IoError::last_error())
}

fn fill_utf16_buf_new<F1, F2, T>(f1: F1, f2: F2) -> io::Result<T>
    where F1: FnMut(*mut u16, libc::DWORD) -> libc::DWORD,
          F2: FnOnce(&[u16]) -> T
{
    fill_utf16_buf_base(f1, f2).map_err(|()| io::Error::last_os_error())
}

fn os2path(s: &[u16]) -> PathBuf {
    let os = <OsString as OsStringExt>::from_wide(s);
    // FIXME(#22751) should consume `os`
    PathBuf::new(&os)
}

pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
    match v.iter().position(|c| *c == 0) {
        // don't include the 0
        Some(i) => &v[..i],
        None => v
    }
}

fn cvt<I: Int>(i: I) -> io::Result<I> {
    if i == Int::zero() {
        Err(io::Error::last_os_error())
    } else {
        Ok(i)
    }
}

fn ms_to_filetime(ms: u64) -> libc::FILETIME {
    // A FILETIME is a count of 100 nanosecond intervals, so we multiply by
    // 10000 b/c there are 10000 intervals in 1 ms
    let ms = ms * 10000;
    libc::FILETIME {
        dwLowDateTime: ms as u32,
        dwHighDateTime: (ms >> 32) as u32,
    }
}
<|endoftext|>