<commit_before>//! Crate `ruma_api` contains core types used to define the requests and responses for each endpoint
//! in the various [Matrix](https://matrix.org) API specifications.
//! These types can be shared by client and server code for all Matrix APIs.
//!
//! When implementing a new Matrix API, each endpoint has a request type which implements
//! `Endpoint`, and a response type connected via an associated type.
//!
//! An implementation of `Endpoint` contains all the information about the HTTP method, the path and
//! input parameters for requests, and the structure of a successful response.
//! Such types can then be used by client code to make requests, and by server code to fulfill
//! those requests.

#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![warn(rust_2018_idioms)]
#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs)]

use std::{
    convert::{TryFrom, TryInto},
    error::Error as StdError,
};

use http::Method;

/// Generates a `ruma_api::Endpoint` from a concise definition.
///
/// The macro expects the following structure as input:
///
/// ```text
/// ruma_api! {
///     metadata: {
///         description: &'static str,
///         method: http::Method,
///         name: &'static str,
///         path: &'static str,
///         rate_limited: bool,
///         authentication: ruma_api::AuthScheme,
///     }
///
///     request: {
///         // Struct fields for each piece of data required
///         // to make a request to this API endpoint.
///     }
///
///     response: {
///         // Struct fields for each piece of data expected
///         // in the response from this API endpoint.
///     }
///
///     // The error returned when a response fails, defaults to `Void`.
///     error: path::to::Error
/// }
/// ```
///
/// This will generate a `ruma_api::Metadata` value to be used for the `ruma_api::Endpoint`'s
/// associated constant, single `Request` and `Response` structs, and the necessary trait
/// implementations to convert the request into a `http::Request` and to create a response from
/// a `http::Response` and vice versa.
///
/// The details of each of the three sections of the macros are documented below.
///
/// ## Metadata
///
/// * `description`: A short description of what the endpoint does.
/// * `method`: The HTTP method used for requests to the endpoint. It's not necessary to import
///   `http::Method`'s associated constants. Just write the value as if it was imported, e.g.
///   `GET`.
/// * `name`: A unique name for the endpoint. Generally this will be the same as the containing
///   module.
/// * `path`: The path component of the URL for the endpoint, e.g. "/foo/bar". Components of
///   the path that are parameterized can indicate a varible by using a Rust identifier
///   prefixed with a colon, e.g. `/foo/:some_parameter`. A corresponding query string
///   parameter will be expected in the request struct (see below for details).
/// * `rate_limited`: Whether or not the endpoint enforces rate limiting on requests.
/// * `authentication`: What authentication scheme the endpoint uses.
///
/// ## Request
///
/// The request block contains normal struct field definitions.
/// Doc comments and attributes are allowed as normal.
/// There are also a few special attributes available to control how the struct is converted
/// into a `http::Request`:
///
/// * `#[ruma_api(header = HEADER_NAME)]`: Fields with this attribute will be treated as HTTP
///   headers on the request. The value must implement `AsRef<str>`. Generally this is a
///   `String`. The attribute value shown above as `HEADER_NAME` must be a header name constant
///   from `http::header`, e.g. `CONTENT_TYPE`.
/// * `#[ruma_api(path)]`: Fields with this attribute will be inserted into the matching path
///   component of the request URL.
/// * `#[ruma_api(query)]`: Fields with this attribute will be inserting into the URL's query
///   string.
/// * `#[ruma_api(query_map)]`: Instead of individual query fields, one query_map field, of any
///   type that implements `IntoIterator<Item = (String, String)>` (e.g. `HashMap<String,
///   String>`, can be used for cases where an endpoint supports arbitrary query parameters.
///
/// Any field that does not include one of these attributes will be part of the request's JSON
/// body.
///
/// ## Response
///
/// Like the request block, the response block consists of normal struct field definitions.
/// Doc comments and attributes are allowed as normal.
/// There is also a special attribute available to control how the struct is created from a
/// `http::Request`:
///
/// * `#[ruma_api(header = HEADER_NAME)]`: Fields with this attribute will be treated as HTTP
///   headers on the response. The value must implement `AsRef<str>`. Generally this is a
///   `String`. The attribute value shown above as `HEADER_NAME` must be a header name constant
///   from `http::header`, e.g. `CONTENT_TYPE`.
///
/// Any field that does not include the above attribute will be expected in the response's JSON
/// body.
///
/// ## Newtype bodies
///
/// Both the request and response block also support "newtype bodies" by using the
/// `#[ruma_api(body)]` attribute on a field. If present on a field, the entire request or
/// response body will be treated as the value of the field. This allows you to treat the
/// entire request or response body as a specific type, rather than a JSON object with named
/// fields. Only one field in each struct can be marked with this attribute. It is an error to
/// have a newtype body field and normal body fields within the same struct.
///
/// There is another kind of newtype body that is enabled with `#[ruma_api(raw_body)]`. It is
/// used for endpoints in which the request or response body can be arbitrary bytes instead of
/// a JSON objects. A field with `#[ruma_api(raw_body)]` needs to have the type `Vec<u8>`.
///
/// # Examples
///
/// ```
/// pub mod some_endpoint {
///     use ruma_api_macros::ruma_api;
///
///     ruma_api! {
///         metadata: {
///             description: "Does something.",
///             method: POST,
///             name: "some_endpoint",
///             path: "/_matrix/some/endpoint/:baz",
///             rate_limited: false,
///             authentication: None,
///         }
///
///         request: {
///             pub foo: String,
///
///             #[ruma_api(header = CONTENT_TYPE)]
///             pub content_type: String,
///
///             #[ruma_api(query)]
///             pub bar: String,
///
///             #[ruma_api(path)]
///             pub baz: String,
///         }
///
///         response: {
///             #[ruma_api(header = CONTENT_TYPE)]
///             pub content_type: String,
///
///             pub value: String,
///         }
///     }
/// }
///
/// pub mod newtype_body_endpoint {
///     use ruma_api_macros::ruma_api;
///     use serde::{Deserialize, Serialize};
///
///     #[derive(Clone, Debug, Deserialize, Serialize)]
///     pub struct MyCustomType {
///         pub foo: String,
///     }
///
///     ruma_api! {
///         metadata: {
///             description: "Does something.",
///             method: PUT,
///             name: "newtype_body_endpoint",
///             path: "/_matrix/some/newtype/body/endpoint",
///             rate_limited: false,
///             authentication: None,
///         }
///
///         request: {
///             #[ruma_api(raw_body)]
///             pub file: Vec<u8>,
///         }
///
///         response: {
///             #[ruma_api(body)]
///             pub my_custom_type: MyCustomType,
///         }
///     }
/// }
/// ```
///
/// ## Fallible deserialization
///
/// All request and response types also derive [`Outgoing`][Outgoing]. As such, to allow
/// fallible deserialization, you can use the `#[wrap_incoming]` attribute. For details, see
/// the documentation for [the derive macro](derive.Outgoing.html).
// TODO: Explain the concept of fallible deserialization before jumping to
// `ruma_serde::Outgoing`
pub use ruma_api_macros::ruma_api;

pub mod error;
/// This module is used to support the generated code from ruma-api-macros.
/// It is not considered part of ruma-api's public API.
#[doc(hidden)]
pub mod exports {
    pub use http;
    pub use percent_encoding;
    pub use ruma_serde;
    pub use serde;
    pub use serde_json;
}

use error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError};

/// Gives users the ability to define their own serializable / deserializable errors.
pub trait EndpointError: StdError + Sized + 'static {
    /// Tries to construct `Self` from an `http::Response`.
    ///
    /// This will always return `Err` variant when no `error` field is defined in
    /// the `ruma_api` macro.
    fn try_from_response(
        response: http::Response<Vec<u8>>,
    ) -> Result<Self, error::ResponseDeserializationError>;
}

/// A request type for a Matrix API endpoint. (trait used for sending requests)
pub trait OutgoingRequest {
    /// A type capturing the expected error conditions the server can return.
    type EndpointError: EndpointError;

    /// Response type returned when the request is successful.
    type IncomingResponse: TryFrom<
        http::Response<Vec<u8>>,
        Error = FromHttpResponseError<Self::EndpointError>,
    >;

    /// Metadata about the endpoint.
    const METADATA: Metadata;

    /// Tries to convert this request into an `http::Request`.
    ///
    /// This method should only fail when called on endpoints that require authentication. It may
    /// also fail with a serialization error in case of bugs in Ruma though.
    ///
    /// The endpoints path will be appended to the given `base_url`, for example
    /// `https://matrix.org`. Since all paths begin with a slash, it is not necessary for the
    /// `base_url` to have a trailing slash. If it has one however, it will be ignored.
    fn try_into_http_request(
        self,
        base_url: &str,
        access_token: Option<&str>,
    ) -> Result<http::Request<Vec<u8>>, IntoHttpError>;
}

/// A request type for a Matrix API endpoint. (trait used for receiving requests)
pub trait IncomingRequest: TryFrom<http::Request<Vec<u8>>, Error = FromHttpRequestError> {
    /// A type capturing the error conditions that can be returned in the response.
    type EndpointError: EndpointError;

    /// Response type to return when the request is successful.
    type OutgoingResponse: TryInto<http::Response<Vec<u8>>, Error = IntoHttpError>;

    /// Metadata about the endpoint.
    const METADATA: Metadata;
}

/// Marker trait for requests that don't require authentication. (for the client side)
pub trait OutgoingNonAuthRequest: OutgoingRequest {}

/// Marker trait for requests that don't require authentication. (for the server side)
pub trait IncomingNonAuthRequest: IncomingRequest {}

/// Authentication scheme used by the endpoint.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AuthScheme {
    /// No authentication is performed.
    None,

    /// Authentication is performed by including an access token in the request headers.
    AccessToken,

    /// Authentication is performed by including X-Matrix signatures in the request headers,
    /// as defined in the federation API.
    ServerSignatures,

    /// Authentication is performed by including an access token in the query parameters.
    QueryOnlyAccessToken,
}

/// Metadata about an API endpoint.
#[derive(Clone, Debug)]
pub struct Metadata {
    /// A human-readable description of the endpoint.
    pub description: &'static str,

    /// The HTTP method used by this endpoint.
    pub method: Method,

    /// A unique identifier for this endpoint.
    pub name: &'static str,

    /// The path of this endpoint's URL, with variable names where path parameters should be filled
    /// in during a request.
    pub path: &'static str,

    /// Whether or not this endpoint is rate limited by the server.
    pub rate_limited: bool,

    /// What authentication scheme the server uses for this endpoint.
    pub authentication: AuthScheme,
}

#[doc(hidden)]
#[macro_export]
macro_rules! try_deserialize {
    ($kind:ident, $call:expr $(,)?) => {
        $crate::try_deserialize!(@$kind, $kind, $call)
    };
    (@request, $kind:ident, $call:expr) => {
        match $call {
            Ok(val) => val,
            Err(err) => return Err($crate::error::RequestDeserializationError::new(err, $kind).into()),
        }
    };
    (@response, $kind:ident, $call:expr) => {
        match $call {
            Ok(val) => val,
            Err(err) => return Err($crate::error::ResponseDeserializationError::new(err, $kind).into()),
        }
    };
}
<commit_msg>api: Improve docs for AuthScheme variants<commit_after>//! Crate `ruma_api` contains core types used to define the requests and responses for each endpoint
//! in the various [Matrix](https://matrix.org) API specifications.
//! These types can be shared by client and server code for all Matrix APIs.
//!
//! When implementing a new Matrix API, each endpoint has a request type which implements
//! `Endpoint`, and a response type connected via an associated type.
//!
//! An implementation of `Endpoint` contains all the information about the HTTP method, the path and
//! input parameters for requests, and the structure of a successful response.
//! Such types can then be used by client code to make requests, and by server code to fulfill
//! those requests.

