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

pub type ResultMap<K, T, E> = Vec<(K, Result<T, E>)>;

/// 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, Vec<(Vec<SetterSelector>, Value)>) -> ResultMap<Id<Setter>, (), Error>;

    /// Watch for any change
    ///
    /// # WebSocket API
    ///
    /// `/api/v1/channels/watch`
    fn register_channel_watch(&self, selectors: Vec<GetterSelector>, range: Exactly<Range>, on_event: Box<Fn(WatchEvent) + Send>) -> Self::WatchGuard;

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

<commit_msg>[fxbox/taxonomy] register_channel_watch now takes a TargetMap, also clarified doc<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 {
    /// 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.
    ///
    /// - 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.
    ///
    /// # 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>#![deny(warnings)]
#![deny(unused_import_braces, unused_qualifications)]

#[macro_use] extern crate log;
extern crate clap;
extern crate env_logger;

extern crate breeze_core;
extern crate breeze_backends;
extern crate breeze_backend;

use breeze_core::rom::Rom;
use breeze_core::snes::Emulator;
use breeze_core::input::Input;
use breeze_core::save::SaveStateFormat;
use breeze_core::record::{RecordingFormat, create_recorder, create_replayer};
use breeze_backend::Renderer;

use clap::ArgMatches;

use std::env;
use std::error::Error;
use std::fs::File;
use std::io::{BufReader, Read};

// FIXME Replace this hack with input detection
#[cfg(feature = "sdl")]
fn attach_default_input(input: &mut Input) {
    use breeze_core::input::Peripheral;
    use breeze_backends::breeze_sdl::KeyboardInput;

    input.ports.0 = Some(Peripheral::new_joypad(Box::new(KeyboardInput)));
}
#[cfg(not(feature = "sdl"))]
fn attach_default_input(_: &mut Input) {
    warn!("`sdl` feature not enabled, input will not work");
}

fn process_args(args: &ArgMatches) -> Result<(), Box<Error>> {
    if args.value_of("record").is_some() && args.value_of("replay").is_some() {
        return Err("`record` and `replay` may not be specified together!".into());
    }

    let renderer_name = args.value_of("renderer").unwrap_or(&*breeze_backends::DEFAULT_RENDERER);

    let renderer_fn = match breeze_backends::RENDERER_MAP.get(renderer_name) {
        None => {
            println!("error: unknown renderer: {}", renderer_name);
            println!("{} renderers known:", breeze_backends::RENDERER_MAP.len());
            for (name, opt_fn) in breeze_backends::RENDERER_MAP.iter() {
                println!("\t{}\t{}", name, match *opt_fn {
                    Some(_) => "available",
                    None => "not compiled in",
                });
            }

            return Err("exiting".into());
        }
        Some(&None) => {
            println!("error: renderer '{}' not compiled in", renderer_name);
            println!("(compile with `cargo build --features {}` to enable)", renderer_name);
            // NOTE: Make sure that renderer name always matches feature name!
            return Err("exiting".into());
        }
        Some(&Some(renderer_fn)) => {
            info!("using {} renderer", renderer_name);
            renderer_fn
        }
    };

    let audio_name = args.value_of("audio").unwrap_or(&*breeze_backends::DEFAULT_AUDIO);
    let audio_fn = match breeze_backends::AUDIO_MAP.get(audio_name) {
        None => {
            println!("error: unknown audio sink: {}", audio_name);
            println!("{} audio sinks known:", breeze_backends::AUDIO_MAP.len());
            for (name, opt_fn) in breeze_backends::AUDIO_MAP.iter() {
                println!("\t{}\t{}", name, match *opt_fn {
                    Some(_) => "available",
                    None => "not compiled in",
                });
            }

            return Err("exiting".into());
        }
        Some(&None) => {
            println!("error: audio backend '{}' not compiled in", audio_name);
            println!("(compile with `cargo build --features {}` to enable)", audio_name);
            // NOTE: Make sure that renderer name always matches feature name!
            return Err("exiting".into());
        }
        Some(&Some(audio_fn)) => {
            info!("using {} audio sink", audio_name);
            audio_fn
        }
    };

    let filename = args.value_of("rom").unwrap();
    let mut file = try!(File::open(&filename));
    let mut buf = Vec::new();
    try!(file.read_to_end(&mut buf));

    let rom = try!(Rom::from_bytes(&buf));
    let mut renderer = try!(renderer_fn());
    if let Some(title) = rom.get_title() {
        renderer.set_rom_title(title);
    }

    let audio = try!(audio_fn());

    let mut emu = Emulator::new(rom, renderer, audio);
    attach_default_input(&mut emu.peripherals_mut().input);
    if let Some(record_file) = args.value_of("record") {
        let writer = Box::new(File::create(record_file).unwrap());
        let recorder = create_recorder(RecordingFormat::default(), writer, &emu.snes).unwrap();
        emu.peripherals_mut().input.start_recording(recorder);
    }
    if let Some(replay_file) = args.value_of("replay") {
        let reader = Box::new(BufReader::new(File::open(replay_file).unwrap()));
        let replayer = create_replayer(RecordingFormat::default(), reader, &emu.snes).unwrap();
        emu.peripherals_mut().input.start_replay(replayer);
    }
    if let Some(filename) = args.value_of("savestate") {
        let file = File::open(filename).unwrap();
        let mut bufrd = BufReader::new(file);
        emu.snes.restore_save_state(SaveStateFormat::default(), &mut bufrd).unwrap()
    }

    if cfg!(debug_assertions) && args.is_present("oneframe") {
        debug!("PPU H={}, V={}",
            emu.peripherals().ppu.h_counter(),
            emu.peripherals().ppu.v_counter());
        try!(emu.snes.render_frame(|_framebuf| Ok(vec![])));

        info!("frame rendered. pausing emulation.");

        // Keep rendering, but don't run emulation
        // Copy out the frame buffer because the damn borrow checker doesn't like it otherwise
        let framebuf = emu.peripherals().ppu.framebuf.clone();
        loop {
            let actions = try!(emu.renderer.render(&*framebuf));
            for a in actions {
                if emu.handle_action(a) { break }
            }
        }
    } else {
        // Run normally
        try!(emu.run());
    }

    Ok(())
}

fn main() {
    if env::var_os("RUST_LOG").is_none() {
        env::set_var("RUST_LOG", "breeze=INFO");
    }
    env_logger::init().unwrap();

    let mut app = clap::App::new("breeze")
        .version(env!("CARGO_PKG_VERSION"))
        .about("SNES emulator")
        .arg(clap::Arg::with_name("rom")
            .required(true)
            .value_name("ROM_PATH")
            .takes_value(true)
            .help("The ROM file to execute"))
        .arg(clap::Arg::with_name("renderer")
            .short("R")
            .long("renderer")
            .takes_value(true)
            .help("The renderer to use"))
        .arg(clap::Arg::with_name("audio")
            .short("A")
            .long("audio")
            .takes_value(true)
            .help("The audio backend to use"))
        .arg(clap::Arg::with_name("savestate")
            .long("savestate")
            .takes_value(true)
            .help("The save state file to load"))
        .arg(clap::Arg::with_name("record")
            .long("record")
            .takes_value(true)
            .help("Record input to a text file"))
        .arg(clap::Arg::with_name("replay")
            .long("replay")
            .takes_value(true)
            .help("Replay a recording from a text file"));

    // Add debugging options
    if cfg!(debug_assertions) {
        app = app.arg(clap::Arg::with_name("oneframe")
            .long("oneframe")
            .help("Render a single frame, then pause"));
    }

    let args = app.get_matches();
    match process_args(&args) {
        Ok(()) => {},
        Err(e) => {
            // FIXME: Glium swallows useful information when using {} instead of {:?}
            error!("{:?}", e);
            println!("error: {}", e);
        }
    }
}
<commit_msg>Proper error handling in the CLI app<commit_after>#![deny(warnings)]
#![deny(unused_import_braces, unused_qualifications)]

#[macro_use] extern crate log;
extern crate clap;
extern crate env_logger;

extern crate breeze_core;
extern crate breeze_backends;
extern crate breeze_backend;

use breeze_core::rom::Rom;
use breeze_core::snes::Emulator;
use breeze_core::input::Input;
use breeze_core::save::SaveStateFormat;
use breeze_core::record::{RecordingFormat, create_recorder, create_replayer};
use breeze_backend::Renderer;

use clap::ArgMatches;

use std::env;
use std::error::Error;
use std::fs::File;
use std::io::{BufReader, Read};
use std::process;

// FIXME Replace this hack with input detection
#[cfg(feature = "sdl")]
fn attach_default_input(input: &mut Input) {
    use breeze_core::input::Peripheral;
    use breeze_backends::breeze_sdl::KeyboardInput;

    input.ports.0 = Some(Peripheral::new_joypad(Box::new(KeyboardInput)));
}
#[cfg(not(feature = "sdl"))]
fn attach_default_input(_: &mut Input) {
    warn!("`sdl` feature not enabled, input will not work");
}

fn process_args(args: &ArgMatches) -> Result<(), Box<Error>> {
    if args.value_of("record").is_some() && args.value_of("replay").is_some() {
        return Err("`record` and `replay` may not be specified together!".into());
    }

    let renderer_name = args.value_of("renderer").unwrap_or(&*breeze_backends::DEFAULT_RENDERER);

    let renderer_fn = match breeze_backends::RENDERER_MAP.get(renderer_name) {
        None => {
            let mut message = format!("unknown renderer: {}\n", renderer_name);
            message.push_str(&format!("{} renderers known:\n",
                breeze_backends::RENDERER_MAP.len()));

            for (name, opt_fn) in breeze_backends::RENDERER_MAP.iter() {
                message.push_str(&format!("\t{}\t{}\n", name, match *opt_fn {
                    Some(_) => "available",
                    None => "not compiled in",
                }));
            }

            return Err(message.into());
        }
        Some(&None) => {
            let mut message = format!("renderer '{}' not compiled in", renderer_name);
            message.push_str(&format!("(compile with `cargo build --features {}` to enable)",
                renderer_name));
            // NOTE: Make sure that renderer name always matches feature name!

            return Err("exiting".into());
        }
        Some(&Some(renderer_fn)) => {
            renderer_fn
        }
    };

    let audio_name = args.value_of("audio").unwrap_or(&*breeze_backends::DEFAULT_AUDIO);
    let audio_fn = match breeze_backends::AUDIO_MAP.get(audio_name) {
        None => {
            let mut message = format!("unknown audio sink: {}\n", audio_name);
            message.push_str(&format!("{} audio sinks known:\n", breeze_backends::AUDIO_MAP.len()));

            for (name, opt_fn) in breeze_backends::AUDIO_MAP.iter() {
                message.push_str(&format!("\t{}\t{}\n", name, match *opt_fn {
                    Some(_) => "available",
                    None => "not compiled in",
                }));
            }

            return Err(message.into());
        }
        Some(&None) => {
            let mut message = format!("audio backend '{0}' not compiled in\n", audio_name);
            message.push_str(&format!("(compile with `cargo build --features {0}` to enable)",
                audio_name));
            // NOTE: Make sure that audio sink name always matches feature name!

            return Err(message.into());
        }
        Some(&Some(audio_fn)) => {
            audio_fn
        }
    };

    // Load the ROM into memory
    let filename = args.value_of("rom").unwrap();
    let mut file = try!(File::open(&filename));
    let mut buf = Vec::new();
    try!(file.read_to_end(&mut buf));

    let rom = try!(Rom::from_bytes(&buf));

    // Create the backend parts
    info!("using {} renderer", renderer_name);
    let mut renderer = try!(renderer_fn());
    if let Some(title) = rom.get_title() {
        renderer.set_rom_title(title);
    }

    info!("using {} audio sink", audio_name);
    let audio = try!(audio_fn());

    // Put everything together in the emulator
    let mut emu = Emulator::new(rom, renderer, audio);
    attach_default_input(&mut emu.peripherals_mut().input);

    if let Some(record_file) = args.value_of("record") {
        let writer = Box::new(File::create(record_file).unwrap());
        let recorder = create_recorder(RecordingFormat::default(), writer, &emu.snes).unwrap();
        emu.peripherals_mut().input.start_recording(recorder);
    }
    if let Some(replay_file) = args.value_of("replay") {
        let reader = Box::new(BufReader::new(File::open(replay_file).unwrap()));
        let replayer = create_replayer(RecordingFormat::default(), reader, &emu.snes).unwrap();
        emu.peripherals_mut().input.start_replay(replayer);
    }
    if let Some(filename) = args.value_of("savestate") {
        let file = File::open(filename).unwrap();
        let mut bufrd = BufReader::new(file);
        emu.snes.restore_save_state(SaveStateFormat::default(), &mut bufrd).unwrap()
    }

    if cfg!(debug_assertions) && args.is_present("oneframe") {
        debug!("PPU H={}, V={}",
            emu.peripherals().ppu.h_counter(),
            emu.peripherals().ppu.v_counter());
        try!(emu.snes.render_frame(|_framebuf| Ok(vec![])));

        info!("frame rendered. pausing emulation.");

        // Keep rendering, but don't run emulation
        // Copy out the frame buffer because the damn borrow checker doesn't like it otherwise
        let framebuf = emu.peripherals().ppu.framebuf.clone();
        loop {
            let actions = try!(emu.renderer.render(&*framebuf));
            for a in actions {
                if emu.handle_action(a) { break }
            }
        }
    } else {
        // Run normally
        try!(emu.run());
    }

    Ok(())
}

fn main() {
    if env::var_os("RUST_LOG").is_none() {
        env::set_var("RUST_LOG", "breeze=INFO");
    }
    env_logger::init().unwrap();

    let mut app = clap::App::new("breeze")
        .version(env!("CARGO_PKG_VERSION"))
        .about("SNES emulator")
        .arg(clap::Arg::with_name("rom")
            .required(true)
            .value_name("ROM_PATH")
            .takes_value(true)
            .help("The ROM file to execute"))
        .arg(clap::Arg::with_name("renderer")
            .short("R")
            .long("renderer")
            .takes_value(true)
            .help("The renderer to use"))
        .arg(clap::Arg::with_name("audio")
            .short("A")
            .long("audio")
            .takes_value(true)
            .help("The audio backend to use"))
        .arg(clap::Arg::with_name("savestate")
            .long("savestate")
            .takes_value(true)
            .help("The save state file to load"))
        .arg(clap::Arg::with_name("record")
            .long("record")
            .takes_value(true)
            .help("Record input to a text file"))
        .arg(clap::Arg::with_name("replay")
            .long("replay")
            .takes_value(true)
            .help("Replay a recording from a text file"));

    // Add debugging options
    if cfg!(debug_assertions) {
        app = app.arg(clap::Arg::with_name("oneframe")
            .long("oneframe")
            .help("Render a single frame, then pause"));
    }

    let args = app.get_matches();
    match process_args(&args) {
        Ok(()) => {},
        Err(e) => {
            // FIXME: Glium swallows useful information when using {} instead of {:?}
            // I should fix this upstream when I have time.
            debug!("error: {:?}", e);
            println!("error: {}", e);
            process::exit(1);
        }
    }
}
<|endoftext|>


<commit_before>use std::mem::transmute;

#[derive(Debug)]
pub enum Message {
    //peer messages according to protocol
    KeepAlive,
    Choke,
    Unchoke,
    Interested,
    NotInterested,
    Have {piece_index: u32},
    Bitfield(Vec<u8>),
    Request {index: u32, begin: u32, length: u32},
    Piece {index: u32, begin: u32, block: Vec<u8>},
    Cancel {index: u32, begin: u32, length: u32},
    Port(u16),
    //internally used messages
    Handshake,
    Unhandled,
}

//this is kind of lazy. but it also makes reading/writing Messages symmetrical...
impl Message {
    fn to_byte_array (&self) -> Vec<u8> {
        match self {
            &Message::KeepAlive => {
                vec![0, 0, 0, 0]
            }
            &Message::Choke => {
                vec![0, 0, 0, 1, 0]
            },
            &Message::Unchoke => {
                vec![0, 0, 0, 1, 1]
            },
            &Message::Interested => {
                vec![0, 0, 0, 1, 2]
            },
            &Message::Have{piece_index: p} => {
                let r: [u8; 4] = unsafe {transmute(p.to_be())};
                vec![0, 0, 0, 5, r[0], r[1], r[2], r[3]]
            },
            _ => {
                vec![]
            }

        }
    }
}

/// Tries to decode a message according to the bittorrent protocol from a slice of bytes
/// If it is not complete, it will return None
/// If successful it will return a tuple enveloping the message in deserialized form and the number
/// of bytes that it consumed. n.b. done immutably - the caller will need to advance the pointer
///
/// # Preconditions
/// ```assert(bytes.len() > 3)```
///
pub fn try_decode (bytes: &[u8]) -> Option<(Message, usize)> {
    //println!("tcp buffer: {:?}", bytes);
    //yes there are some magic numbers floating around in here... but they're byte manipulations
    let rest = &bytes[4..];
    match u8_4_to_u32(&bytes[0..4]) as usize {
        0 => Some((Message::KeepAlive, 4)),
        len => { //len is inclusive of the id byte
            let message_type = match rest.first() {
                None => return None,
                Some(a) => a
            };
            let message = match *message_type {
                _ if len > rest.len() => return None, //the entire envelope is not here yet
                0 => Message::Choke,
                1 => Message::Unchoke,
                2 => Message::Interested,
                3 => Message::NotInterested,
                4 => Message::Have{piece_index: u8_4_to_u32(&rest[1..5])},
                5 => Message::Bitfield((&rest[1..len as usize]).to_owned()),
                6 => {
                    let index = u8_4_to_u32(&rest[1..5]);
                    let begin = u8_4_to_u32(&rest[5..9]);
                    let length = u8_4_to_u32(&rest[9..13]);
                    Message::Request{index: index, begin: begin, length: length}
                },
                7 => {
                    let index = u8_4_to_u32(&rest[1..5]);
                    let begin = u8_4_to_u32(&rest[5..9]);
                    let block = (&rest[9..len as usize]).to_owned();
                    Message::Piece{index: index, begin: begin, block: block}
                },
                8  => {
                    let index = u8_4_to_u32(&rest[1..5]);
                    let begin = u8_4_to_u32(&rest[5..9]);
                    let length = u8_4_to_u32(&rest[9..13]);
                    Message::Cancel{index: index, begin: begin, length: length}
                },
                9 => Message::Port(u8_2_to_u16(&rest[1..3])),
                _ => return None
            };

            //successfully consume the len + the 4 byte length value
            Some((message, len + 4))

        }
    }
}

//this is relatively unsafe
fn u8_2_to_u16 (bytes: &[u8]) -> u16 {
    (bytes[1] as u16 | (bytes[0] as u16) << 8)
}

fn u8_4_to_u32 (bytes: &[u8]) -> u32 {
    (bytes[3] as u32
        | ((bytes[2] as u32) << 8)
        | ((bytes[1] as u32) << 16)
        | ((bytes[0] as u32) << 24))
}

#[test]
fn test_have_message () {
    let a_message = Message::Have{piece_index: 400};

    let a = a_message.to_byte_array();

    assert_eq!(a, vec![0, 0, 0, 5, 0, 0, 1, 144]);

}

/*#[test]
fn test_decode () {
    use std::io::{Result, Read};

    struct MockStream;

    impl Read for MockStream {
        fn read (&mut self, buf: &mut [u8]) -> Result<usize> {
            buf[0] = 0;
            buf[1] = 0;
            buf[2] = 0;
            buf[3] = 0;
            Ok(4)
        }
    }

    let mut stream = MockStream;
    let mut buf = [1; 4];
    stream.read(&mut buf);
    try_decode(&buf);
}*/
<commit_msg>fixing Message::Have serializez<commit_after>use std::mem::transmute;

#[derive(Debug, PartialEq, Eq)]
pub enum Message {
    //peer messages according to protocol
    KeepAlive,
    Choke,
    Unchoke,
    Interested,
    NotInterested,
    Have {piece_index: u32},
    Bitfield(Vec<u8>),
    Request {index: u32, begin: u32, length: u32},
    Piece {index: u32, begin: u32, block: Vec<u8>},
    Cancel {index: u32, begin: u32, length: u32},
    Port(u16),
    //internally used messages
    Handshake,
    Unhandled,
}

//this is kind of lazy. but it also makes reading/writing Messages symmetrical...
impl Message {
    fn to_byte_array (&self) -> Vec<u8> {
        match self {
            &Message::KeepAlive => {
                vec![0, 0, 0, 0]
            }
            &Message::Choke => {
                vec![0, 0, 0, 1, 0]
            },
            &Message::Unchoke => {
                vec![0, 0, 0, 1, 1]
            },
            &Message::Interested => {
                vec![0, 0, 0, 1, 2]
            },
            &Message::Have{piece_index: p} => {
                let r: [u8; 4] = unsafe {transmute(p.to_be())};
                vec![0, 0, 0, 5, 4, r[0], r[1], r[2], r[3]]
            },
            _ => {
                vec![]
            }

        }
    }
}

/// Tries to decode a message according to the bittorrent protocol from a slice of bytes
/// If it is not complete, it will return None
/// If successful it will return a tuple enveloping the message in deserialized form and the number
/// of bytes that it consumed. n.b. done immutably - the caller will need to advance the pointer
///
/// # Preconditions
/// ```assert(bytes.len() > 3)```
///
pub fn try_decode (bytes: &[u8]) -> Option<(Message, usize)> {
    //println!("tcp buffer: {:?}", bytes);
    //yes there are some magic numbers floating around in here... but they're byte manipulations
    let rest = &bytes[4..];
    match u8_4_to_u32(&bytes[0..4]) as usize {
        0 => Some((Message::KeepAlive, 4)),
        len => { //len is inclusive of the id byte
            let message_type = match rest.first() {
                None => return None,
                Some(a) => a
            };
            let message = match *message_type {
                _ if len > rest.len() => return None, //the entire envelope is not here yet
                0 => Message::Choke,
                1 => Message::Unchoke,
                2 => Message::Interested,
                3 => Message::NotInterested,
                4 => Message::Have{piece_index: u8_4_to_u32(&rest[1..5])},
                5 => Message::Bitfield((&rest[1..len as usize]).to_owned()),
                6 => {
                    let index = u8_4_to_u32(&rest[1..5]);
                    let begin = u8_4_to_u32(&rest[5..9]);
                    let length = u8_4_to_u32(&rest[9..13]);
                    Message::Request{index: index, begin: begin, length: length}
                },
                7 => {
                    let index = u8_4_to_u32(&rest[1..5]);
                    let begin = u8_4_to_u32(&rest[5..9]);
                    let block = (&rest[9..len as usize]).to_owned();
                    Message::Piece{index: index, begin: begin, block: block}
                },
                8  => {
                    let index = u8_4_to_u32(&rest[1..5]);
                    let begin = u8_4_to_u32(&rest[5..9]);
                    let length = u8_4_to_u32(&rest[9..13]);
                    Message::Cancel{index: index, begin: begin, length: length}
                },
                9 => Message::Port(u8_2_to_u16(&rest[1..3])),
                _ => return None
            };

            //successfully consume the len + the 4 byte length value
            Some((message, len + 4))

        }
    }
}

//this is relatively unsafe
fn u8_2_to_u16 (bytes: &[u8]) -> u16 {
    (bytes[1] as u16 | (bytes[0] as u16) << 8)
}

fn u8_4_to_u32 (bytes: &[u8]) -> u32 {
    (bytes[3] as u32
        | ((bytes[2] as u32) << 8)
        | ((bytes[1] as u32) << 16)
        | ((bytes[0] as u32) << 24))
}

#[test]
fn test_have_message () {
    let a_message = Message::Have{piece_index: 400};

    let a = a_message.to_byte_array();

    assert_eq!(a, vec![0, 0, 0, 5, 4, 0, 0, 1, 144]);

    let (message, _) = try_decode(&a).unwrap();

    assert_eq!(message, Message::Have{piece_index:400});

}

/*#[test]
fn test_decode () {
    use std::io::{Result, Read};

    struct MockStream;

    impl Read for MockStream {
        fn read (&mut self, buf: &mut [u8]) -> Result<usize> {
            buf[0] = 0;
            buf[1] = 0;
            buf[2] = 0;
            buf[3] = 0;
            Ok(4)
        }
    }

    let mut stream = MockStream;
    let mut buf = [1; 4];
    stream.read(&mut buf);
    try_decode(&buf);
}*/
<|endoftext|>


<commit_before>use nom::branch::alt;
use nom::bytes::complete::{tag, take, take_till1, take_while};
use nom::multi::many0;
use nom::IResult;
use std::cmp::Ordering;
use std::str;

/// Describes how formats should be padded: on the left, on the right, or not at all.
#[derive(PartialEq, Eq, Debug)]
pub enum Padding {
    /// Do not pad the value.
    None,
    /// Pad the value on the left until it reaches specified width.
    Left(usize),
    /// Pad the value on the right until it reaches specified width.
    Right(usize),
}

/// Describes all the different "format specifiers" we support, plus a chunk of text that would be
/// copied to the output verbatim.
#[derive(PartialEq, Eq, Debug)]
pub enum Specifier<'a> {
    /// Will expand to pad everything that comes next to the right. Given char is used for padding.
    Spacing(char),
    /// Will center the text in a given width
    Center(usize, char),
    /// A format to be replaced with a value (`%a`, `%t` etc.), padded to the given width on the
    /// left (if it's positive) or on the right (if it's negative).
    Format(char, Padding),
    /// A chunk of text that will be copied to the output verbatim.
    Text(&'a str),
    /// Conditional format that is replaced by one of the sub-formats depending on the value of the
    /// given key. "Else" branch might be missing.
    Conditional(char, Vec<Specifier<'a>>, Option<Vec<Specifier<'a>>>),
}

fn escaped_percent_sign(input: &str) -> IResult<&str, Specifier> {
    tag("%%")(input).map(|result| (result.0, Specifier::Text(&result.1[0..1])))
}

fn center(input: &str) -> IResult<&str, Specifier> {
    let (input, _) = tag("%=")(input)?;
    let (input, c) = take(1usize)(input)?;
    let (input, width) =
        take_while(|chr: char| chr.is_ascii() && (chr.is_numeric()))(input)?;

    let width: usize = width.parse::<usize>().unwrap_or(0);
    let chr: char = c.chars().next().unwrap();

    Ok((input, Specifier::Center(width, chr)))
}

fn spacing(input: &str) -> IResult<&str, Specifier> {
    let (input, _) = tag("%>")(input)?;
    let (input, c) = take(1usize)(input)?;

    // unwrap() won't panic because we use take!(1) in parser above
    let chr = c.chars().next().unwrap();

    Ok((input, Specifier::Spacing(chr)))
}

fn padded_format(input: &str) -> IResult<&str, Specifier> {
    let (input, _) = tag("%")(input)?;
    let (input, width) =
        take_while(|chr: char| chr.is_ascii() && (chr.is_numeric() || chr == '-'))(input)?;
    let (input, format) = take(1usize)(input)?;

    // unwrap() won't fail because parser uses take!(1) to get exactly one character
    let format = format.chars().next().unwrap();

    let width = width.parse::<isize>().unwrap_or(0);
    let padding = match width.cmp(&0isize) {
        Ordering::Equal => Padding::None,
        Ordering::Greater => Padding::Left(width.abs() as usize),
        Ordering::Less => Padding::Right(width.abs() as usize),
    };

    Ok((input, Specifier::Format(format, padding)))
}

fn text_outside_conditional(input: &str) -> IResult<&str, Specifier> {
    let (input, text) = take_till1(|chr: char| chr == '%')(input)?;

    Ok((input, Specifier::Text(text)))
}

fn text_inside_conditional(input: &str) -> IResult<&str, Specifier> {
    let (input, text) = take_till1(|chr: char| chr == '%' || chr == '&' || chr == '?')(input)?;

    Ok((input, Specifier::Text(text)))
}

fn conditional(input: &str) -> IResult<&str, Specifier> {
    // Prepared partial parsers
    let start_tag = tag("%?");
    let condition = take(1usize);
    let then_tag = tag("?");
    let then_branch = conditional_branch;
    let else_tag = tag("&");
    let end_tag = tag("?");

    let some_else_branch = |input| {
        let (input, _) = else_tag(input)?;
        let (input, els) = conditional_branch(input)?;
        let (input, _) = end_tag(input)?;
        Ok((input, Some(els)))
    };
    let none_else_branch = |input| {
        let (input, _) = end_tag(input)?;
        Ok((input, None))
    };
    let else_branch = alt((some_else_branch, none_else_branch));

    // Input parsing
    let (input, _) = start_tag(input)?;
    let (input, cond) = condition(input)?;
    let (input, _) = then_tag(input)?;
    let (input, then) = then_branch(input)?;
    let (input, els) = else_branch(input)?;

    // unwrap() won't panic because we're using take!(1) to get exactly one character
    let cond = cond.chars().next().unwrap();

    Ok((input, Specifier::Conditional(cond, then, els)))
}

fn conditional_branch(input: &str) -> IResult<&str, Vec<Specifier>> {
    let alternatives = (
        escaped_percent_sign,
        spacing,
        center,
        padded_format,
        text_inside_conditional,
    );
    many0(alt(alternatives))(input)
}

fn parser(input: &str) -> IResult<&str, Vec<Specifier>> {
    let alternatives = (
        conditional,
        escaped_percent_sign,
        spacing,
        center,
        padded_format,
        text_outside_conditional,
    );
    many0(alt(alternatives))(input)
}

fn sanitize(mut input: Vec<Specifier>) -> Vec<Specifier> {
    input.retain(|s| {
        if let Specifier::Format(c, ref _b) = *s {
            c.is_ascii()
        } else {
            true
        }
    });
    input
}

pub fn parse(input: &str) -> Vec<Specifier> {
    match parser(input) {
        Ok((_leftovers, ast)) => sanitize(ast),
        Err(_) => vec![Specifier::Text("")],
    }
}

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

    #[test]
    fn t_parses_formats_without_specifiers() {
        let input = "Hello, world!";
        let (leftovers, result) = parser(input).unwrap();
        assert_eq!(leftovers, "");
        assert_eq!(result, vec![Specifier::Text("Hello, world!")]);
    }

    #[test]
    fn t_replaces_double_percent_with_a_single_percent() {
        let input = "%%";
        let (leftovers, result) = parser(input).unwrap();
        assert_eq!(leftovers, "");
        assert_eq!(result, vec![Specifier::Text("%")]);
    }

    #[test]
    fn t_parses_sequences_of_specifiers() {
        let input = "100%% pure Ceylon tea";
        let (leftovers, result) = parser(input).unwrap();
        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Text("100"),
            Specifier::Text("%"),
            Specifier::Text(" pure Ceylon tea"),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_formats_with_letters() {
        let input = "%t (%a)";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Format('t', Padding::None),
            Specifier::Text(" ("),
            Specifier::Format('a', Padding::None),
            Specifier::Text(")"),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_formats_with_positive_padding() {
        let input = "%8a%4b%13x";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Format('a', Padding::Left(8)),
            Specifier::Format('b', Padding::Left(4)),
            Specifier::Format('x', Padding::Left(13)),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_formats_with_negative_padding() {
        let input = "%-8a%-4b%-13x";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Format('a', Padding::Right(8)),
            Specifier::Format('b', Padding::Right(4)),
            Specifier::Format('x', Padding::Right(13)),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_spacing_format() {
        let input = "%-8a%>m%4b%> %-13x";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Format('a', Padding::Right(8)),
            Specifier::Spacing('m'),
            Specifier::Format('b', Padding::Left(4)),
            Specifier::Spacing(' '),
            Specifier::Format('x', Padding::Right(13)),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals() {
        let input = "%?x?success&failure?";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional(
            'x',
            vec![Specifier::Text("success")],
            Some(vec![Specifier::Text("failure")]),
        )];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals_without_else_branch() {
        let input = "%?x?success?";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional(
            'x',
            vec![Specifier::Text("success")],
            None,
        )];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals_with_empty_then_branch() {
        let input = "%?x??";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional('x', vec![], None)];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals_with_empty_then_nonempty_else_branches() {
        let input = "%?x?&nonempty?";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional(
            'x',
            vec![],
            Some(vec![Specifier::Text("nonempty")]),
        )];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals_with_empty_then_and_else_branches() {
        let input = "%?x?&?";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional('x', vec![], Some(vec![]))];
        assert_eq!(result, expected);
    }
}
<commit_msg>Fix format<commit_after>use nom::branch::alt;
use nom::bytes::complete::{tag, take, take_till1, take_while};
use nom::multi::many0;
use nom::IResult;
use std::cmp::Ordering;
use std::str;

/// Describes how formats should be padded: on the left, on the right, or not at all.
#[derive(PartialEq, Eq, Debug)]
pub enum Padding {
    /// Do not pad the value.
    None,
    /// Pad the value on the left until it reaches specified width.
    Left(usize),
    /// Pad the value on the right until it reaches specified width.
    Right(usize),
}

/// Describes all the different "format specifiers" we support, plus a chunk of text that would be
/// copied to the output verbatim.
#[derive(PartialEq, Eq, Debug)]
pub enum Specifier<'a> {
    /// Will expand to pad everything that comes next to the right. Given char is used for padding.
    Spacing(char),
    /// Will center the text in a given width
    Center(usize, char),
    /// A format to be replaced with a value (`%a`, `%t` etc.), padded to the given width on the
    /// left (if it's positive) or on the right (if it's negative).
    Format(char, Padding),
    /// A chunk of text that will be copied to the output verbatim.
    Text(&'a str),
    /// Conditional format that is replaced by one of the sub-formats depending on the value of the
    /// given key. "Else" branch might be missing.
    Conditional(char, Vec<Specifier<'a>>, Option<Vec<Specifier<'a>>>),
}

fn escaped_percent_sign(input: &str) -> IResult<&str, Specifier> {
    tag("%%")(input).map(|result| (result.0, Specifier::Text(&result.1[0..1])))
}

fn center(input: &str) -> IResult<&str, Specifier> {
    let (input, _) = tag("%=")(input)?;
    let (input, c) = take(1usize)(input)?;
    let (input, width) = take_while(|chr: char| chr.is_ascii() && (chr.is_numeric()))(input)?;

    let width: usize = width.parse::<usize>().unwrap_or(0);
    let chr: char = c.chars().next().unwrap();

    Ok((input, Specifier::Center(width, chr)))
}

fn spacing(input: &str) -> IResult<&str, Specifier> {
    let (input, _) = tag("%>")(input)?;
    let (input, c) = take(1usize)(input)?;

    // unwrap() won't panic because we use take!(1) in parser above
    let chr = c.chars().next().unwrap();

    Ok((input, Specifier::Spacing(chr)))
}

fn padded_format(input: &str) -> IResult<&str, Specifier> {
    let (input, _) = tag("%")(input)?;
    let (input, width) =
        take_while(|chr: char| chr.is_ascii() && (chr.is_numeric() || chr == '-'))(input)?;
    let (input, format) = take(1usize)(input)?;

    // unwrap() won't fail because parser uses take!(1) to get exactly one character
    let format = format.chars().next().unwrap();

    let width = width.parse::<isize>().unwrap_or(0);
    let padding = match width.cmp(&0isize) {
        Ordering::Equal => Padding::None,
        Ordering::Greater => Padding::Left(width.abs() as usize),
        Ordering::Less => Padding::Right(width.abs() as usize),
    };

    Ok((input, Specifier::Format(format, padding)))
}

fn text_outside_conditional(input: &str) -> IResult<&str, Specifier> {
    let (input, text) = take_till1(|chr: char| chr == '%')(input)?;

    Ok((input, Specifier::Text(text)))
}

fn text_inside_conditional(input: &str) -> IResult<&str, Specifier> {
    let (input, text) = take_till1(|chr: char| chr == '%' || chr == '&' || chr == '?')(input)?;

    Ok((input, Specifier::Text(text)))
}

fn conditional(input: &str) -> IResult<&str, Specifier> {
    // Prepared partial parsers
    let start_tag = tag("%?");
    let condition = take(1usize);
    let then_tag = tag("?");
    let then_branch = conditional_branch;
    let else_tag = tag("&");
    let end_tag = tag("?");

    let some_else_branch = |input| {
        let (input, _) = else_tag(input)?;
        let (input, els) = conditional_branch(input)?;
        let (input, _) = end_tag(input)?;
        Ok((input, Some(els)))
    };
    let none_else_branch = |input| {
        let (input, _) = end_tag(input)?;
        Ok((input, None))
    };
    let else_branch = alt((some_else_branch, none_else_branch));

    // Input parsing
    let (input, _) = start_tag(input)?;
    let (input, cond) = condition(input)?;
    let (input, _) = then_tag(input)?;
    let (input, then) = then_branch(input)?;
    let (input, els) = else_branch(input)?;

    // unwrap() won't panic because we're using take!(1) to get exactly one character
    let cond = cond.chars().next().unwrap();

    Ok((input, Specifier::Conditional(cond, then, els)))
}

fn conditional_branch(input: &str) -> IResult<&str, Vec<Specifier>> {
    let alternatives = (
        escaped_percent_sign,
        spacing,
        center,
        padded_format,
        text_inside_conditional,
    );
    many0(alt(alternatives))(input)
}

fn parser(input: &str) -> IResult<&str, Vec<Specifier>> {
    let alternatives = (
        conditional,
        escaped_percent_sign,
        spacing,
        center,
        padded_format,
        text_outside_conditional,
    );
    many0(alt(alternatives))(input)
}

fn sanitize(mut input: Vec<Specifier>) -> Vec<Specifier> {
    input.retain(|s| {
        if let Specifier::Format(c, ref _b) = *s {
            c.is_ascii()
        } else {
            true
        }
    });
    input
}

pub fn parse(input: &str) -> Vec<Specifier> {
    match parser(input) {
        Ok((_leftovers, ast)) => sanitize(ast),
        Err(_) => vec![Specifier::Text("")],
    }
}

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

    #[test]
    fn t_parses_formats_without_specifiers() {
        let input = "Hello, world!";
        let (leftovers, result) = parser(input).unwrap();
        assert_eq!(leftovers, "");
        assert_eq!(result, vec![Specifier::Text("Hello, world!")]);
    }

    #[test]
    fn t_replaces_double_percent_with_a_single_percent() {
        let input = "%%";
        let (leftovers, result) = parser(input).unwrap();
        assert_eq!(leftovers, "");
        assert_eq!(result, vec![Specifier::Text("%")]);
    }

    #[test]
    fn t_parses_sequences_of_specifiers() {
        let input = "100%% pure Ceylon tea";
        let (leftovers, result) = parser(input).unwrap();
        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Text("100"),
            Specifier::Text("%"),
            Specifier::Text(" pure Ceylon tea"),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_formats_with_letters() {
        let input = "%t (%a)";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Format('t', Padding::None),
            Specifier::Text(" ("),
            Specifier::Format('a', Padding::None),
            Specifier::Text(")"),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_formats_with_positive_padding() {
        let input = "%8a%4b%13x";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Format('a', Padding::Left(8)),
            Specifier::Format('b', Padding::Left(4)),
            Specifier::Format('x', Padding::Left(13)),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_formats_with_negative_padding() {
        let input = "%-8a%-4b%-13x";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Format('a', Padding::Right(8)),
            Specifier::Format('b', Padding::Right(4)),
            Specifier::Format('x', Padding::Right(13)),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_spacing_format() {
        let input = "%-8a%>m%4b%> %-13x";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![
            Specifier::Format('a', Padding::Right(8)),
            Specifier::Spacing('m'),
            Specifier::Format('b', Padding::Left(4)),
            Specifier::Spacing(' '),
            Specifier::Format('x', Padding::Right(13)),
        ];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals() {
        let input = "%?x?success&failure?";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional(
            'x',
            vec![Specifier::Text("success")],
            Some(vec![Specifier::Text("failure")]),
        )];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals_without_else_branch() {
        let input = "%?x?success?";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional(
            'x',
            vec![Specifier::Text("success")],
            None,
        )];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals_with_empty_then_branch() {
        let input = "%?x??";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional('x', vec![], None)];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals_with_empty_then_nonempty_else_branches() {
        let input = "%?x?&nonempty?";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional(
            'x',
            vec![],
            Some(vec![Specifier::Text("nonempty")]),
        )];
        assert_eq!(result, expected);
    }

    #[test]
    fn t_parses_conditionals_with_empty_then_and_else_branches() {
        let input = "%?x?&?";
        let (leftovers, result) = parser(input).unwrap();

        assert_eq!(leftovers, "");

        let expected = vec![Specifier::Conditional('x', vec![], Some(vec![]))];
        assert_eq!(result, expected);
    }
}
<|endoftext|>


<commit_before>use crate::{utils::project_root, Result};
use anyhow::anyhow;
use std::{
    ffi::{OsStr, OsString},
    path::Path,
    process::{Command, Output, Stdio},
};

pub struct Cargo {
    inner: Command,
    pass_through_args: Vec<OsString>,
}

impl Cargo {
    pub fn new<S: AsRef<OsStr>>(command: S) -> Self {
        let mut inner = Command::new("cargo");
        inner.arg(command);
        Self {
            inner,
            pass_through_args: Vec::new(),
        }
    }

    pub fn all(&mut self) -> &mut Self {
        self.inner.arg("--all");
        self
    }

    pub fn all_features(&mut self) -> &mut Self {
        self.inner.arg("--all-features");
        self
    }

    pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
        self.inner.current_dir(dir);
        self
    }

    pub fn packages<I, S>(&mut self, packages: I) -> &mut Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        for p in packages {
            self.inner.arg("--package");
            self.inner.arg(p);
        }
        self
    }

    pub fn exclusions<I, S>(&mut self, exclusions: I) -> &mut Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        for e in exclusions {
            self.inner.arg("--exclude");
            self.inner.arg(e);
        }
        self
    }

    pub fn pass_through<I, S>(&mut self, args: I) -> &mut Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        for a in args {
            self.pass_through_args.push(a.as_ref().to_owned());
        }
        self
    }

    pub fn run(&mut self) -> Result<()> {
        self.inner.stdout(Stdio::inherit()).stderr(Stdio::inherit());
        self.do_run().map(|_| ())
    }

    pub fn run_with_output(&mut self) -> Result<Vec<u8>> {
        self.inner.stderr(Stdio::inherit());
        self.do_run().map(|o| o.stdout)
    }

    fn do_run(&mut self) -> Result<Output> {
        if !self.pass_through_args.is_empty() {
            self.inner.arg("--").args(&self.pass_through_args);
        }
        let output = self.inner.output()?;
        if !output.status.success() {
            return Err(anyhow!("failed to run cargo command"));
        }
        Ok(output)
    }
}

pub enum CargoCommand {
    Test,
}

impl CargoCommand {
    pub fn run_on_local_package(
        &self,
        all_features: bool,
        pass_through_args: &[OsString],
    ) -> Result<()> {
        let mut cargo = Cargo::new(self.as_str());
        if all_features {
            cargo.all_features();
        }
        cargo.pass_through(pass_through_args).run()
    }

    pub fn run_on_packages_separate<I, S>(
        &self,
        packages: I,
        pass_through_args: &[OsString],
    ) -> Result<()>
    where
        I: IntoIterator<Item = (S, bool)>,
        S: AsRef<OsStr>,
    {
        for (name, all_features) in packages {
            let mut cargo = Cargo::new(self.as_str());
            if all_features {
                cargo.all_features();
            }
            cargo
                .packages(&[name])
                .current_dir(project_root())
                .pass_through(pass_through_args)
                .run()?;
        }
        Ok(())
    }

    pub fn run_on_packages_together<I, S>(
        &self,
        packages: I,
        pass_through_args: &[OsString],
    ) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        Cargo::new(self.as_str())
            .current_dir(project_root())
            .all_features()
            .packages(packages)
            .pass_through(pass_through_args)
            .run()
    }

    pub fn run_with_exclusions<I, S>(
        &self,
        exclusions: I,
        pass_through_args: &[OsString],
    ) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        Cargo::new(self.as_str())
            .current_dir(project_root())
            .all()
            .all_features()
            .exclusions(exclusions)
            .pass_through(pass_through_args)
            .run()
    }

    pub fn as_str(&self) -> &'static str {
        match self {
            CargoCommand::Test => "test",
        }
    }
}
<commit_msg>[x] use --workspace instead of the deprecated --all<commit_after>use crate::{utils::project_root, Result};
use anyhow::anyhow;
use std::{
    ffi::{OsStr, OsString},
    path::Path,
    process::{Command, Output, Stdio},
};