#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![warn(rust_2018_idioms)]
#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs)]

use std::{
    convert::{TryFrom, TryInto},
    error::Error as StdError,
};

use http::Method;

/// Generates a `ruma_api::Endpoint` from a concise definition.
///
/// The macro expects the following structure as input:
///
/// ```text
/// ruma_api! {
///     metadata: {
///         description: &'static str,
///         method: http::Method,
///         name: &'static str,
///         path: &'static str,
///         rate_limited: bool,
///         authentication: ruma_api::AuthScheme,
///     }
///
///     request: {
///         // Struct fields for each piece of data required
///         // to make a request to this API endpoint.
///     }
///
///     response: {
///         // Struct fields for each piece of data expected
///         // in the response from this API endpoint.
///     }
///
///     // The error returned when a response fails, defaults to `Void`.
///     error: path::to::Error
/// }
/// ```
///
/// This will generate a `ruma_api::Metadata` value to be used for the `ruma_api::Endpoint`'s
/// associated constant, single `Request` and `Response` structs, and the necessary trait
/// implementations to convert the request into a `http::Request` and to create a response from
/// a `http::Response` and vice versa.
///
/// The details of each of the three sections of the macros are documented below.
///
/// ## Metadata
///
/// * `description`: A short description of what the endpoint does.
/// * `method`: The HTTP method used for requests to the endpoint. It's not necessary to import
///   `http::Method`'s associated constants. Just write the value as if it was imported, e.g.
///   `GET`.
/// * `name`: A unique name for the endpoint. Generally this will be the same as the containing
///   module.
/// * `path`: The path component of the URL for the endpoint, e.g. "/foo/bar". Components of
///   the path that are parameterized can indicate a varible by using a Rust identifier
///   prefixed with a colon, e.g. `/foo/:some_parameter`. A corresponding query string
///   parameter will be expected in the request struct (see below for details).
/// * `rate_limited`: Whether or not the endpoint enforces rate limiting on requests.
/// * `authentication`: What authentication scheme the endpoint uses.
///
/// ## Request
///
/// The request block contains normal struct field definitions.
/// Doc comments and attributes are allowed as normal.
/// There are also a few special attributes available to control how the struct is converted
/// into a `http::Request`:
///
/// * `#[ruma_api(header = HEADER_NAME)]`: Fields with this attribute will be treated as HTTP
///   headers on the request. The value must implement `AsRef<str>`. Generally this is a
///   `String`. The attribute value shown above as `HEADER_NAME` must be a header name constant
///   from `http::header`, e.g. `CONTENT_TYPE`.
/// * `#[ruma_api(path)]`: Fields with this attribute will be inserted into the matching path
///   component of the request URL.
/// * `#[ruma_api(query)]`: Fields with this attribute will be inserting into the URL's query
///   string.
/// * `#[ruma_api(query_map)]`: Instead of individual query fields, one query_map field, of any
///   type that implements `IntoIterator<Item = (String, String)>` (e.g. `HashMap<String,
///   String>`, can be used for cases where an endpoint supports arbitrary query parameters.
///
/// Any field that does not include one of these attributes will be part of the request's JSON
/// body.
///
/// ## Response
///
/// Like the request block, the response block consists of normal struct field definitions.
/// Doc comments and attributes are allowed as normal.
/// There is also a special attribute available to control how the struct is created from a
/// `http::Request`:
///
/// * `#[ruma_api(header = HEADER_NAME)]`: Fields with this attribute will be treated as HTTP
///   headers on the response. The value must implement `AsRef<str>`. Generally this is a
///   `String`. The attribute value shown above as `HEADER_NAME` must be a header name constant
///   from `http::header`, e.g. `CONTENT_TYPE`.
///
/// Any field that does not include the above attribute will be expected in the response's JSON
/// body.
///
/// ## Newtype bodies
///
/// Both the request and response block also support "newtype bodies" by using the
/// `#[ruma_api(body)]` attribute on a field. If present on a field, the entire request or
/// response body will be treated as the value of the field. This allows you to treat the
/// entire request or response body as a specific type, rather than a JSON object with named
/// fields. Only one field in each struct can be marked with this attribute. It is an error to
/// have a newtype body field and normal body fields within the same struct.
///
/// There is another kind of newtype body that is enabled with `#[ruma_api(raw_body)]`. It is
/// used for endpoints in which the request or response body can be arbitrary bytes instead of
/// a JSON objects. A field with `#[ruma_api(raw_body)]` needs to have the type `Vec<u8>`.
///
/// # Examples
///
/// ```
/// pub mod some_endpoint {
///     use ruma_api_macros::ruma_api;
///
///     ruma_api! {
///         metadata: {
///             description: "Does something.",
///             method: POST,
///             name: "some_endpoint",
///             path: "/_matrix/some/endpoint/:baz",
///             rate_limited: false,
///             authentication: None,
///         }
///
///         request: {
///             pub foo: String,
///
///             #[ruma_api(header = CONTENT_TYPE)]
///             pub content_type: String,
///
///             #[ruma_api(query)]
///             pub bar: String,
///
///             #[ruma_api(path)]
///             pub baz: String,
///         }
///
///         response: {
///             #[ruma_api(header = CONTENT_TYPE)]
///             pub content_type: String,
///
///             pub value: String,
///         }
///     }
/// }
///
/// pub mod newtype_body_endpoint {
///     use ruma_api_macros::ruma_api;
///     use serde::{Deserialize, Serialize};
///
///     #[derive(Clone, Debug, Deserialize, Serialize)]
///     pub struct MyCustomType {
///         pub foo: String,
///     }
///
///     ruma_api! {
///         metadata: {
///             description: "Does something.",
///             method: PUT,
///             name: "newtype_body_endpoint",
///             path: "/_matrix/some/newtype/body/endpoint",
///             rate_limited: false,
///             authentication: None,
///         }
///
///         request: {
///             #[ruma_api(raw_body)]
///             pub file: Vec<u8>,
///         }
///
///         response: {
///             #[ruma_api(body)]
///             pub my_custom_type: MyCustomType,
///         }
///     }
/// }
/// ```
///
/// ## Fallible deserialization
///
/// All request and response types also derive [`Outgoing`][Outgoing]. As such, to allow
/// fallible deserialization, you can use the `#[wrap_incoming]` attribute. For details, see
/// the documentation for [the derive macro](derive.Outgoing.html).
// TODO: Explain the concept of fallible deserialization before jumping to
// `ruma_serde::Outgoing`
pub use ruma_api_macros::ruma_api;

pub mod error;
/// This module is used to support the generated code from ruma-api-macros.
/// It is not considered part of ruma-api's public API.
#[doc(hidden)]
pub mod exports {
    pub use http;
    pub use percent_encoding;
    pub use ruma_serde;
    pub use serde;
    pub use serde_json;
}

use error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError};

/// Gives users the ability to define their own serializable / deserializable errors.
pub trait EndpointError: StdError + Sized + 'static {
    /// Tries to construct `Self` from an `http::Response`.
    ///
    /// This will always return `Err` variant when no `error` field is defined in
    /// the `ruma_api` macro.
    fn try_from_response(
        response: http::Response<Vec<u8>>,
    ) -> Result<Self, error::ResponseDeserializationError>;
}

/// A request type for a Matrix API endpoint. (trait used for sending requests)
pub trait OutgoingRequest {
    /// A type capturing the expected error conditions the server can return.
    type EndpointError: EndpointError;

    /// Response type returned when the request is successful.
    type IncomingResponse: TryFrom<
        http::Response<Vec<u8>>,
        Error = FromHttpResponseError<Self::EndpointError>,
    >;

    /// Metadata about the endpoint.
    const METADATA: Metadata;

    /// Tries to convert this request into an `http::Request`.
    ///
    /// This method should only fail when called on endpoints that require authentication. It may
    /// also fail with a serialization error in case of bugs in Ruma though.
    ///
    /// The endpoints path will be appended to the given `base_url`, for example
    /// `https://matrix.org`. Since all paths begin with a slash, it is not necessary for the
    /// `base_url` to have a trailing slash. If it has one however, it will be ignored.
    fn try_into_http_request(
        self,
        base_url: &str,
        access_token: Option<&str>,
    ) -> Result<http::Request<Vec<u8>>, IntoHttpError>;
}

/// A request type for a Matrix API endpoint. (trait used for receiving requests)
pub trait IncomingRequest: TryFrom<http::Request<Vec<u8>>, Error = FromHttpRequestError> {
    /// A type capturing the error conditions that can be returned in the response.
    type EndpointError: EndpointError;

    /// Response type to return when the request is successful.
    type OutgoingResponse: TryInto<http::Response<Vec<u8>>, Error = IntoHttpError>;

    /// Metadata about the endpoint.
    const METADATA: Metadata;
}

/// Marker trait for requests that don't require authentication. (for the client side)
pub trait OutgoingNonAuthRequest: OutgoingRequest {}

/// Marker trait for requests that don't require authentication. (for the server side)
pub trait IncomingNonAuthRequest: IncomingRequest {}

/// Authentication scheme used by the endpoint.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AuthScheme {
    /// No authentication is performed.
    None,

    /// Authentication is performed by including an access token in the `Authentication` http
    /// header, or an `access_token` query parameter.
    ///
    /// It is recommended to use the header over the query parameter.
    AccessToken,

    /// Authentication is performed by including X-Matrix signatures in the request headers,
    /// as defined in the federation API.
    ServerSignatures,

    /// Authentication is performed by setting the `access_token` query parameter.
    QueryOnlyAccessToken,
}

/// Metadata about an API endpoint.
#[derive(Clone, Debug)]
pub struct Metadata {
    /// A human-readable description of the endpoint.
    pub description: &'static str,

    /// The HTTP method used by this endpoint.
    pub method: Method,

    /// A unique identifier for this endpoint.
    pub name: &'static str,

    /// The path of this endpoint's URL, with variable names where path parameters should be filled
    /// in during a request.
    pub path: &'static str,

    /// Whether or not this endpoint is rate limited by the server.
    pub rate_limited: bool,

    /// What authentication scheme the server uses for this endpoint.
    pub authentication: AuthScheme,
}

#[doc(hidden)]
#[macro_export]
macro_rules! try_deserialize {
    ($kind:ident, $call:expr $(,)?) => {
        $crate::try_deserialize!(@$kind, $kind, $call)
    };
    (@request, $kind:ident, $call:expr) => {
        match $call {
            Ok(val) => val,
            Err(err) => return Err($crate::error::RequestDeserializationError::new(err, $kind).into()),
        }
    };
    (@response, $kind:ident, $call:expr) => {
        match $call {
            Ok(val) => val,
            Err(err) => return Err($crate::error::ResponseDeserializationError::new(err, $kind).into()),
        }
    };
}
<|endoftext|>


<commit_before>use crate::plugins;
use anyhow::{anyhow, bail, Result};
use chrono::{DateTime, Duration, Utc};
use std::{fs, thread};

/// Upgrade the application
///
/// Checks for a higher version on [GitHub releases](https://github.com/stencila/stencila/releases)
/// and downloads the binary for the current platform if one is found.
///
/// Optionally checks for new versions and, upgrades if necessary, all installed plugins.
/// See `plugins::upgrade_list` to only upgrade certain plugins.
///
/// # Arguments
///
/// - `current_version`: The current version (used mainly for testing)
/// - `wanted_version`: The version that is wanted (other than latest)
/// - `include_plugins`: Whether to upgrade installed plugins to their latest version
/// - `confirm`: Prompt the user to confirm an upgrade
/// - `verbose`: Print information on the upgrade process
pub async fn upgrade(
    current_version: Option<String>,
    wanted_version: Option<String>,
    include_plugins: bool,
    confirm: bool,
    verbose: bool,
    plugins: &mut plugins::Plugins,
) -> Result<()> {
    let mut builder = self_update::backends::github::Update::configure();
    builder
        .repo_owner("stencila")
        .repo_name("stencila")
        .bin_name("stencila")
        .current_version(
            current_version
                .unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string())
                .as_str(),
        )
        .no_confirm(!confirm)
        .show_output(verbose)
        .show_download_progress(verbose);

    if let Some(version) = wanted_version {
        builder.target_version_tag(format!("v{}", version).as_str());
    }

    // The actual upgrade is run in a separate thread because `self_update`
    // creates a new `tokio` runtime (which can not be nested within our main `tokio` runtime).
    thread::spawn(move || -> Result<()> {
        // Fail silently unless `verbose` is true.
        match builder.build()?.update() {
            Ok(_status) => Ok(()),
            Err(error) => {
                let message = error.to_string();
                if !message.contains("Update aborted") && verbose {
                    bail!("Error attempting to upgrade: {}", message)
                } else {
                    Ok(())
                }
            }
        }
    })
    .join()
    .map_err(|_| anyhow!("Error joining thread"))??;

    if include_plugins {
        plugins::Plugin::upgrade_all(plugins).await?;
    }

    Ok(())
}

const UPGRADE_FILE: &str = "cli-upgrade.txt";

/// Do a upgrade check automatically if not within the configured interval
/// since the last check.
///
/// Runs in a separate thread so that is does not slow down the
/// command currently being run by the user.
///
/// Note that the in-memory state of application and plugins is unchanged after this call
/// A restart is required to upload both the new version and plugin versions.
///
/// Because this function use values form the config file, requires
/// that `feature = "config"` is enabled.
#[cfg(feature = "config")]
pub fn upgrade_auto(
    config: &config::Config,
    plugins: &plugins::Plugins,
) -> std::thread::JoinHandle<Result<()>> {
    let config = config.clone();
    let mut plugins = plugins.clone();
    thread::spawn(move || -> Result<()> {
        let config::Config {
            auto,
            confirm,
            plugins: include_plugins,
            ..
        } = config;

        // Go no further if auto upgrade is not enabled
        if auto == "off" {
            return Ok(());
        }

        // Check if within the time since the last check was done
        let upgrade_file = crate::util::dirs::config(true)?.join(UPGRADE_FILE);
        let last = match fs::read_to_string(upgrade_file.clone()) {
            Ok(date) => DateTime::parse_from_rfc3339(date.as_str())?.with_timezone(&Utc),
            Err(_) => Utc::now(),
        };
        let duration = Duration::from_std(humantime::parse_duration(auto.as_str())?)?;
        if Utc::now() < last + duration {
            return Ok(());
        }

        // Attempt an upgrade
        let runtime = tokio::runtime::Runtime::new()?;
        runtime.block_on(async move {
            upgrade(None, None, include_plugins, confirm, false, &mut plugins).await
        })?;

        // Record the time of the upgrade check, so another check
        // is not made within the `auto`.
        let now = Utc::now();
        fs::write(upgrade_file, now.to_rfc3339())?;

        Ok(())
    })
}

#[cfg(feature = "config")]
pub mod config {
    use defaults::Defaults;
    use serde::{Deserialize, Serialize};
    use validator::{Validate, ValidationError};

    #[derive(Debug, Defaults, PartialEq, Clone, Deserialize, Serialize, Validate)]
    #[serde(default)]
    pub struct Config {
        /// Plugins should also be upgraded to latest version
        #[def = "true"]
        pub plugins: bool,

        /// Prompt the user to confirm an upgrade
        #[def = "true"]
        pub confirm: bool,

        /// Print information on the upgrade process
        #[def = "false"]
        pub verbose: bool,

        /// The interval between automatic upgrade checks (defaults to "1 day").
        /// Only used when for configuration. Set to "off" for no automatic checks.
        #[def = "\"1 day\".to_string()"]
        #[validate(
            length(min = 2),
            custom(function = "validate_auto", message = "Not a valid duration")
        )]
        pub auto: String,
    }

    /// Validate `auto` (a valid duration or "off")
    pub fn validate_auto(value: &str) -> Result<(), ValidationError> {
        if value == "off" {
            return Ok(());
        }
        if humantime::parse_duration(value).is_err() {
            return Err(ValidationError::new("invalid_duration_string"));
        }
        Ok(())
    }
}

#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Upgrade to the latest (or other) version",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Args {
        /// Version to upgrade (or downgrade) to (defaults to the latest)
        #[structopt(short, long)]
        pub to: Option<String>,

        /// Plugins should also be upgraded to their latest version
        #[structopt(short, long)]
        pub plugins: bool,

        /// The user should be asked to confirm an upgrade
        #[structopt(short, long)]
        pub confirm: bool,

        /// Print information on the upgrade process
        #[structopt(short, long)]
        pub verbose: bool,
    }

    /// Run the upgrade.
    ///
    /// Note that the in-memory state of application and plugins is unchanged after this call
    /// (e.g. if called in interactive mode). A restart is required to upload both the new
    /// version and plugin versions.
    pub async fn run(
        args: Args,
        config: &config::Config,
        plugins: &mut plugins::Plugins,
    ) -> Result<()> {
        let Args {
            to,
            plugins: include_plugins,
            confirm,
            verbose,
            ..
        } = args;

        let include_plugins = include_plugins || config.plugins;
        let confirm = confirm || config.confirm;
        let verbose = verbose || config.verbose;

        upgrade(None, to, include_plugins, confirm, verbose, plugins).await
    }
}

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

    // Note that these tests are a little funky in that they make requests to the
    // GitHub API. They currently also have few assertions.
    // They use an artificially high `current_version` to avoid any binaries
    // from being downloaded.

    #[tokio::test]
    async fn test_upgrade() -> Result<()> {
        let mut plugins = plugins::Plugins::empty();
        upgrade(
            Some("100.0.0".to_string()),
            None,
            false,
            false,
            false,
            &mut plugins,
        )
        .await
    }

    #[test]
    fn test_upgrade_auto() -> Result<()> {
        let config = config::Config::default();
        let mut plugins = plugins::Plugins::empty();
        upgrade_auto(&config, &mut plugins).join().expect("Failed")
    }

    #[tokio::test]
    async fn test_cli() -> Result<()> {
        let config = config::Config::default();
        let mut plugins = plugins::Plugins::empty();
        cli::run(
            cli::Args {
                to: None,
                plugins: false,
                confirm: false,
                verbose: false,
            },
            &config,
            &mut plugins,
        )
        .await
    }

    #[test]
    fn test_validate_auto() {
        assert_eq!(config::validate_auto("off"), Ok(()));
        assert_eq!(config::validate_auto("1 day"), Ok(()));
        assert_eq!(config::validate_auto("2 weeks 3 days 1 hr"), Ok(()));

        assert_eq!(
            config::validate_auto("foo").unwrap_err().to_string(),
            "Validation error: invalid_duration_string [{}]".to_string()
        );
    }
}
<commit_msg>test(Upgrade): Skip test<commit_after>use crate::plugins;
use anyhow::{anyhow, bail, Result};
use chrono::{DateTime, Duration, Utc};
use std::{fs, thread};

/// Upgrade the application
///
/// Checks for a higher version on [GitHub releases](https://github.com/stencila/stencila/releases)
/// and downloads the binary for the current platform if one is found.
///
/// Optionally checks for new versions and, upgrades if necessary, all installed plugins.
/// See `plugins::upgrade_list` to only upgrade certain plugins.
///
/// # Arguments
///
/// - `current_version`: The current version (used mainly for testing)
/// - `wanted_version`: The version that is wanted (other than latest)
/// - `include_plugins`: Whether to upgrade installed plugins to their latest version
/// - `confirm`: Prompt the user to confirm an upgrade
/// - `verbose`: Print information on the upgrade process
pub async fn upgrade(
    current_version: Option<String>,
    wanted_version: Option<String>,
    include_plugins: bool,
    confirm: bool,
    verbose: bool,
    plugins: &mut plugins::Plugins,
) -> Result<()> {
    let mut builder = self_update::backends::github::Update::configure();
    builder
        .repo_owner("stencila")
        .repo_name("stencila")
        .bin_name("stencila")
        .current_version(
            current_version
                .unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string())
                .as_str(),
        )
        .no_confirm(!confirm)
        .show_output(verbose)
        .show_download_progress(verbose);

    if let Some(version) = wanted_version {
        builder.target_version_tag(format!("v{}", version).as_str());
    }

    // The actual upgrade is run in a separate thread because `self_update`
    // creates a new `tokio` runtime (which can not be nested within our main `tokio` runtime).
    thread::spawn(move || -> Result<()> {
        // Fail silently unless `verbose` is true.
        match builder.build()?.update() {
            Ok(_status) => Ok(()),
            Err(error) => {
                let message = error.to_string();
                if !message.contains("Update aborted") && verbose {
                    bail!("Error attempting to upgrade: {}", message)
                } else {
                    Ok(())
                }
            }
        }
    })
    .join()
    .map_err(|_| anyhow!("Error joining thread"))??;

    if include_plugins {
        plugins::Plugin::upgrade_all(plugins).await?;
    }

    Ok(())
}

const UPGRADE_FILE: &str = "cli-upgrade.txt";

/// Do a upgrade check automatically if not within the configured interval
/// since the last check.
///
/// Runs in a separate thread so that is does not slow down the
/// command currently being run by the user.
///
/// Note that the in-memory state of application and plugins is unchanged after this call
/// A restart is required to upload both the new version and plugin versions.
///
/// Because this function use values form the config file, requires
/// that `feature = "config"` is enabled.
#[cfg(feature = "config")]
pub fn upgrade_auto(
    config: &config::Config,
    plugins: &plugins::Plugins,
) -> std::thread::JoinHandle<Result<()>> {
    let config = config.clone();
    let mut plugins = plugins.clone();
    thread::spawn(move || -> Result<()> {
        let config::Config {
            auto,
            confirm,
            plugins: include_plugins,
            ..
        } = config;

        // Go no further if auto upgrade is not enabled
        if auto == "off" {
            return Ok(());
        }

        // Check if within the time since the last check was done
        let upgrade_file = crate::util::dirs::config(true)?.join(UPGRADE_FILE);
        let last = match fs::read_to_string(upgrade_file.clone()) {
            Ok(date) => DateTime::parse_from_rfc3339(date.as_str())?.with_timezone(&Utc),
            Err(_) => Utc::now(),
        };
        let duration = Duration::from_std(humantime::parse_duration(auto.as_str())?)?;
        if Utc::now() < last + duration {
            return Ok(());
        }

        // Attempt an upgrade
        let runtime = tokio::runtime::Runtime::new()?;
        runtime.block_on(async move {
            upgrade(None, None, include_plugins, confirm, false, &mut plugins).await
        })?;

        // Record the time of the upgrade check, so another check
        // is not made within the `auto`.
        let now = Utc::now();
        fs::write(upgrade_file, now.to_rfc3339())?;

        Ok(())
    })
}