pub struct Cargo {
    inner: Command,
    pass_through_args: Vec<OsString>,
}

impl Cargo {
    pub fn new<S: AsRef<OsStr>>(command: S) -> Self {
        let mut inner = Command::new("cargo");
        inner.arg(command);
        Self {
            inner,
            pass_through_args: Vec::new(),
        }
    }

    pub fn workspace(&mut self) -> &mut Self {
        self.inner.arg("--workspace");
        self
    }

    pub fn all_features(&mut self) -> &mut Self {
        self.inner.arg("--all-features");
        self
    }

    pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
        self.inner.current_dir(dir);
        self
    }

    pub fn packages<I, S>(&mut self, packages: I) -> &mut Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        for p in packages {
            self.inner.arg("--package");
            self.inner.arg(p);
        }
        self
    }

    pub fn exclusions<I, S>(&mut self, exclusions: I) -> &mut Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        for e in exclusions {
            self.inner.arg("--exclude");
            self.inner.arg(e);
        }
        self
    }

    pub fn pass_through<I, S>(&mut self, args: I) -> &mut Self
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        for a in args {
            self.pass_through_args.push(a.as_ref().to_owned());
        }
        self
    }

    pub fn run(&mut self) -> Result<()> {
        self.inner.stdout(Stdio::inherit()).stderr(Stdio::inherit());
        self.do_run().map(|_| ())
    }

    pub fn run_with_output(&mut self) -> Result<Vec<u8>> {
        self.inner.stderr(Stdio::inherit());
        self.do_run().map(|o| o.stdout)
    }

    fn do_run(&mut self) -> Result<Output> {
        if !self.pass_through_args.is_empty() {
            self.inner.arg("--").args(&self.pass_through_args);
        }
        let output = self.inner.output()?;
        if !output.status.success() {
            return Err(anyhow!("failed to run cargo command"));
        }
        Ok(output)
    }
}