#[cfg(feature = "config")]
pub mod config {
    use defaults::Defaults;
    use serde::{Deserialize, Serialize};
    use validator::{Validate, ValidationError};

    #[derive(Debug, Defaults, PartialEq, Clone, Deserialize, Serialize, Validate)]
    #[serde(default)]
    pub struct Config {
        /// Plugins should also be upgraded to latest version
        #[def = "true"]
        pub plugins: bool,

        /// Prompt the user to confirm an upgrade
        #[def = "true"]
        pub confirm: bool,

        /// Print information on the upgrade process
        #[def = "false"]
        pub verbose: bool,

        /// The interval between automatic upgrade checks (defaults to "1 day").
        /// Only used when for configuration. Set to "off" for no automatic checks.
        #[def = "\"1 day\".to_string()"]
        #[validate(
            length(min = 2),
            custom(function = "validate_auto", message = "Not a valid duration")
        )]
        pub auto: String,
    }

    /// Validate `auto` (a valid duration or "off")
    pub fn validate_auto(value: &str) -> Result<(), ValidationError> {
        if value == "off" {
            return Ok(());
        }
        if humantime::parse_duration(value).is_err() {
            return Err(ValidationError::new("invalid_duration_string"));
        }
        Ok(())
    }
}

#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Upgrade to the latest (or other) version",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Args {
        /// Version to upgrade (or downgrade) to (defaults to the latest)
        #[structopt(short, long)]
        pub to: Option<String>,

        /// Plugins should also be upgraded to their latest version
        #[structopt(short, long)]
        pub plugins: bool,

        /// The user should be asked to confirm an upgrade
        #[structopt(short, long)]
        pub confirm: bool,

        /// Print information on the upgrade process
        #[structopt(short, long)]
        pub verbose: bool,
    }

    /// Run the upgrade.
    ///
    /// Note that the in-memory state of application and plugins is unchanged after this call
    /// (e.g. if called in interactive mode). A restart is required to upload both the new
    /// version and plugin versions.
    pub async fn run(
        args: Args,
        config: &config::Config,
        plugins: &mut plugins::Plugins,
    ) -> Result<()> {
        let Args {
            to,
            plugins: include_plugins,
            confirm,
            verbose,
            ..
        } = args;

        let include_plugins = include_plugins || config.plugins;
        let confirm = confirm || config.confirm;
        let verbose = verbose || config.verbose;

        upgrade(None, to, include_plugins, confirm, verbose, plugins).await
    }
}

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

    // Note that these tests are a little funky in that they make requests to the
    // GitHub API. They currently also have few assertions.
    // They use an artificially high `current_version` to avoid any binaries
    // from being downloaded.

    #[tokio::test]
    async fn test_upgrade() -> Result<()> {
        let mut plugins = plugins::Plugins::empty();
        upgrade(
            Some("100.0.0".to_string()),
            None,
            false,
            false,
            false,
            &mut plugins,
        )
        .await
    }

    #[test]
    fn test_upgrade_auto() -> Result<()> {
        let config = config::Config::default();
        let mut plugins = plugins::Plugins::empty();
        upgrade_auto(&config, &mut plugins).join().expect("Failed")
    }

    /// This hangs so is currently ignored
    #[ignore]
    #[tokio::test]
    async fn test_cli() -> Result<()> {
        let config = config::Config::default();
        let mut plugins = plugins::Plugins::empty();
        cli::run(
            cli::Args {
                to: None,
                plugins: false,
                confirm: false,
                verbose: false,
            },
            &config,
            &mut plugins,
        )
        .await
    }

    #[test]
    fn test_validate_auto() {
        assert_eq!(config::validate_auto("off"), Ok(()));
        assert_eq!(config::validate_auto("1 day"), Ok(()));
        assert_eq!(config::validate_auto("2 weeks 3 days 1 hr"), Ok(()));

        assert_eq!(
            config::validate_auto("foo").unwrap_err().to_string(),
            "Validation error: invalid_duration_string [{}]".to_string()
        );
    }
}
<|endoftext|>


<commit_before>#![warn(unused_extern_crates)]

#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate rhq_core as rhq;
extern crate shlex;

use std::borrow::Cow;
use std::env;
use std::path::Path;
use clap::{AppSettings, Arg, SubCommand};

use rhq::{Query, Remote, Repository, Workspace};
use rhq::url::build_url;
use rhq::util;
use rhq::vcs::{self, Vcs};
use rhq::Result;

const POSSIBLE_VCS: &[&str] = &["git", "hg", "darcs", "pijul"];

fn main() {
    env_logger::init().expect("failed to initialize env_logger.");

    let app = build_cli();
    let matches = app.get_matches();
    if let Err(message) = Command::from_args(&matches).run() {
        println!("failed with: {}", message);
        std::process::exit(1);
    }
}

/// Toplevel application
pub enum Command<'a> {
    Add(AddCommand<'a>),
    Refresh(RefreshCommand),
    New(NewCommand<'a>),
    Clone(CloneCommand<'a>),
    List(ListCommand),
    Foreach(ForeachCommand<'a>),
    Completion(CompletionCommand<'a>),
}

impl<'a> Command<'a> {
    fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
        app.subcommand(AddCommand::make_app(SubCommand::with_name("add")))
            .subcommand(RefreshCommand::make_app(SubCommand::with_name("refresh")))
            .subcommand(NewCommand::make_app(SubCommand::with_name("new")))
            .subcommand(CloneCommand::make_app(SubCommand::with_name("clone")))
            .subcommand(ListCommand::make_app(SubCommand::with_name("list")))
            .subcommand(ForeachCommand::make_app(SubCommand::with_name("foreach")))
            .subcommand(CompletionCommand::make_app(
                SubCommand::with_name("completion"),
            ))
    }

    fn from_args<'b: 'a>(m: &'b clap::ArgMatches<'a>) -> Command<'a> {
        match m.subcommand() {
            ("add", Some(m)) => Command::Add(AddCommand::from_args(m)),
            ("refresh", Some(m)) => Command::Refresh(RefreshCommand::from_args(m)),
            ("new", Some(m)) => Command::New(NewCommand::from_args(m)),
            ("clone", Some(m)) => Command::Clone(CloneCommand::from_args(m)),
            ("list", Some(m)) => Command::List(ListCommand::from_args(m)),
            ("foreach", Some(m)) => Command::Foreach(ForeachCommand::from_args(m)),
            ("completion", Some(m)) => Command::Completion(CompletionCommand::from_args(m)),
            _ => unreachable!(),
        }
    }

    pub fn run(self) -> Result<()> {
        match self {
            Command::Refresh(m) => m.run(),
            Command::Add(m) => m.run(),
            Command::New(m) => m.run(),
            Command::Clone(m) => m.run(),
            Command::List(m) => m.run(),
            Command::Foreach(m) => m.run(),
            Command::Completion(m) => m.run(),
        }
    }
}

/// subcommand `add`
pub struct AddCommand<'a> {
    path: Option<Vec<&'a Path>>,
    verbose: bool,
    import: bool,
    depth: Option<usize>,
}

impl<'a> AddCommand<'a> {
    fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
        app.about("Add existed repositories into management")
            .arg_from_usage("[path]...       'Location of local repositories'")
            .arg_from_usage("-v, --verbose   'Use verbose output'")
            .arg_from_usage("-i, --import    'Use import mode'")
            .arg_from_usage("--depth=[depth] 'Maximal depth of entries for each base directory'")
    }

    fn from_args<'b: 'a>(m: &'b clap::ArgMatches<'a>) -> AddCommand<'a> {
        AddCommand {
            path: m.values_of("path").map(|s| s.map(Path::new).collect()),
            verbose: m.is_present("verbose"),
            import: m.is_present("import"),
            depth: m.value_of("depth").and_then(|s| s.parse().ok()),
        }
    }

    fn run_import(&self) -> Result<()> {
        let mut workspace = Workspace::new(None)?;
        if let Some(ref roots) = self.path {
            for root in roots {
                workspace.scan_repositories(root, self.verbose, self.depth)?;
            }
        } else {
            workspace.scan_repositories_default(self.verbose, self.depth)?;
        }
        workspace.save_cache()?;
        Ok(())
    }

    fn run(self) -> Result<()> {
        if self.import {
            return self.run_import();
        }

        let paths: Vec<Cow<Path>> = match self.path {
            Some(ref path) => path.into_iter().map(|&path| path.into()).collect(),
            None => vec![env::current_dir()?.into()],
        };

        let mut workspace = Workspace::new(None)?;
        for path in paths {
            let vcs = match vcs::detect_from_path(&path) {
                Some(vcs) => vcs,
                None => {
                    println!("Ignored: {} is not a repository", path.display());
                    continue;
                }
            };
            let remote = vcs.get_remote_url(&path)
                .ok()
                .map_or(None, |s| s.map(Remote::new));
            let repo = Repository::new(&path, remote)?;

            workspace.add_repository(repo, false);
            if self.verbose {
                println!("Added: {}", util::canonicalize_pretty(&path)?.display());
            }
        }
        workspace.save_cache()?;

        Ok(())
    }
}

/// Subommand `refresh`
pub struct RefreshCommand {
    verbose: bool,
    sort: bool,
}

impl RefreshCommand {
    fn make_app<'a, 'b: 'a>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
        app.about("Scan repository list and drop if it is not existed or matches exclude pattern.")
            .arg_from_usage("-v, --verbose 'Use verbose output'")
            .arg_from_usage("-s, --sort    'Sort by path string'")
    }

    fn from_args(m: &clap::ArgMatches) -> RefreshCommand {
        RefreshCommand {
            verbose: m.is_present("verbose"),
            sort: m.is_present("sort"),
        }
    }

    fn run(self) -> Result<()> {
        let mut workspace = Workspace::new(None)?;
        workspace.drop_invalid_repositories(self.verbose);
        if self.sort {
            workspace.sort_repositories();
        }
        workspace.save_cache()?;
        Ok(())
    }
}

/// Subcommand `new`
pub struct NewCommand<'a> {
    path: &'a str,
    vcs: Vcs,
    hook: Option<Vec<String>>,
}

impl<'a> NewCommand<'a> {
    fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
        app.about("Create a new repository and add it into management")
            .arg_from_usage("<path>           'Path of target repository, or URL-like pattern'")
            .arg(Arg::from_usage("--vcs=[vcs] 'Used Version Control System'").possible_values(POSSIBLE_VCS))
            .arg_from_usage("--hook=[hook]    'Post hook after initialization'")
    }

    fn from_args<'b: 'a>(m: &'b clap::ArgMatches<'a>) -> NewCommand<'a> {
        NewCommand {
            path: m.value_of("path").unwrap(),
            vcs: m.value_of("vcs")
                .and_then(|s| s.parse().ok())
                .unwrap_or(Vcs::Git),
            hook: m.value_of("hook").and_then(|s| shlex::split(s)),
        }
    }

    fn run(self) -> Result<()> {
        let mut workspace = Workspace::new(None)?;

        let path: Cow<Path> = match self.path.parse::<Query>() {
            Ok(query) => workspace.resolve_query(&query)?.into(),
            Err(_) => Path::new(self.path).into(),
        };

        // init
        if vcs::detect_from_path(&path).is_some() {
            println!("The repository {} has already existed.", path.display());
            return Ok(());
        }
        print!("Creating an empty repository at \"{}\"", path.display());
        print!(" (VCS: {:?})", self.vcs);
        println!();
        self.vcs.do_init(&path)?;
        let repo = Repository::new(path, None)?;

        // hook
        if let Some(hook) = self.hook {
            if hook.len() >= 1 {
                repo.run_command(&hook[0], &hook[1..])?;
            }
        }

        workspace.add_repository(repo, false);
        workspace.save_cache()?;

        Ok(())
    }
}

/// Subcommand `clone`
pub struct CloneCommand<'a> {
    query: Query,
    dest: Option<&'a Path>,
    root: Option<&'a Path>,
    ssh: bool,
    args: Vec<&'a str>,
    vcs: Vcs,
}

impl<'a> CloneCommand<'a> {
    fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
        app.about("Clone remote repositories, and then add it under management")
            .arg_from_usage("<query>          'an URL or a string to determine the URL of remote repository'")
            .arg_from_usage("[dest]           'Destination directory of cloned repository'")
            .arg_from_usage("[args]...        'Supplemental arguments for VCS command'")
            .arg_from_usage("--root=[root]    'Path to determine the destination directory of cloned repository'")
            .arg_from_usage("-s, --ssh        'Use SSH protocol'")
            .arg(Arg::from_usage("--vcs=[vcs] 'Used Version Control System'").possible_values(POSSIBLE_VCS))
    }

    fn from_args<'b: 'a>(m: &'b clap::ArgMatches<'a>) -> CloneCommand<'a> {
        CloneCommand {
            query: m.value_of("query").and_then(|s| s.parse().ok()).unwrap(),
            dest: m.value_of("dest").map(Path::new),
            root: m.value_of("root").map(Path::new),
            ssh: m.is_present("ssh"),
            args: m.values_of("args").map(|s| s.collect()).unwrap_or_default(),
            vcs: m.value_of("vcs")
                .and_then(|s| s.parse().ok())
                .unwrap_or(Vcs::Git),
        }
    }
}

impl<'a> CloneCommand<'a> {
    fn run(self) -> Result<()> {
        let mut workspace = Workspace::new(self.root)?;

        let dest: Cow<Path> = match self.dest {
            Some(dest) => dest.into(),
            None => workspace.resolve_query(&self.query)?.into(),
        };
        let url = build_url(&self.query, self.ssh)?;

        if vcs::detect_from_path(&dest).is_some() {
            println!("The repository {} has already existed.", dest.display());
            return Ok(());
        }

        println!(
            "Clone from {} into {} by using {:?} (with arguments: {})",
            url,
            dest.display(),
            self.vcs,
            util::join_str(&self.args[..]),
        );

        self.vcs.do_clone(&dest, &url, &self.args[..])?;
        let repo = Repository::new(dest, Remote::new(url))?;

        workspace.add_repository(repo, false);

        workspace.save_cache()?;

        Ok(())
    }
}

#[derive(Debug)]
enum ListFormat {
    Name,
    FullPath,
}

impl ::std::str::FromStr for ListFormat {
    type Err = ();
    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        match s {
            "name" => Ok(ListFormat::Name),
            "fullpath" => Ok(ListFormat::FullPath),
            _ => Err(()),
        }
    }
}

/// Subcommand `list`
pub struct ListCommand {
    format: ListFormat,
}

impl ListCommand {
    fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
        app.about("List local repositories managed by rhq")
            .arg(clap::Arg::from_usage("--format=[format] 'List format'").possible_values(&["name", "fullpath"]))
    }

    fn from_args(m: &clap::ArgMatches) -> ListCommand {
        ListCommand {
            format: m.value_of("format")
                .and_then(|s| s.parse().ok())
                .unwrap_or(ListFormat::FullPath),
        }
    }

    fn run(self) -> Result<()> {
        let workspace = Workspace::new(None)?;
        workspace.for_each_repo(|repo| {
            match self.format {
                ListFormat::Name => println!("{}", repo.name()),
                ListFormat::FullPath => println!("{}", repo.path_string()),
            }
            Ok(())
        })
    }
}

/// Subcommand `foreach`
pub struct ForeachCommand<'a> {
    command: &'a str,
    args: Vec<&'a str>,
    dry_run: bool,
}

impl<'a> ForeachCommand<'a> {
    fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
        app.about("Execute command into each repositories")
            .arg_from_usage("<command>       'Command name'")
            .arg_from_usage("[args]...       'Arguments of command'")
            .arg_from_usage("-n, --dry-run   'Do not actually execute command'")
    }

    fn from_args<'b: 'a>(m: &'b clap::ArgMatches<'a>) -> ForeachCommand<'a> {
        ForeachCommand {
            command: m.value_of("command").unwrap(),
            args: m.values_of("args").map(|s| s.collect()).unwrap_or_default(),
            dry_run: m.is_present("dry-run"),
        }
    }

    fn run(self) -> Result<()> {
        let workspace = Workspace::new(None)?;
        workspace.for_each_repo(|repo| {
            if self.dry_run {
                println!("+ {} {}", self.command, util::join_str(&self.args[..]));
            } else {
                repo.run_command(self.command, &self.args[..])?;
            }
            Ok(())
        })
    }
}

pub struct CompletionCommand<'a> {
    shell: clap::Shell,
    out_file: Option<&'a Path>,
}

impl<'a> CompletionCommand<'a> {
    fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
        app.about("Generate completion scripts for your shell")
            .setting(AppSettings::ArgRequiredElseHelp)
            .arg(
                clap::Arg::with_name("shell")
                    .help("target shell")
                    .possible_values(&["bash", "zsh", "fish", "powershell"])
                    .required(true),
            )
            .arg(clap::Arg::from_usage("[out-file]  'path to output script'"))
    }

    fn from_args<'b: 'a>(m: &'b clap::ArgMatches<'a>) -> CompletionCommand<'a> {
        CompletionCommand {
            shell: m.value_of("shell").and_then(|s| s.parse().ok()).unwrap(),
            out_file: m.value_of("out-file").map(Path::new),
        }
    }

    fn run(self) -> Result<()> {
        let mut app = build_cli();
        if let Some(path) = self.out_file {
            let mut file = ::std::fs::OpenOptions::new()
                .write(true)
                .create(true)
                .append(false)
                .open(path)
                .unwrap();
            app.gen_completions_to(env!("CARGO_PKG_NAME"), self.shell, &mut file);
        } else {
            app.gen_completions_to(env!("CARGO_PKG_NAME"), self.shell, &mut ::std::io::stdout());
        }
        ::std::process::exit(0)
    }
}

fn build_cli<'a, 'b: 'a>() -> clap::App<'a, 'b> {
    let app = app_from_crate!()
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::SubcommandRequiredElseHelp);
    Command::make_app(app)
}
<commit_msg>refactor(cli):<commit_after>#![warn(unused_extern_crates)]

#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate rhq_core as rhq;
extern crate shlex;

use std::borrow::Cow;
use std::env;
use std::path::Path;
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};

use rhq::{Query, Remote, Repository, Workspace};
use rhq::url::build_url;
use rhq::util;
use rhq::vcs::{self, Vcs};
use rhq::Result;

const POSSIBLE_VCS: &[&str] = &["git", "hg", "darcs", "pijul"];

fn main() {
    env_logger::init().expect("failed to initialize env_logger.");
    if let Err(message) = run() {
        println!("failed with: {}", message);
        std::process::exit(1);
    }
}

macro_rules! def_app {
    ($( $name:expr => $t:ident, )*) => {
        fn app<'a, 'b: 'a>() -> App<'a, 'b> {
            app_from_crate!()
                .setting(AppSettings::VersionlessSubcommands)
                .setting(AppSettings::SubcommandRequiredElseHelp)
                $( .subcommand($t::app(SubCommand::with_name($name))) )*
        }

        pub fn run() -> Result<()> {
            let matches = app().get_matches();
            match matches.subcommand() {
                $( ($name, Some(m)) => $t::from_matches(m).run(), )*
                _ => unreachable!(),
            }
        }
    }
}

def_app! {
    "add"        => AddCommand,
    "clone"      => CloneCommand,
    "completion" => CompletionCommand,
    "foreach"    => ForeachCommand,
    "list"       => ListCommand,
    "new"        => NewCommand,
    "refresh"    => RefreshCommand,
}

/// subcommand `add`
struct AddCommand<'a> {
    path: Option<Vec<&'a Path>>,
    verbose: bool,
    import: bool,
    depth: Option<usize>,
}

impl<'a> AddCommand<'a> {
    fn app<'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
        app.about("Add existed repositories into management")
            .arg_from_usage("[path]...       'Location of local repositories'")
            .arg_from_usage("-v, --verbose   'Use verbose output'")
            .arg_from_usage("-i, --import    'Use import mode'")
            .arg_from_usage("--depth=[depth] 'Maximal depth of entries for each base directory'")
    }

    fn from_matches<'b: 'a>(m: &'b ArgMatches<'a>) -> AddCommand<'a> {
        AddCommand {
            path: m.values_of("path").map(|s| s.map(Path::new).collect()),
            verbose: m.is_present("verbose"),
            import: m.is_present("import"),
            depth: m.value_of("depth").and_then(|s| s.parse().ok()),
        }
    }

    fn run_import(&self) -> Result<()> {
        let mut workspace = Workspace::new(None)?;
        if let Some(ref roots) = self.path {
            for root in roots {
                workspace.scan_repositories(root, self.verbose, self.depth)?;
            }
        } else {
            workspace.scan_repositories_default(self.verbose, self.depth)?;
        }
        workspace.save_cache()?;
        Ok(())
    }

    fn run(self) -> Result<()> {
        if self.import {
            return self.run_import();
        }

        let paths: Vec<Cow<Path>> = match self.path {
            Some(ref path) => path.into_iter().map(|&path| path.into()).collect(),
            None => vec![env::current_dir()?.into()],
        };

        let mut workspace = Workspace::new(None)?;
        for path in paths {
            let vcs = match vcs::detect_from_path(&path) {
                Some(vcs) => vcs,
                None => {
                    println!("Ignored: {} is not a repository", path.display());
                    continue;
                }
            };
            let remote = vcs.get_remote_url(&path)
                .ok()
                .map_or(None, |s| s.map(Remote::new));
            let repo = Repository::new(&path, remote)?;

            workspace.add_repository(repo, false);
            if self.verbose {
                println!("Added: {}", util::canonicalize_pretty(&path)?.display());
            }
        }
        workspace.save_cache()?;

        Ok(())
    }
}

/// Subommand `refresh`
struct RefreshCommand {
    verbose: bool,
    sort: bool,
}

impl RefreshCommand {
    fn app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
        app.about("Scan repository list and drop if it is not existed or matches exclude pattern.")
            .arg_from_usage("-v, --verbose 'Use verbose output'")
            .arg_from_usage("-s, --sort    'Sort by path string'")
    }

    fn from_matches(m: &ArgMatches) -> RefreshCommand {
        RefreshCommand {
            verbose: m.is_present("verbose"),
            sort: m.is_present("sort"),
        }
    }

    fn run(self) -> Result<()> {
        let mut workspace = Workspace::new(None)?;
        workspace.drop_invalid_repositories(self.verbose);
        if self.sort {
            workspace.sort_repositories();
        }
        workspace.save_cache()?;
        Ok(())
    }
}