pub enum CargoCommand {
    Test,
}

impl CargoCommand {
    pub fn run_on_local_package(
        &self,
        all_features: bool,
        pass_through_args: &[OsString],
    ) -> Result<()> {
        let mut cargo = Cargo::new(self.as_str());
        if all_features {
            cargo.all_features();
        }
        cargo.pass_through(pass_through_args).run()
    }

    pub fn run_on_packages_separate<I, S>(
        &self,
        packages: I,
        pass_through_args: &[OsString],
    ) -> Result<()>
    where
        I: IntoIterator<Item = (S, bool)>,
        S: AsRef<OsStr>,
    {
        for (name, all_features) in packages {
            let mut cargo = Cargo::new(self.as_str());
            if all_features {
                cargo.all_features();
            }
            cargo
                .packages(&[name])
                .current_dir(project_root())
                .pass_through(pass_through_args)
                .run()?;
        }
        Ok(())
    }

    pub fn run_on_packages_together<I, S>(
        &self,
        packages: I,
        pass_through_args: &[OsString],
    ) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        Cargo::new(self.as_str())
            .current_dir(project_root())
            .all_features()
            .packages(packages)
            .pass_through(pass_through_args)
            .run()
    }

    pub fn run_with_exclusions<I, S>(
        &self,
        exclusions: I,
        pass_through_args: &[OsString],
    ) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        Cargo::new(self.as_str())
            .current_dir(project_root())
            .workspace()
            .all_features()
            .exclusions(exclusions)
            .pass_through(pass_through_args)
            .run()
    }

    pub fn as_str(&self) -> &'static str {
        match self {
            CargoCommand::Test => "test",
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::str::FromStr;
use super::text::Reader;

pub type Price = u32;
pub type Time = u16;
pub type Shield = u16;

#[derive(Debug)]
pub struct Car {
    pub name: String,
    pub class: u8,
    pub price_total: Price,
    pub price_part: Price,
    pub size: [u8; 3],
    pub shield_regen: Shield,
    pub max_speed: u8,
    pub max_armor: u8,
    pub max_shield: Shield,
    //pub drop_energy: u16,
    //pub drop_time: Time,
    //pub max_fire: Time,
    //pub max_water: Time,
    pub max_oxygen: Time,
    //pub max_armor_destruction: u8,
    //pub max_damage: u8,
    pub spiral_capacity: u8,
}

impl FromStr for Car {
    type Err = ();
    fn from_str(s: &str) -> Result<Self, ()> {
        let mut items = s.split_whitespace();
        let name = items.next().unwrap().to_owned();
        let x: Vec<u32> = items.map(|i| i.parse().unwrap()).collect();
        assert_eq!(x.len(), 19);
        Ok(Car {
            name: name,
            class: x[0] as u8,
            price_total: x[1] as Price,
            price_part: x[2] as Price,
            size: [x[3] as u8, x[4] as u8, x[5] as u8],
            shield_regen: x[6] as Shield,
            max_speed: x[7] as u8,
            max_armor: x[8] as u8,
            max_shield: x[9] as Shield,
            //drop_energy: x[8] as u16,
            //drop_time: x[9] as Time,
            //max_fire: x[10] as Time,
            //max_water: x[11] as Time,
            max_oxygen: x[15] as Time,
            //max_armor_destruction: x[13] as u8,
            //max_damage: x[14] as u8,
            spiral_capacity: x[18] as u8,
        })
    }
}

#[derive(Debug)]
pub struct Registry {
    pub main: Vec<Car>,
    pub ruffa: Vec<Car>,
    pub constructor: Vec<Car>,
}

impl Registry {
    pub fn load(file: File) -> Registry {
        let mut reg = Registry {
            main: Vec::new(),
            ruffa: Vec::new(),
            constructor: Vec::new(),
        };
        let mut fi = Reader::new(file);

        assert_eq!(fi.next(), "uniVang-ParametersFile_Ver_1");

        let num_main: u8 = fi.next().parse().unwrap();
        let num_ruffa: u8 = fi.next().parse().unwrap();
        let num_const: u8 = fi.next().parse().unwrap();
        info!("Reading {} main vehicles, {} ruffas, and {} constructors",
            num_main, num_ruffa, num_const);

        for _ in 0 .. num_main {
            reg.main.push(fi.next().parse().unwrap());
        }
        for _ in 0 .. num_ruffa {
            reg.ruffa.push(fi.next().parse().unwrap());
        }
        for _ in 0 .. num_const {
            reg.constructor.push(fi.next().parse().unwrap());
        }

        reg
    }
}
<commit_msg>Refined cars parameters parsing<commit_after>use std::fs::File;
use std::str::FromStr;
use super::text::Reader;

pub type Price = u32;
pub type Time = u16;
pub type Shield = u16;

#[derive(Debug)]
pub struct Car {
    pub name: String,
    pub class: u8,
    pub price_buy: Price,
    pub price_sell: Price,
    pub size: [u8; 4],
    pub max_speed: u8,
    pub max_armor: u8,
    pub shield_max: Shield,
    pub shield_regen: Shield,
    pub shield_drop: Shield,
    pub drop_time: Time,
    pub max_fire: Time,
    pub max_water: Time,
    pub max_oxygen: Time,
    pub max_fly: Time,
    pub max_damage: u8,
    pub max_teleport: u8,
}

impl FromStr for Car {
    type Err = ();
    fn from_str(s: &str) -> Result<Self, ()> {
        let mut items = s.split_whitespace();
        let name = items.next().unwrap().to_owned();
        let x: Vec<u32> = items.map(|i| i.parse().unwrap()).collect();
        assert_eq!(x.len(), 19);
        Ok(Car {
            name: name,
            class: x[0] as u8,
            price_buy: x[1] as Price,
            price_sell: x[2] as Price,
            size: [x[3] as u8, x[4] as u8, x[5] as u8, x[6] as u8],
            max_speed: x[7] as u8,
            max_armor: x[8] as u8,
            shield_max: x[9] as Shield,
            shield_regen: x[10] as Shield,
            shield_drop: x[11] as Shield,
            drop_time: x[12] as Time,
            max_fire: x[13] as Time,
            max_water: x[14] as Time,
            max_oxygen: x[15] as Time,
            max_fly: x[16] as Time,
            max_damage: x[17] as u8,
            max_teleport: x[18] as u8,
        })
    }
}

#[derive(Debug)]
pub struct Registry {
    pub main: Vec<Car>,
    pub ruffa: Vec<Car>,
    pub constructor: Vec<Car>,
}

impl Registry {
    pub fn load(file: File) -> Registry {
        let mut reg = Registry {
            main: Vec::new(),
            ruffa: Vec::new(),
            constructor: Vec::new(),
        };
        let mut fi = Reader::new(file);

        assert_eq!(fi.next(), "uniVang-ParametersFile_Ver_1");

        let num_main: u8 = fi.next().parse().unwrap();
        let num_ruffa: u8 = fi.next().parse().unwrap();
        let num_const: u8 = fi.next().parse().unwrap();
        info!("Reading {} main vehicles, {} ruffas, and {} constructors",
            num_main, num_ruffa, num_const);

        for _ in 0 .. num_main {
            reg.main.push(fi.next().parse().unwrap());
        }
        for _ in 0 .. num_ruffa {
            reg.ruffa.push(fi.next().parse().unwrap());
        }
        for _ in 0 .. num_const {
            reg.constructor.push(fi.next().parse().unwrap());
        }

        reg
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use super::stream::Stream;

pub struct Database {
    tables: HashMap<String, Stream>
}

impl Database {
    pub fn new() -> Database {
        Database{
            tables: HashMap::new()
        }
    }

    fn create_stream(&mut self, name: &str)  {
        let tmp = Stream::new();
        self.tables.insert(name.to_string(), tmp);
    }

    fn get_table_mut(&mut self, name: &str) -> Option<&mut Stream> {
        self.tables.get_mut(name)
    }
}

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

    // returns a valid DB for use with testing with valid simple schema
    fn get_db_with_stream() {
        let mut db = Database::new();
        db.create_stream("Jon");

    }

    #[test]
    fn create_table() {
        let mut db = Database::new();
        db.create_stream("Jon");
    }

}
<commit_msg>creating a stream now returns a result<commit_after>use std::collections::HashMap;
use super::stream::Stream;

#[derive(Debug)]
pub enum DatabaseError {
    TableExists,
}

pub struct Database {
    tables: HashMap<String, Stream>
}

impl Database {
    pub fn new() -> Database {
        Database{
            tables: HashMap::new()
        }
    }

    fn create_stream(&mut self, name: &str) -> Result<(), DatabaseError> {
        let tmp = Stream::new();
        self.tables.insert(name.to_string(), tmp);
        Ok(())
    }

    fn get_table_mut(&mut self, name: &str) -> Option<&mut Stream> {
        self.tables.get_mut(name)
    }
}

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

    // returns a valid DB for use with testing with valid simple schema
    fn get_db_with_stream() {
        let mut db = Database::new();
        db.create_stream("Jon");

    }

    #[test]
    fn create_table() {
        let mut db = Database::new();
        db.create_stream("Jon");
    }

}
<|endoftext|>