/// Subcommand `new`
struct NewCommand<'a> {
    path: &'a str,
    vcs: Vcs,
    hook: Option<Vec<String>>,
}

impl<'a> NewCommand<'a> {
    fn app<'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
        app.about("Create a new repository and add it into management")
            .arg_from_usage("<path>           'Path of target repository, or URL-like pattern'")
            .arg(Arg::from_usage("--vcs=[vcs] 'Used Version Control System'").possible_values(POSSIBLE_VCS))
            .arg_from_usage("--hook=[hook]    'Post hook after initialization'")
    }

    fn from_matches<'b: 'a>(m: &'b ArgMatches<'a>) -> NewCommand<'a> {
        NewCommand {
            path: m.value_of("path").unwrap(),
            vcs: m.value_of("vcs")
                .and_then(|s| s.parse().ok())
                .unwrap_or(Vcs::Git),
            hook: m.value_of("hook").and_then(|s| shlex::split(s)),
        }
    }

    fn run(self) -> Result<()> {
        let mut workspace = Workspace::new(None)?;

        let path: Cow<Path> = match self.path.parse::<Query>() {
            Ok(query) => workspace.resolve_query(&query)?.into(),
            Err(_) => Path::new(self.path).into(),
        };

        // init
        if vcs::detect_from_path(&path).is_some() {
            println!("The repository {} has already existed.", path.display());
            return Ok(());
        }
        print!("Creating an empty repository at \"{}\"", path.display());
        print!(" (VCS: {:?})", self.vcs);
        println!();
        self.vcs.do_init(&path)?;
        let repo = Repository::new(path, None)?;

        // hook
        if let Some(hook) = self.hook {
            if hook.len() >= 1 {
                repo.run_command(&hook[0], &hook[1..])?;
            }
        }

        workspace.add_repository(repo, false);
        workspace.save_cache()?;

        Ok(())
    }
}

/// Subcommand `clone`
pub struct CloneCommand<'a> {
    query: Query,
    dest: Option<&'a Path>,
    root: Option<&'a Path>,
    ssh: bool,
    args: Vec<&'a str>,
    vcs: Vcs,
}

impl<'a> CloneCommand<'a> {
    fn app<'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
        app.about("Clone remote repositories, and then add it under management")
            .arg_from_usage("<query>          'an URL or a string to determine the URL of remote repository'")
            .arg_from_usage("[dest]           'Destination directory of cloned repository'")
            .arg_from_usage("[args]...        'Supplemental arguments for VCS command'")
            .arg_from_usage("--root=[root]    'Path to determine the destination directory of cloned repository'")
            .arg_from_usage("-s, --ssh        'Use SSH protocol'")
            .arg(Arg::from_usage("--vcs=[vcs] 'Used Version Control System'").possible_values(POSSIBLE_VCS))
    }

    fn from_matches<'b: 'a>(m: &'b ArgMatches<'a>) -> CloneCommand<'a> {
        CloneCommand {
            query: m.value_of("query").and_then(|s| s.parse().ok()).unwrap(),
            dest: m.value_of("dest").map(Path::new),
            root: m.value_of("root").map(Path::new),
            ssh: m.is_present("ssh"),
            args: m.values_of("args").map(|s| s.collect()).unwrap_or_default(),
            vcs: m.value_of("vcs")
                .and_then(|s| s.parse().ok())
                .unwrap_or(Vcs::Git),
        }
    }

    fn run(self) -> Result<()> {
        let mut workspace = Workspace::new(self.root)?;

        let dest: Cow<Path> = match self.dest {
            Some(dest) => dest.into(),
            None => workspace.resolve_query(&self.query)?.into(),
        };
        let url = build_url(&self.query, self.ssh)?;

        if vcs::detect_from_path(&dest).is_some() {
            println!("The repository {} has already existed.", dest.display());
            return Ok(());
        }

        println!(
            "Clone from {} into {} by using {:?} (with arguments: {})",
            url,
            dest.display(),
            self.vcs,
            util::join_str(&self.args[..]),
        );

        self.vcs.do_clone(&dest, &url, &self.args[..])?;
        let repo = Repository::new(dest, Remote::new(url))?;

        workspace.add_repository(repo, false);

        workspace.save_cache()?;

        Ok(())
    }
}

#[derive(Debug)]
enum ListFormat {
    Name,
    FullPath,
}

impl ::std::str::FromStr for ListFormat {
    type Err = ();
    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        match s {
            "name" => Ok(ListFormat::Name),
            "fullpath" => Ok(ListFormat::FullPath),
            _ => Err(()),
        }
    }
}

/// Subcommand `list`
pub struct ListCommand {
    format: ListFormat,
}

impl ListCommand {
    fn app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
        app.about("List local repositories managed by rhq")
            .arg(clap::Arg::from_usage("--format=[format] 'List format'").possible_values(&["name", "fullpath"]))
    }

    fn from_matches(m: &ArgMatches) -> ListCommand {
        ListCommand {
            format: m.value_of("format")
                .and_then(|s| s.parse().ok())
                .unwrap_or(ListFormat::FullPath),
        }
    }

    fn run(self) -> Result<()> {
        let workspace = Workspace::new(None)?;
        workspace.for_each_repo(|repo| {
            match self.format {
                ListFormat::Name => println!("{}", repo.name()),
                ListFormat::FullPath => println!("{}", repo.path_string()),
            }
            Ok(())
        })
    }
}

/// Subcommand `foreach`
pub struct ForeachCommand<'a> {
    command: &'a str,
    args: Vec<&'a str>,
    dry_run: bool,
}

impl<'a> ForeachCommand<'a> {
    fn app<'b: 'a>(app: App<'a, 'b>) -> clap::App<'a, 'b> {
        app.about("Execute command into each repositories")
            .arg_from_usage("<command>       'Command name'")
            .arg_from_usage("[args]...       'Arguments of command'")
            .arg_from_usage("-n, --dry-run   'Do not actually execute command'")
    }

    fn from_matches<'b: 'a>(m: &'b ArgMatches<'a>) -> ForeachCommand<'a> {
        ForeachCommand {
            command: m.value_of("command").unwrap(),
            args: m.values_of("args").map(|s| s.collect()).unwrap_or_default(),
            dry_run: m.is_present("dry-run"),
        }
    }

    fn run(self) -> Result<()> {
        let workspace = Workspace::new(None)?;
        workspace.for_each_repo(|repo| {
            if self.dry_run {
                println!("+ {} {}", self.command, util::join_str(&self.args[..]));
            } else {
                repo.run_command(self.command, &self.args[..])?;
            }
            Ok(())
        })
    }
}

pub struct CompletionCommand<'a> {
    shell: clap::Shell,
    out_file: Option<&'a Path>,
}

impl<'a> CompletionCommand<'a> {
    fn app<'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
        app.about("Generate completion scripts for your shell")
            .setting(AppSettings::ArgRequiredElseHelp)
            .arg(
                clap::Arg::with_name("shell")
                    .help("target shell")
                    .possible_values(&["bash", "zsh", "fish", "powershell"])
                    .required(true),
            )
            .arg(clap::Arg::from_usage("[out-file]  'path to output script'"))
    }

    fn from_matches<'b: 'a>(m: &'b ArgMatches<'a>) -> CompletionCommand<'a> {
        CompletionCommand {
            shell: m.value_of("shell").and_then(|s| s.parse().ok()).unwrap(),
            out_file: m.value_of("out-file").map(Path::new),
        }
    }

    fn run(self) -> Result<()> {
        if let Some(path) = self.out_file {
            let mut file = ::std::fs::OpenOptions::new()
                .write(true)
                .create(true)
                .append(false)
                .open(path)
                .unwrap();
            app().gen_completions_to(env!("CARGO_PKG_NAME"), self.shell, &mut file);
        } else {
            app().gen_completions_to(env!("CARGO_PKG_NAME"), self.shell, &mut ::std::io::stdout());
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use snake;
use game_state;
use player;

pub static NUMBER_OF_LAYERS: uint = 4;
pub static WATER_COLOR: [f32, ..4] = [0.0, 0.0, 0.45, 1.0];
pub static WATER_FRICTION: f64 = 0.2;
pub static RADIUS: f64 = 0.1;
pub static SURFACE_Y: f64 = 10.0;
pub static INITIAL_GAME_STATE: game_state::GameState = game_state::Play;
pub static INITIAL_CAMERA_POS: [f64, ..2] = [0.0, 0.0];
pub static CAMERA_FOLLOW_PERCENTAGE: f64 = 0.4;

pub static YOU_WIN_TEXT: &'static str = "you win";
pub static YOU_WIN_TEXT_COLOR: [f32, ..4] = GREEN;
pub static YOU_WIN_POS: [f64, ..2] = [-0.42, 0.1];

pub static YOU_LOOSE_TEXT: &'static str = "you loose";
pub static YOU_LOOSE_TEXT_COLOR: [f32, ..4] = RED;
pub static YOU_LOOSE_POS: [f64, ..2] = [-0.55, 0.1];

pub static AIR_BOTTLE_RADIUS: f64 = 0.1;
pub static AIR_BOTTLE_TEST_COLOR: [f32, ..4] = WHITE;
pub static AIR_BOTTLE_TEXT_COLOR: [f32, ..4] = BLACK;
pub static AIR_BOTTLE_FILL_UP: f64 = 0.5;
pub static AIR_BOTTLE_POS: &'static [f64] = &[
    0.5, 0.3,
    0.3, 0.8,
    0.6, 1.4,
];

pub static PLAYER_LOOSE_AIR_SPEED: f64 = 0.1;
pub static PLAYER_COLOR: [f32, ..4] = YELLOW;
pub static PLAYER_BITTEN_COLOR: [f32, ..4] = RED;
pub static PLAYER_INITIAL_BLOOD: f64 = 1.0;
pub static PLAYER_INITIAL_AIR: f64 = 1.0;
pub static PLAYER_RADIUS: f64 = 0.1;
pub static PLAYER_ACCELERATION_RIGHT: f64 = 0.1;
pub static PLAYER_ACCELERATION_LEFT: f64 = 0.1;
pub static PLAYER_ACCELERATION_UP: f64 = 0.1;
pub static PLAYER_ACCELERATION_DOWN: f64 = 0.1;
pub static PLAYER_INITIAL_TWEEN_FACTOR: f64 = 0.0;
pub static PLAYER_TWEEN_SPEED: f64 = 1.0;
pub static PLAYER_BITTEN_FADE_OUT_SECONDS: f64 = 2.0;
pub static PLAYER_INITIAL_STATE: player::PlayerState = player::Normal;

pub struct SnakeSettings {
    pub acceleration_left: f64,
    pub acceleration_right: f64,
    pub acceleration_up: f64,
    pub acceleration_down: f64,
    pub initial_state: snake::SnakeState,
    pub bite_damage: f64,
    pub sensor_distance: f64,
    pub attack_distance: f64,
    pub wait_seconds_before_initial_attack: f64,
    pub wait_seconds_before_repeat_attack: f64,
    pub test_color: [f32, ..4],
    pub radius: f64,
}

pub static SNAKE_TAIL_COLOR: [f32, ..4] = [0.8, 0.6, 0.6, 0.4];

pub static SNAKE_TAIL_DISTANCE: f64 = 0.01;
pub static SNAKE_OVERALL_ACCELERATION: f64 = 2.0;
pub static SNAKE_SETTINGS: SnakeSettings = SnakeSettings {
    acceleration_left: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_right: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_up: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_down: 0.1 * SNAKE_OVERALL_ACCELERATION,
    initial_state: snake::Ignorant,
    bite_damage: 0.1,
    sensor_distance: 2.0,
    attack_distance: 2.0 * PLAYER_RADIUS,
    wait_seconds_before_initial_attack: 1.0,
    wait_seconds_before_repeat_attack: 1.0,
    test_color: BLACK,
    radius: 0.03,
};

pub static SNAKE_SPEED_RIGHT: f64 = 0.1;
pub static SNAKE_SPEED_LEFT: f64 = 0.1;
pub static SNAKE_SPEED_UP: f64 = 0.1;
pub static SNAKE_SPEED_DOWN: f64 = 0.1;

// snake 1.
pub static SNAKE_1_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_1_POS: [f64, ..2] = [-0.8, 0.8];
pub static SNAKE_1_ADD: bool = true;

// snake 2.
pub static SNAKE_2_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_2_POS: [f64, ..2] = [0.8, 0.8];
pub static SNAKE_2_ADD: bool = true;

pub static BAR_RECTANGLE: [f64, ..4] = [0.3, -0.06, 1.5, 0.05];
pub static BAR_MARGIN: f64 = 0.01;
pub static BAR_TEXT_COLOR: [f32, ..4] = WHITE;

pub static AIR_BAR_INITIAL_VALUE: f64 = 0.5;
pub static AIR_BAR_POS: [f64, ..2] = [-0.9, 0.95];
pub static AIR_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static AIR_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_BLUE;
pub static AIR_BAR_BAR_COLOR: [f32, ..4] = LIGHT_BLUE;

pub static BLOOD_BAR_INITIAL_VALUE: f64 = 0.5;
pub static BLOOD_BAR_POS: [f64, ..2] = [-0.9, 0.85];
pub static BLOOD_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static BLOOD_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_RED;
pub static BLOOD_BAR_BAR_COLOR: [f32, ..4] = LIGHT_RED;

pub static BAR_BACKGROUND_HEIGHT: f64 = 0.275;
pub static BAR_BACKGROUND_COLOR: [f32, ..4] = [0.1, 0.1, 0.1, 0.9];
pub static BAR_BACKGROUND_COLOR_2: [f32, ..4] = [0.4, 0.4, 0.4, 0.8];
pub static BAR_BACKGROUND_MARGIN: f64 = 0.01;
pub static BAR_BACKGROUND_MARGIN_2: f64 = 0.02;

pub static BLACK: [f32, ..4] = [0.0, 0.0, 0.0, 1.0];
pub static WHITE: [f32, ..4] = [1.0, 1.0, 1.0, 1.0];
pub static GRAY: [f32, ..4] = [0.5, 0.5, 0.5, 1.0];
pub static DARK_GRAY: [f32, ..4] = [0.2, 0.2, 0.2, 1.0];

pub static RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];
pub static DARK_RED: [f32, ..4] = [0.5, 0.0, 0.0, 1.0];
pub static LIGHT_RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];

pub static GREEN: [f32, ..4] = [0.0, 1.0, 0.0, 1.0];

pub static BLUE: [f32, ..4] = [0.0, 0.0, 1.0, 1.0];
pub static DARK_BLUE: [f32, ..4] = [0.0, 0.0, 0.5, 1.0];
pub static LIGHT_BLUE: [f32, ..4] = [0.5, 0.5, 1.0, 1.0];

pub static YELLOW: [f32, ..4] = [0.0, 1.0, 1.0, 1.0];

pub static ORIGIN: [f64, ..2] = [0.0, 0.0];

<commit_msg>Decreased snake attack range<commit_after>use snake;
use game_state;
use player;

pub static NUMBER_OF_LAYERS: uint = 4;
pub static WATER_COLOR: [f32, ..4] = [0.0, 0.0, 0.45, 1.0];
pub static WATER_FRICTION: f64 = 0.2;
pub static RADIUS: f64 = 0.1;
pub static SURFACE_Y: f64 = 10.0;
pub static INITIAL_GAME_STATE: game_state::GameState = game_state::Play;
pub static INITIAL_CAMERA_POS: [f64, ..2] = [0.0, 0.0];
pub static CAMERA_FOLLOW_PERCENTAGE: f64 = 0.4;

pub static YOU_WIN_TEXT: &'static str = "you win";
pub static YOU_WIN_TEXT_COLOR: [f32, ..4] = GREEN;
pub static YOU_WIN_POS: [f64, ..2] = [-0.42, 0.1];

pub static YOU_LOOSE_TEXT: &'static str = "you loose";
pub static YOU_LOOSE_TEXT_COLOR: [f32, ..4] = RED;
pub static YOU_LOOSE_POS: [f64, ..2] = [-0.55, 0.1];

pub static AIR_BOTTLE_RADIUS: f64 = 0.1;
pub static AIR_BOTTLE_TEST_COLOR: [f32, ..4] = WHITE;
pub static AIR_BOTTLE_TEXT_COLOR: [f32, ..4] = BLACK;
pub static AIR_BOTTLE_FILL_UP: f64 = 0.5;
pub static AIR_BOTTLE_POS: &'static [f64] = &[
    0.5, 0.3,
    0.3, 0.8,
    0.6, 1.4,
];

pub static PLAYER_LOOSE_AIR_SPEED: f64 = 0.1;
pub static PLAYER_COLOR: [f32, ..4] = YELLOW;
pub static PLAYER_BITTEN_COLOR: [f32, ..4] = RED;
pub static PLAYER_INITIAL_BLOOD: f64 = 1.0;
pub static PLAYER_INITIAL_AIR: f64 = 1.0;
pub static PLAYER_RADIUS: f64 = 0.1;
pub static PLAYER_ACCELERATION_RIGHT: f64 = 0.1;
pub static PLAYER_ACCELERATION_LEFT: f64 = 0.1;
pub static PLAYER_ACCELERATION_UP: f64 = 0.1;
pub static PLAYER_ACCELERATION_DOWN: f64 = 0.1;
pub static PLAYER_INITIAL_TWEEN_FACTOR: f64 = 0.0;
pub static PLAYER_TWEEN_SPEED: f64 = 1.0;
pub static PLAYER_BITTEN_FADE_OUT_SECONDS: f64 = 2.0;
pub static PLAYER_INITIAL_STATE: player::PlayerState = player::Normal;

pub struct SnakeSettings {
    pub acceleration_left: f64,
    pub acceleration_right: f64,
    pub acceleration_up: f64,
    pub acceleration_down: f64,
    pub initial_state: snake::SnakeState,
    pub bite_damage: f64,
    pub sensor_distance: f64,
    pub attack_distance: f64,
    pub wait_seconds_before_initial_attack: f64,
    pub wait_seconds_before_repeat_attack: f64,
    pub test_color: [f32, ..4],
    pub radius: f64,
}

pub static SNAKE_TAIL_COLOR: [f32, ..4] = [0.8, 0.6, 0.6, 0.4];

pub static SNAKE_TAIL_DISTANCE: f64 = 0.01;
pub static SNAKE_OVERALL_ACCELERATION: f64 = 2.0;
pub static SNAKE_SETTINGS: SnakeSettings = SnakeSettings {
    acceleration_left: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_right: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_up: 0.1 * SNAKE_OVERALL_ACCELERATION,
    acceleration_down: 0.1 * SNAKE_OVERALL_ACCELERATION,
    initial_state: snake::Ignorant,
    bite_damage: 0.1,
    sensor_distance: 2.0,
    attack_distance: 0.5 * PLAYER_RADIUS,
    wait_seconds_before_initial_attack: 1.0,
    wait_seconds_before_repeat_attack: 1.0,
    test_color: BLACK,
    radius: 0.03,
};

pub static SNAKE_SPEED_RIGHT: f64 = 0.1;
pub static SNAKE_SPEED_LEFT: f64 = 0.1;
pub static SNAKE_SPEED_UP: f64 = 0.1;
pub static SNAKE_SPEED_DOWN: f64 = 0.1;

// snake 1.
pub static SNAKE_1_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_1_POS: [f64, ..2] = [-0.8, 0.8];
pub static SNAKE_1_ADD: bool = true;

// snake 2.
pub static SNAKE_2_SETTINGS: SnakeSettings = SNAKE_SETTINGS;
pub static SNAKE_2_POS: [f64, ..2] = [0.8, 0.8];
pub static SNAKE_2_ADD: bool = true;

pub static BAR_RECTANGLE: [f64, ..4] = [0.3, -0.06, 1.5, 0.05];
pub static BAR_MARGIN: f64 = 0.01;
pub static BAR_TEXT_COLOR: [f32, ..4] = WHITE;

pub static AIR_BAR_INITIAL_VALUE: f64 = 0.5;
pub static AIR_BAR_POS: [f64, ..2] = [-0.9, 0.95];
pub static AIR_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static AIR_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_BLUE;
pub static AIR_BAR_BAR_COLOR: [f32, ..4] = LIGHT_BLUE;

pub static BLOOD_BAR_INITIAL_VALUE: f64 = 0.5;
pub static BLOOD_BAR_POS: [f64, ..2] = [-0.9, 0.85];
pub static BLOOD_BAR_TEXT_COLOR: [f32, ..4] = BAR_TEXT_COLOR;
pub static BLOOD_BAR_BACKGROUND_COLOR: [f32, ..4] = DARK_RED;
pub static BLOOD_BAR_BAR_COLOR: [f32, ..4] = LIGHT_RED;

pub static BAR_BACKGROUND_HEIGHT: f64 = 0.275;
pub static BAR_BACKGROUND_COLOR: [f32, ..4] = [0.1, 0.1, 0.1, 0.9];
pub static BAR_BACKGROUND_COLOR_2: [f32, ..4] = [0.4, 0.4, 0.4, 0.8];
pub static BAR_BACKGROUND_MARGIN: f64 = 0.01;
pub static BAR_BACKGROUND_MARGIN_2: f64 = 0.02;

pub static BLACK: [f32, ..4] = [0.0, 0.0, 0.0, 1.0];
pub static WHITE: [f32, ..4] = [1.0, 1.0, 1.0, 1.0];
pub static GRAY: [f32, ..4] = [0.5, 0.5, 0.5, 1.0];
pub static DARK_GRAY: [f32, ..4] = [0.2, 0.2, 0.2, 1.0];

pub static RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];
pub static DARK_RED: [f32, ..4] = [0.5, 0.0, 0.0, 1.0];
pub static LIGHT_RED: [f32, ..4] = [1.0, 0.0, 0.0, 1.0];

pub static GREEN: [f32, ..4] = [0.0, 1.0, 0.0, 1.0];

pub static BLUE: [f32, ..4] = [0.0, 0.0, 1.0, 1.0];
pub static DARK_BLUE: [f32, ..4] = [0.0, 0.0, 0.5, 1.0];
pub static LIGHT_BLUE: [f32, ..4] = [0.5, 0.5, 1.0, 1.0];

pub static YELLOW: [f32, ..4] = [0.0, 1.0, 1.0, 1.0];

pub static ORIGIN: [f64, ..2] = [0.0, 0.0];

<|endoftext|>


<commit_before>use indexmap::IndexMap;
use semver::Version;
use serde::{Deserialize, Deserializer, Serialize};

use std::collections::{BTreeMap, BTreeSet};

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Definitions {
    /// The Syn version used to generate the introspection file.
    pub version: Version,
    pub types: Vec<Node>,
    pub tokens: BTreeMap<String, String>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Node {
    pub ident: String,
    pub features: Features,
    #[serde(
        flatten,
        skip_serializing_if = "is_private",
        deserialize_with = "private_if_absent"
    )]
    pub data: Data,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Data {
    Private,
    #[serde(rename = "fields")]
    Struct(Fields),
    #[serde(rename = "variants")]
    Enum(Variants),
}

pub type Fields = IndexMap<String, Type>;
pub type Variants = IndexMap<String, Vec<Type>>;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Type {
    /// Type defined by `syn`
    Syn(String),

    /// Type defined in `std`.
    Std(String),

    /// Type external to `syn`
    #[serde(rename = "proc_macro2")]
    Ext(String),

    /// Token type
    Token(String),

    /// Token group
    Group(String),

    /// Punctuated list
    Punctuated(Punctuated),

    Option(Box<Type>),
    Box(Box<Type>),
    Vec(Box<Type>),
    Tuple(Vec<Type>),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Punctuated {
    pub element: Box<Type>,
    pub punct: String,
}

#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Features {
    pub any: BTreeSet<String>,
}

fn is_private(data: &Data) -> bool {
    match data {
        Data::Private => true,
        Data::Struct(_) | Data::Enum(_) => false,
    }
}

fn private_if_absent<'de, D>(deserializer: D) -> Result<Data, D::Error>
where
    D: Deserializer<'de>,
{
    let option = Option::deserialize(deserializer)?;
    Ok(option.unwrap_or(Data::Private))
}
<commit_msg>Document purpose of syn-codegen crate<commit_after>//! # Data structures that describe Syn's syntax tree.
//!
//! The Syn syntax tree is made up of more than 200 types. Occasionally it can
//! come up that you need to implement some behavior across them all.
//!
//! - For example [the Rust integration for AST Explorer][astexplorer] wants to
//!   turn a syntax tree from Syn into a JavaScript value understood by the
//!   platform's existing cross-language syntax tree visualization code.
//!
//!   [astexplorer]: https://astexplorer.net/#/gist/388150a52f74d45a355d2b5e865ded96/0c6d563f28d900472f699c21a1845ad20ae9927f
//!
//! - As another example from within Syn itself, the traits and implementations
//!   of the [`visit`], [`visit_mut`], and [`fold`] modules can be generated
//!   programmatically from a description of the syntax tree.
//!
//!   [`visit`]: https://docs.rs/syn/0.15/syn/visit/index.html
//!   [`visit_mut`]: https://docs.rs/syn/0.15/syn/visit_mut/index.html
//!   [`fold`]: https://docs.rs/syn/0.15/syn/fold/index.html
//!
//! To make this type of code as easy as possible to implement in any language,
//! every Syn release comes with a machine-readable description of that version
//! of the syntax tree as a JSON file [syn.json]. This `syn-codegen` crate
//! provides the canonical data structures for parsing and making use of the
//! representation in syn.json from Rust code.
//!
//! [syn.json]: https://raw.githubusercontent.com/dtolnay/syn/master/syn.json

use indexmap::IndexMap;
use semver::Version;
use serde::{Deserialize, Deserializer, Serialize};

use std::collections::{BTreeMap, BTreeSet};

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Definitions {
    /// The Syn version used to generate the introspection file.
    pub version: Version,
    pub types: Vec<Node>,
    pub tokens: BTreeMap<String, String>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Node {
    pub ident: String,
    pub features: Features,
    #[serde(
        flatten,
        skip_serializing_if = "is_private",
        deserialize_with = "private_if_absent"
    )]
    pub data: Data,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Data {
    Private,
    #[serde(rename = "fields")]
    Struct(Fields),
    #[serde(rename = "variants")]
    Enum(Variants),
}

pub type Fields = IndexMap<String, Type>;
pub type Variants = IndexMap<String, Vec<Type>>;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Type {
    /// Type defined by `syn`
    Syn(String),

    /// Type defined in `std`.
    Std(String),

    /// Type external to `syn`
    #[serde(rename = "proc_macro2")]
    Ext(String),

    /// Token type
    Token(String),

    /// Token group
    Group(String),

    /// Punctuated list
    Punctuated(Punctuated),

    Option(Box<Type>),
    Box(Box<Type>),
    Vec(Box<Type>),
    Tuple(Vec<Type>),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Punctuated {
    pub element: Box<Type>,
    pub punct: String,
}

#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Features {
    pub any: BTreeSet<String>,
}

fn is_private(data: &Data) -> bool {
    match data {
        Data::Private => true,
        Data::Struct(_) | Data::Enum(_) => false,
    }
}

fn private_if_absent<'de, D>(deserializer: D) -> Result<Data, D::Error>
where
    D: Deserializer<'de>,
{
    let option = Option::deserialize(deserializer)?;
    Ok(option.unwrap_or(Data::Private))
}
<|endoftext|>


<commit_before>
use Settings = piston::game::Settings;
use Game = piston::game::Game;
use GameWindow = piston::game_window::GameWindow;
use Gl = piston::gl::Gl;
use graphics::*;
use rand::random;

fn load_vertices() -> Vec<f32> {
    vec!(
         0.0,  0.5,
        -0.5, -0.5,
         0.5, -0.5,
    )
}

fn load_colors() -> Vec<f32> {
    vec!(
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
    )
}

pub struct SnakeApp {
    gl: Option<Gl>,
    vertices: Vec<f32>,
    colors: Vec<f32>,
    settings: Settings,
    game_window: GameWindow,
}

impl Game for SnakeApp {
    fn get_game_window<'a>(&'a self) -> &'a GameWindow { &self.game_window }
    fn get_settings<'a>(&'a self) -> &'a Settings { &self.settings }
    fn render(&self) {
        // Render triangle.
        // self.shader.unwrap().render(self.vertices.as_slice(), self.colors.as_slice());

        let mut gl = self.gl.unwrap();
        let c = Context::new();
        for _ in range(0, 1 << 10) {
            let x: f64 = random();
            let y: f64 = random();
            c
            .rect(x - 0.5, y - 0.5, 0.005, 0.005)
            .rgba(random(), 0.0, 0.0, 1.0)
            .fill(&mut gl);
        }
    }
    fn update(&mut self) {
    }
    fn load(&mut self) {
        self.gl = Some(Gl::new());
    }
}

impl SnakeApp {
    pub fn new() -> SnakeApp {
        let exit_on_esc = true;
        let background_color = [1.0, 1.0, 1.0, 1.0];
        SnakeApp {
            gl: None,
            game_window: GameWindow::window("Snake", 512, 512),
            vertices: load_vertices(),
            colors: load_colors(),
            settings: Settings::new(exit_on_esc, background_color),
        }
    }
}

<commit_msg>Using `clear` from Rust-Graphics<commit_after>
use Settings = piston::game::Settings;
use Game = piston::game::Game;
use GameWindow = piston::game_window::GameWindow;
use piston::gl::Gl;
use graphics;
use graphics::*;
use rand::random;

fn load_vertices() -> Vec<f32> {
    vec!(
         0.0,  0.5,
        -0.5, -0.5,
         0.5, -0.5,
    )
}

fn load_colors() -> Vec<f32> {
    vec!(
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
    )
}

pub struct SnakeApp {
    vertices: Vec<f32>,
    colors: Vec<f32>,
    settings: Settings,
    game_window: GameWindow,
}

impl Game for SnakeApp {
    fn get_game_window<'a>(&'a self) -> &'a GameWindow { &self.game_window }
    fn get_settings<'a>(&'a self) -> &'a Settings { &self.settings }
    fn render(&self, c: &graphics::Context, gl: &mut Gl) {
        // Render triangle.
        // self.shader.unwrap().render(self.vertices.as_slice(), self.colors.as_slice());

        for _ in range(0, 1 << 10) {
            let x: f64 = random();
            let y: f64 = random();
            c
            .rect(x - 0.5, y - 0.5, 0.005, 0.005)
            .rgba(random(), 0.0, 0.0, 1.0)
            .fill(gl);
        }
    }
    fn update(&mut self) {
    }
    fn load(&mut self) {
    }
}

impl SnakeApp {
    pub fn new() -> SnakeApp {
        let exit_on_esc = true;
        let background_color = [1.0, 1.0, 1.0, 1.0];
        SnakeApp {
            game_window: GameWindow::window("Snake", 512, 512),
            vertices: load_vertices(),
            colors: load_colors(),
            settings: Settings::new(exit_on_esc, background_color),
        }
    }
}

<|endoftext|>


<commit_before>use std::str;
use std::collections::{HashSet, HashMap};

use nom::IResult;

use error::{Error, Result};
use parser;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Chunk<'a> {
    Str(&'a [u8]),
    Var(&'a [u8]),
}

#[derive(Debug)]
pub struct Template<'a> {
    chunks: Vec<Chunk<'a>>,
}

impl<'a> Template<'a> {
    pub fn parse(i: &'a [u8]) -> Template<'a> {
        match parser::template(i) {
            IResult::Done(_, o) => Template { chunks: o },
            _ => unreachable!(),
        }
    }

    pub fn render(&self, vars: &HashMap<&[u8], String>) -> Result<Vec<u8>> {
        let mut buf = Vec::new();
        for chunk in &self.chunks {
            match *chunk {
                Chunk::Str(s) => buf.extend_from_slice(s),
                Chunk::Var(v) => {
                    buf.extend_from_slice(vars.get(v).ok_or(Error::VarNotFound)?.as_bytes())
                }
            }
        }
        Ok(buf)
    }

    pub fn extract_vars(&self, target: &mut HashSet<&'a [u8]>) {
        for chunk in &self.chunks {
            if let Chunk::Var(v) = *chunk {
                target.insert(v);
            }
        }
    }
}
<commit_msg>Test parsing<commit_after>use std::str;
use std::collections::{HashSet, HashMap};

use nom::IResult;

use error::{Error, Result};
use parser;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Chunk<'a> {
    Str(&'a [u8]),
    Var(&'a [u8]),
}

#[derive(Debug, PartialEq, Eq)]
pub struct Template<'a> {
    chunks: Vec<Chunk<'a>>,
}

impl<'a> Template<'a> {
    pub fn parse(i: &'a [u8]) -> Template<'a> {
        match parser::template(i) {
            IResult::Done(_, o) => Template { chunks: o },
            _ => unreachable!(),
        }
    }

    pub fn render(&self, vars: &HashMap<&[u8], String>) -> Result<Vec<u8>> {
        let mut buf = Vec::new();
        for chunk in &self.chunks {
            match *chunk {
                Chunk::Str(s) => buf.extend_from_slice(s),
                Chunk::Var(v) => {
                    buf.extend_from_slice(vars.get(v).ok_or(Error::VarNotFound)?.as_bytes())
                }
            }
        }
        Ok(buf)
    }

    pub fn extract_vars(&self, target: &mut HashSet<&'a [u8]>) {
        for chunk in &self.chunks {
            if let Chunk::Var(v) = *chunk {
                target.insert(v);
            }
        }
    }
}

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

    #[test]
    fn basic_parsing() {
        let parsed = Template::parse(b"Hello, {% who %}!").unwrap();
        let chunks = vec![Chunk::Str(b"Hello, "), Chunk::Var(b"who"), Chunk::Str(b"!")];
        assert_eq!(Template { chunks: chunks }, parsed);
    }
}

<|endoftext|>