<commit_before>//! Builder type for `TwitterStream`.

pub use http::Method as RequestMethod;
pub use http::StatusCode;
pub use http::Uri;

use std::borrow::{Borrow, Cow};
use std::fmt::{self, Formatter};

use http::header::{HeaderValue, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE};
use http::Request;

use crate::service::HttpService;
use crate::token::Token;
use crate::util::fmt_join;
use crate::FutureTwitterStream;

/// A builder for `TwitterStream`.
///
/// ## Example
///
/// ```rust,no_run
/// use futures::prelude::*;
/// use twitter_stream::{builder::BoundingBox, Token};
///
/// # #[tokio::main]
/// # async fn main() {
/// let token = Token::new("consumer_key", "consumer_secret", "access_key", "access_secret");
///
/// const TOKYO_WARDS: &'static [BoundingBox] = &[BoundingBox::new((139.56, 35.53), (139.92, 35.82))];
///
/// // Prints geolocated English Tweets associated with the special wards of Tokyo.
/// twitter_stream::Builder::filter(token)
///     .locations(TOKYO_WARDS)
///     .language("en")
///     .listen()
///     .try_flatten_stream()
///     .try_for_each(|json| {
///         println!("{}", json);
///         future::ok(())
///     })
///     .await
///     .unwrap();
/// # }
/// ```
#[derive(Clone, Debug)]
pub struct Builder<'a, T = Token> {
    method: RequestMethod,
    endpoint: Uri,
    token: T,
    parameters: Parameters<'a>,
}

/// Parameters to the Streaming API.
#[derive(Clone, Debug, Default, oauth::Authorize)]
struct Parameters<'a> {
    #[oauth1(skip_if = "not")]
    stall_warnings: bool,
    filter_level: Option<FilterLevel>,
    #[oauth1(skip_if = "str::is_empty")]
    language: Cow<'a, str>,
    #[oauth1(encoded, fmt = "fmt_follow", skip_if = "<[_]>::is_empty")]
    follow: Cow<'a, [u64]>,
    #[oauth1(skip_if = "str::is_empty")]
    track: Cow<'a, str>,
    #[oauth1(encoded, fmt = "fmt_locations", skip_if = "<[_]>::is_empty")]
    #[allow(clippy::type_complexity)]
    locations: Cow<'a, [BoundingBox]>,
    #[oauth1(encoded)]
    count: Option<i32>,
}

/// A `BoundingBox` is a rectangular area on the globe specified by coordinates of
/// the southwest and northeast edges in decimal degrees.
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
pub struct BoundingBox {
    /// Longitude of the west side of the bounding box.
    pub west_longitude: f64,
    /// Latitude of the south side of the bounding box.
    pub south_latitude: f64,
    /// Longitude of the east side of the bounding box.
    pub east_longitude: f64,
    /// Latitude of the north side of the bounding box.
    pub north_latitude: f64,
}

str_enum! {
    /// Represents the `filter_level` parameter in API requests.
    #[derive(Clone, Debug, PartialEq, Hash, Eq)]
    pub enum FilterLevel {
        None = "none",
        Low = "low",
        Medium = "medium",
    }
}

impl<'a, C, A> Builder<'a, Token<C, A>>
where
    C: Borrow<str>,
    A: Borrow<str>,
{
    /// Create a builder for `POST statuses/filter` endpoint.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://dev.twitter.com/streaming/reference/post/statuses/filter
    pub fn filter(token: Token<C, A>) -> Self {
        const URI: &str = "https://stream.twitter.com/1.1/statuses/filter.json";
        Self::custom(RequestMethod::POST, Uri::from_static(URI), token)
    }

    /// Create a builder for `GET statuses/sample` endpoint.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://dev.twitter.com/streaming/reference/get/statuses/sample
    pub fn sample(token: Token<C, A>) -> Self {
        const URI: &str = "https://stream.twitter.com/1.1/statuses/sample.json";
        Self::custom(RequestMethod::GET, Uri::from_static(URI), token)
    }

    /// Constructs a builder for a Stream at a custom endpoint.
    pub fn custom(method: RequestMethod, endpoint: Uri, token: Token<C, A>) -> Self {
        Self {
            method,
            endpoint,
            token,
            parameters: Parameters::default(),
        }
    }

    /// Start listening on the Streaming API endpoint, returning a `Future` which resolves
    /// to a `Stream` yielding JSON messages from the API.
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    #[cfg(feature = "hyper")]
    #[cfg_attr(docsrs, doc(cfg(feature = "hyper")))]
    pub fn listen(&self) -> crate::hyper::FutureTwitterStream {
        let conn = hyper_tls::HttpsConnector::new();
        self.listen_with_client(hyper_pkg::Client::builder().build::<_, hyper_pkg::Body>(conn))
    }

    /// Same as `listen` except that it uses `client` to make HTTP request to the endpoint.
    ///
    /// # Panics
    ///
    /// This will call `<S as Service>::call` without checking for `<S as Service>::poll_ready`
    /// and may cause a panic if `client` is not ready to send an HTTP request yet.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use tower::ServiceExt;
    ///
    /// # async fn doc() {
    /// # let mut client = hyper_pkg::Client::new();
    /// # let token = twitter_stream::Token::new("", "", "", "");
    /// client.ready().await; // Ensure that the client is ready to send a request.
    /// let stream = twitter_stream::Builder::sample(token)
    ///     .listen_with_client(&mut client)
    ///     .await
    ///     .unwrap();
    /// # }
    /// ```
    pub fn listen_with_client<S, B>(&self, mut client: S) -> FutureTwitterStream<S::Future>
    where
        S: HttpService<B>,
        B: Default + From<Vec<u8>>,
    {
        let req = Request::builder().method(self.method.clone());

        #[cfg(feature = "gzip")]
        let req = req.header(
            http::header::ACCEPT_ENCODING,
            HeaderValue::from_static("gzip"),
        );

        let mut oauth = oauth::Builder::new(self.token.client.as_ref(), oauth::HmacSha1);
        oauth.token(self.token.token.as_ref());
        let req = if RequestMethod::POST == self.method {
            let oauth::Request {
                authorization,
                data,
            } = oauth.post_form(&self.endpoint, &self.parameters);

            req.uri(self.endpoint.clone())
                .header(AUTHORIZATION, authorization)
                .header(
                    CONTENT_TYPE,
                    HeaderValue::from_static("application/x-www-form-urlencoded"),
                )
                .header(CONTENT_LENGTH, data.len())
                .body(data.into_bytes().into())
                .unwrap()
        } else {
            let oauth::Request {
                authorization,
                data: uri,
            } = oauth.build(self.method.as_ref(), &self.endpoint, &self.parameters);

            req.uri(uri)
                .header(AUTHORIZATION, authorization)
                .body(B::default())
                .unwrap()
        };

        let response = client.call(req);
        FutureTwitterStream { response }
    }
}

impl<'a, C, A> Builder<'a, Token<C, A>> {
    /// Reset the HTTP request method to be used when connecting
    /// to the server.
    pub fn method(&mut self, method: RequestMethod) -> &mut Self {
        self.method = method;
        self
    }

    /// Reset the API endpoint URI to be connected.
    pub fn endpoint(&mut self, endpoint: Uri) -> &mut Self {
        self.endpoint = endpoint;
        self
    }

    /// Reset the token to be used to log into Twitter.
    pub fn token(&mut self, token: Token<C, A>) -> &mut Self {
        self.token = token;
        self
    }

    /// Set whether to receive messages when in danger of
    /// being disconnected.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#stall-warnings
    pub fn stall_warnings(&mut self, stall_warnings: bool) -> &mut Self {
        self.parameters.stall_warnings = stall_warnings;
        self
    }

    /// Set the minimum `filter_level` Tweet attribute to receive.
    /// The default is `FilterLevel::None`.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#filter-level
    pub fn filter_level(&mut self, filter_level: impl Into<Option<FilterLevel>>) -> &mut Self {
        self.parameters.filter_level = filter_level.into();
        self
    }

    /// Set a comma-separated language identifiers to receive Tweets
    /// written in the specified languages only.
    ///
    /// Setting an empty string will unset this parameter.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#language
    pub fn language(&mut self, language: impl Into<Cow<'a, str>>) -> &mut Self {
        self.parameters.language = language.into();
        self
    }

    /// Set a list of user IDs to receive Tweets from the specified users.
    ///
    /// Setting an empty slice will unset this parameter.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#follow
    pub fn follow(&mut self, follow: impl Into<Cow<'a, [u64]>>) -> &mut Self {
        self.parameters.follow = follow.into();
        self
    }

    /// A comma separated list of phrases to filter Tweets by.
    ///
    /// Setting an empty string will unset this parameter.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#track
    pub fn track(&mut self, track: impl Into<Cow<'a, str>>) -> &mut Self {
        self.parameters.track = track.into();
        self
    }

    /// Set a list of bounding boxes to filter Tweets by.
    ///
    /// Setting an empty slice will unset this parameter.
    ///
    /// See [`BoundingBox`](struct.BoundingBox.html) and
    /// the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#locations
    pub fn locations(&mut self, locations: impl Into<Cow<'a, [BoundingBox]>>) -> &mut Self {
        self.parameters.locations = locations.into();
        self
    }

    /// The `count` parameter.
    /// This parameter requires elevated access to use.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#count
    pub fn count(&mut self, count: impl Into<Option<i32>>) -> &mut Self {
        self.parameters.count = count.into();
        self
    }
}

impl BoundingBox {
    /// Creates a `BoundingBox` with two `(longitude, latitude)` pairs.
    ///
    /// The first argument specifies the southwest edge and the second specifies the northeast edge.
    ///
    /// # Example
    ///
    /// ```rust
    /// use twitter_stream::builder::BoundingBox;
    ///
    /// // Examples taken from Twitter's documentation.
    /// BoundingBox::new((-122.75, 36.8), (-121.75,37.8)); // San Francisco
    /// BoundingBox::new((-74.0, 40.0), (-73.0, 41.0)); // New York City
    /// ```
    pub fn new(
        (west_longitude, south_latitude): (f64, f64),
        (east_longitude, north_latitude): (f64, f64),
    ) -> Self {
        BoundingBox {
            west_longitude,
            south_latitude,
            east_longitude,
            north_latitude,
        }
    }
}

impl From<(f64, f64, f64, f64)> for BoundingBox {
    fn from(
        (west_longitude, south_latitude, east_longitude, north_latitude): (f64, f64, f64, f64),
    ) -> Self {
        BoundingBox {
            west_longitude,
            south_latitude,
            east_longitude,
            north_latitude,
        }
    }
}

impl From<((f64, f64), (f64, f64))> for BoundingBox {
    fn from(
        ((west_longitude, south_latitude), (east_longitude, north_latitude)): (
            (f64, f64),
            (f64, f64),
        ),
    ) -> Self {
        BoundingBox {
            west_longitude,
            south_latitude,
            east_longitude,
            north_latitude,
        }
    }
}

impl std::default::Default for FilterLevel {
    fn default() -> Self {
        FilterLevel::None
    }
}

impl std::fmt::Display for FilterLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        AsRef::<str>::as_ref(self).fmt(f)
    }
}

const COMMA: &str = "%2C";

fn fmt_follow(ids: &[u64], f: &mut Formatter<'_>) -> fmt::Result {
    fmt_join(ids, COMMA, f)
}

fn fmt_locations(locs: &[BoundingBox], f: &mut Formatter<'_>) -> fmt::Result {
    use std::mem::size_of;
    use std::slice;

    use static_assertions::const_assert;

    let locs: &[f64] = unsafe {
        // Safety:
        // `BoundingBox` is defined with the `#[repr(C)]` attribute so it is sound to interpret
        // the struct as a `[f64; 4]` and also the fields are guaranteed to be placed in
        // `[west_longitude, south_latitude, east_longitude, north_latitude]` order.
        // We are checking the size of `BoundingBox` here just to be sure.
        const_assert!(size_of::<BoundingBox>() == 4 * size_of::<f64>());
        let ptr: *const BoundingBox = locs.as_ptr();
        let n = 4 * <[BoundingBox]>::len(locs);
        slice::from_raw_parts(ptr as *const f64, n)
    };

    fmt_join(locs, COMMA, f)
}

#[allow(clippy::trivially_copy_pass_by_ref)]
fn not(p: &bool) -> bool {
    !p
}
<commit_msg>Make `BoundingBox::new` a const fn<commit_after>//! Builder type for `TwitterStream`.

pub use http::Method as RequestMethod;
pub use http::StatusCode;
pub use http::Uri;

use std::borrow::{Borrow, Cow};
use std::fmt::{self, Formatter};

use http::header::{HeaderValue, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE};
use http::Request;

use crate::service::HttpService;
use crate::token::Token;
use crate::util::fmt_join;
use crate::FutureTwitterStream;

/// A builder for `TwitterStream`.
///
/// ## Example
///
/// ```rust,no_run
/// use futures::prelude::*;
/// use twitter_stream::{builder::BoundingBox, Token};
///
/// # #[tokio::main]
/// # async fn main() {
/// let token = Token::new("consumer_key", "consumer_secret", "access_key", "access_secret");
///
/// const TOKYO_WARDS: &'static [BoundingBox] = &[BoundingBox::new((139.56, 35.53), (139.92, 35.82))];
///
/// // Prints geolocated English Tweets associated with the special wards of Tokyo.
/// twitter_stream::Builder::filter(token)
///     .locations(TOKYO_WARDS)
///     .language("en")
///     .listen()
///     .try_flatten_stream()
///     .try_for_each(|json| {
///         println!("{}", json);
///         future::ok(())
///     })
///     .await
///     .unwrap();
/// # }
/// ```
#[derive(Clone, Debug)]
pub struct Builder<'a, T = Token> {
    method: RequestMethod,
    endpoint: Uri,
    token: T,
    parameters: Parameters<'a>,
}

/// Parameters to the Streaming API.
#[derive(Clone, Debug, Default, oauth::Authorize)]
struct Parameters<'a> {
    #[oauth1(skip_if = "not")]
    stall_warnings: bool,
    filter_level: Option<FilterLevel>,
    #[oauth1(skip_if = "str::is_empty")]
    language: Cow<'a, str>,
    #[oauth1(encoded, fmt = "fmt_follow", skip_if = "<[_]>::is_empty")]
    follow: Cow<'a, [u64]>,
    #[oauth1(skip_if = "str::is_empty")]
    track: Cow<'a, str>,
    #[oauth1(encoded, fmt = "fmt_locations", skip_if = "<[_]>::is_empty")]
    #[allow(clippy::type_complexity)]
    locations: Cow<'a, [BoundingBox]>,
    #[oauth1(encoded)]
    count: Option<i32>,
}

/// A `BoundingBox` is a rectangular area on the globe specified by coordinates of
/// the southwest and northeast edges in decimal degrees.
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
pub struct BoundingBox {
    /// Longitude of the west side of the bounding box.
    pub west_longitude: f64,
    /// Latitude of the south side of the bounding box.
    pub south_latitude: f64,
    /// Longitude of the east side of the bounding box.
    pub east_longitude: f64,
    /// Latitude of the north side of the bounding box.
    pub north_latitude: f64,
}

str_enum! {
    /// Represents the `filter_level` parameter in API requests.
    #[derive(Clone, Debug, PartialEq, Hash, Eq)]
    pub enum FilterLevel {
        None = "none",
        Low = "low",
        Medium = "medium",
    }
}

impl<'a, C, A> Builder<'a, Token<C, A>>
where
    C: Borrow<str>,
    A: Borrow<str>,
{
    /// Create a builder for `POST statuses/filter` endpoint.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://dev.twitter.com/streaming/reference/post/statuses/filter
    pub fn filter(token: Token<C, A>) -> Self {
        const URI: &str = "https://stream.twitter.com/1.1/statuses/filter.json";
        Self::custom(RequestMethod::POST, Uri::from_static(URI), token)
    }

    /// Create a builder for `GET statuses/sample` endpoint.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://dev.twitter.com/streaming/reference/get/statuses/sample
    pub fn sample(token: Token<C, A>) -> Self {
        const URI: &str = "https://stream.twitter.com/1.1/statuses/sample.json";
        Self::custom(RequestMethod::GET, Uri::from_static(URI), token)
    }

    /// Constructs a builder for a Stream at a custom endpoint.
    pub fn custom(method: RequestMethod, endpoint: Uri, token: Token<C, A>) -> Self {
        Self {
            method,
            endpoint,
            token,
            parameters: Parameters::default(),
        }
    }

    /// Start listening on the Streaming API endpoint, returning a `Future` which resolves
    /// to a `Stream` yielding JSON messages from the API.
    ///
    /// # Panics
    ///
    /// This will panic if the underlying HTTPS connector failed to initialize.
    #[cfg(feature = "hyper")]
    #[cfg_attr(docsrs, doc(cfg(feature = "hyper")))]
    pub fn listen(&self) -> crate::hyper::FutureTwitterStream {
        let conn = hyper_tls::HttpsConnector::new();
        self.listen_with_client(hyper_pkg::Client::builder().build::<_, hyper_pkg::Body>(conn))
    }

    /// Same as `listen` except that it uses `client` to make HTTP request to the endpoint.
    ///
    /// # Panics
    ///
    /// This will call `<S as Service>::call` without checking for `<S as Service>::poll_ready`
    /// and may cause a panic if `client` is not ready to send an HTTP request yet.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use tower::ServiceExt;
    ///
    /// # async fn doc() {
    /// # let mut client = hyper_pkg::Client::new();
    /// # let token = twitter_stream::Token::new("", "", "", "");
    /// client.ready().await; // Ensure that the client is ready to send a request.
    /// let stream = twitter_stream::Builder::sample(token)
    ///     .listen_with_client(&mut client)
    ///     .await
    ///     .unwrap();
    /// # }
    /// ```
    pub fn listen_with_client<S, B>(&self, mut client: S) -> FutureTwitterStream<S::Future>
    where
        S: HttpService<B>,
        B: Default + From<Vec<u8>>,
    {
        let req = Request::builder().method(self.method.clone());

        #[cfg(feature = "gzip")]
        let req = req.header(
            http::header::ACCEPT_ENCODING,
            HeaderValue::from_static("gzip"),
        );

        let mut oauth = oauth::Builder::new(self.token.client.as_ref(), oauth::HmacSha1);
        oauth.token(self.token.token.as_ref());
        let req = if RequestMethod::POST == self.method {
            let oauth::Request {
                authorization,
                data,
            } = oauth.post_form(&self.endpoint, &self.parameters);

            req.uri(self.endpoint.clone())
                .header(AUTHORIZATION, authorization)
                .header(
                    CONTENT_TYPE,
                    HeaderValue::from_static("application/x-www-form-urlencoded"),
                )
                .header(CONTENT_LENGTH, data.len())
                .body(data.into_bytes().into())
                .unwrap()
        } else {
            let oauth::Request {
                authorization,
                data: uri,
            } = oauth.build(self.method.as_ref(), &self.endpoint, &self.parameters);

            req.uri(uri)
                .header(AUTHORIZATION, authorization)
                .body(B::default())
                .unwrap()
        };

        let response = client.call(req);
        FutureTwitterStream { response }
    }
}

impl<'a, C, A> Builder<'a, Token<C, A>> {
    /// Reset the HTTP request method to be used when connecting
    /// to the server.
    pub fn method(&mut self, method: RequestMethod) -> &mut Self {
        self.method = method;
        self
    }

    /// Reset the API endpoint URI to be connected.
    pub fn endpoint(&mut self, endpoint: Uri) -> &mut Self {
        self.endpoint = endpoint;
        self
    }

    /// Reset the token to be used to log into Twitter.
    pub fn token(&mut self, token: Token<C, A>) -> &mut Self {
        self.token = token;
        self
    }

    /// Set whether to receive messages when in danger of
    /// being disconnected.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#stall-warnings
    pub fn stall_warnings(&mut self, stall_warnings: bool) -> &mut Self {
        self.parameters.stall_warnings = stall_warnings;
        self
    }

    /// Set the minimum `filter_level` Tweet attribute to receive.
    /// The default is `FilterLevel::None`.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#filter-level
    pub fn filter_level(&mut self, filter_level: impl Into<Option<FilterLevel>>) -> &mut Self {
        self.parameters.filter_level = filter_level.into();
        self
    }

    /// Set a comma-separated language identifiers to receive Tweets
    /// written in the specified languages only.
    ///
    /// Setting an empty string will unset this parameter.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#language
    pub fn language(&mut self, language: impl Into<Cow<'a, str>>) -> &mut Self {
        self.parameters.language = language.into();
        self
    }

    /// Set a list of user IDs to receive Tweets from the specified users.
    ///
    /// Setting an empty slice will unset this parameter.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#follow
    pub fn follow(&mut self, follow: impl Into<Cow<'a, [u64]>>) -> &mut Self {
        self.parameters.follow = follow.into();
        self
    }

    /// A comma separated list of phrases to filter Tweets by.
    ///
    /// Setting an empty string will unset this parameter.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#track
    pub fn track(&mut self, track: impl Into<Cow<'a, str>>) -> &mut Self {
        self.parameters.track = track.into();
        self
    }

    /// Set a list of bounding boxes to filter Tweets by.
    ///
    /// Setting an empty slice will unset this parameter.
    ///
    /// See [`BoundingBox`](struct.BoundingBox.html) and
    /// the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#locations
    pub fn locations(&mut self, locations: impl Into<Cow<'a, [BoundingBox]>>) -> &mut Self {
        self.parameters.locations = locations.into();
        self
    }

    /// The `count` parameter.
    /// This parameter requires elevated access to use.
    ///
    /// See the [Twitter Developer Documentation][1] for more information.
    ///
    /// [1]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#count
    pub fn count(&mut self, count: impl Into<Option<i32>>) -> &mut Self {
        self.parameters.count = count.into();
        self
    }
}

impl BoundingBox {
    /// Creates a `BoundingBox` with two `(longitude, latitude)` pairs.
    ///
    /// The first argument specifies the southwest edge and the second specifies the northeast edge.
    ///
    /// # Example
    ///
    /// ```rust
    /// use twitter_stream::builder::BoundingBox;
    ///
    /// // Examples taken from Twitter's documentation.
    /// BoundingBox::new((-122.75, 36.8), (-121.75,37.8)); // San Francisco
    /// BoundingBox::new((-74.0, 40.0), (-73.0, 41.0)); // New York City
    /// ```
    pub const fn new(
        (west_longitude, south_latitude): (f64, f64),
        (east_longitude, north_latitude): (f64, f64),
    ) -> Self {
        BoundingBox {
            west_longitude,
            south_latitude,
            east_longitude,
            north_latitude,
        }
    }
}

impl From<(f64, f64, f64, f64)> for BoundingBox {
    fn from(
        (west_longitude, south_latitude, east_longitude, north_latitude): (f64, f64, f64, f64),
    ) -> Self {
        BoundingBox {
            west_longitude,
            south_latitude,
            east_longitude,
            north_latitude,
        }
    }
}

impl From<((f64, f64), (f64, f64))> for BoundingBox {
    fn from(
        ((west_longitude, south_latitude), (east_longitude, north_latitude)): (
            (f64, f64),
            (f64, f64),
        ),
    ) -> Self {
        BoundingBox {
            west_longitude,
            south_latitude,
            east_longitude,
            north_latitude,
        }
    }
}

impl std::default::Default for FilterLevel {
    fn default() -> Self {
        FilterLevel::None
    }
}

impl std::fmt::Display for FilterLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        AsRef::<str>::as_ref(self).fmt(f)
    }
}

const COMMA: &str = "%2C";

fn fmt_follow(ids: &[u64], f: &mut Formatter<'_>) -> fmt::Result {
    fmt_join(ids, COMMA, f)
}

fn fmt_locations(locs: &[BoundingBox], f: &mut Formatter<'_>) -> fmt::Result {
    use std::mem::size_of;
    use std::slice;

    use static_assertions::const_assert;

    let locs: &[f64] = unsafe {
        // Safety:
        // `BoundingBox` is defined with the `#[repr(C)]` attribute so it is sound to interpret
        // the struct as a `[f64; 4]` and also the fields are guaranteed to be placed in
        // `[west_longitude, south_latitude, east_longitude, north_latitude]` order.
        // We are checking the size of `BoundingBox` here just to be sure.
        const_assert!(size_of::<BoundingBox>() == 4 * size_of::<f64>());
        let ptr: *const BoundingBox = locs.as_ptr();
        let n = 4 * <[BoundingBox]>::len(locs);
        slice::from_raw_parts(ptr as *const f64, n)
    };

    fmt_join(locs, COMMA, f)
}

#[allow(clippy::trivially_copy_pass_by_ref)]
fn not(p: &bool) -> bool {
    !p
}
<|endoftext|>


<commit_before>#![feature(plugin, custom_attribute)]
#![plugin(gfx_macros)]

extern crate piston;
extern crate piston_window;
extern crate vecmath;
extern crate camera_controllers;
extern crate gfx;
extern crate gfx_device_gl;
extern crate glfw_window;

use std::cell::RefCell;
use std::rc::Rc;
use piston_window::*;
use piston::event::*;
use piston::window::{ AdvancedWindow, WindowSettings };
use camera_controllers::{
    FirstPersonSettings,
    FirstPerson,
    CameraPerspective,
    model_view_projection
};
use gfx::traits::*;
use glfw_window::{ GlfwWindow, OpenGL };

//----------------------------------------
// Cube associated data

#[vertex_format]
#[derive(Copy, Clone)]
struct Vertex {
    #[as_float]
    a_pos: [i8; 3],
    #[as_float]
    a_tex_coord: [u8; 2],
}

impl Vertex {
    fn new(pos: [i8; 3], tc: [u8; 2]) -> Vertex {
        Vertex {
            a_pos: pos,
            a_tex_coord: tc,
        }
    }
}

#[shader_param]
struct Params<R: gfx::Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    t_color: gfx::shade::TextureParam<R>,
}

//----------------------------------------

fn main() {
    let (win_width, win_height) = (640, 480);
    let window = Rc::new(RefCell::new(GlfwWindow::new(
        OpenGL::_3_2,
        WindowSettings::new("piston-example-gfx_cube", [win_width, win_height])
        .exit_on_esc(true)
        .samples(4)
    ).capture_cursor(true)));

    let events = PistonWindow::new(window, empty_app());

    let vertex_data = vec![
        //top (0, 0, 1)
        Vertex::new([-1, -1,  1], [0, 0]),
        Vertex::new([ 1, -1,  1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //bottom (0, 0, -1)
        Vertex::new([ 1,  1, -1], [0, 0]),
        Vertex::new([-1,  1, -1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
        //right (1, 0, 0)
        Vertex::new([ 1, -1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([ 1, -1,  1], [0, 1]),
        //left (-1, 0, 0)
        Vertex::new([-1,  1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([-1,  1, -1], [0, 1]),
        //front (0, 1, 0)
        Vertex::new([-1,  1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //back (0, -1, 0)
        Vertex::new([ 1, -1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
    ];

    let mesh = events.canvas.borrow_mut().factory.create_mesh(&vertex_data);

    let index_data: &[u8] = &[
         0,  1,  2,  2,  3,  0, // top
         4,  6,  5,  6,  4,  7, // bottom
         8,  9, 10, 10, 11,  8, // right
        12, 14, 13, 14, 12, 16, // left
        16, 18, 17, 18, 16, 19, // front
        20, 21, 22, 22, 23, 20, // back
    ];

    let slice = events.canvas.borrow_mut().factory.create_buffer_index(index_data)
                       .to_slice(gfx::PrimitiveType::TriangleList);

    let tinfo = gfx::tex::TextureInfo {
        width: 1, height: 1, depth: 1, levels: 1,
        kind: gfx::tex::TextureKind::Texture2D,
        format: gfx::tex::RGBA8,
    };
    let img_info = tinfo.to_image_info();
    let texture = events.canvas.borrow_mut().factory.create_texture(tinfo).unwrap();
    events.canvas.borrow_mut().factory.update_texture(
        &texture,
        &img_info,
        &[0x20u8, 0xA0, 0xC0, 0x00],
        Some(gfx::tex::TextureKind::Texture2D)
    ).unwrap();

    let sampler = events.canvas.borrow_mut().factory.create_sampler(
        gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Bilinear,
            gfx::tex::WrapMode::Clamp));

    let program = {
        let canvas = &mut *events.canvas.borrow_mut();
        let vertex = gfx::ShaderSource {
            glsl_120: Some(include_bytes!("cube_120.glslv")),
            glsl_150: Some(include_bytes!("cube_150.glslv")),
            .. gfx::ShaderSource::empty()
        };
        let fragment = gfx::ShaderSource {
            glsl_120: Some(include_bytes!("cube_120.glslf")),
            glsl_150: Some(include_bytes!("cube_150.glslf")),
            .. gfx::ShaderSource::empty()
        };
        canvas.factory.link_program_source(vertex, fragment,
            &canvas.device.get_capabilities()).unwrap()
    };

    let mut data = Params {
        u_model_view_proj: vecmath::mat4_id(),
        t_color: (texture, Some(sampler)),
    };

    let model = vecmath::mat4_id();
    let projection = CameraPerspective {
        fov: 90.0, near_clip: 0.1, far_clip: 1000.0,
        aspect_ratio: (win_width as f32) / (win_height as f32)
    }.projection();
    let mut first_person = FirstPerson::new([0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd());
    let state = gfx::DrawState::new().depth(gfx::state::Comparison::LessEqual, true);

    for e in events {
        first_person.event(&e);

        e.draw_3d(|canvas| {
            let args = e.render_args().unwrap();
            canvas.renderer.clear(
                gfx::ClearData {
                    color: [0.3, 0.3, 0.3, 1.0],
                    depth: 1.0,
                    stencil: 0,
                },
                gfx::COLOR | gfx::DEPTH,
                &canvas.output
            );
            data.u_model_view_proj = model_view_projection(
                model,
                first_person.camera(args.ext_dt).orthogonal(),
                projection
            );
            canvas.renderer.draw(&(&mesh, slice.clone(), &program, &data, &state),
                &canvas.output).unwrap();
            canvas.device.submit(canvas.renderer.as_buffer());
            canvas.renderer.reset();
        });
    }
}
<commit_msg>Removed stuff now handled by PistonWindow<commit_after>#![feature(plugin, custom_attribute)]
#![plugin(gfx_macros)]

extern crate piston;
extern crate piston_window;
extern crate vecmath;
extern crate camera_controllers;
extern crate gfx;
extern crate gfx_device_gl;
extern crate glfw_window;

use std::cell::RefCell;
use std::rc::Rc;
use piston_window::*;
use piston::event::*;
use piston::window::{ AdvancedWindow, WindowSettings };
use camera_controllers::{
    FirstPersonSettings,
    FirstPerson,
    CameraPerspective,
    model_view_projection
};
use gfx::traits::*;
use glfw_window::{ GlfwWindow, OpenGL };

//----------------------------------------
// Cube associated data

#[vertex_format]
#[derive(Copy, Clone)]
struct Vertex {
    #[as_float]
    a_pos: [i8; 3],
    #[as_float]
    a_tex_coord: [u8; 2],
}

impl Vertex {
    fn new(pos: [i8; 3], tc: [u8; 2]) -> Vertex {
        Vertex {
            a_pos: pos,
            a_tex_coord: tc,
        }
    }
}

#[shader_param]
struct Params<R: gfx::Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    t_color: gfx::shade::TextureParam<R>,
}

//----------------------------------------

fn main() {
    let (win_width, win_height) = (640, 480);
    let window = Rc::new(RefCell::new(GlfwWindow::new(
        OpenGL::_3_2,
        WindowSettings::new("piston-example-gfx_cube", [win_width, win_height])
        .exit_on_esc(true)
        .samples(4)
    ).capture_cursor(true)));

    let events = PistonWindow::new(window, empty_app());

    let vertex_data = vec![
        //top (0, 0, 1)
        Vertex::new([-1, -1,  1], [0, 0]),
        Vertex::new([ 1, -1,  1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //bottom (0, 0, -1)
        Vertex::new([ 1,  1, -1], [0, 0]),
        Vertex::new([-1,  1, -1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
        //right (1, 0, 0)
        Vertex::new([ 1, -1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([ 1, -1,  1], [0, 1]),
        //left (-1, 0, 0)
        Vertex::new([-1,  1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([-1,  1, -1], [0, 1]),
        //front (0, 1, 0)
        Vertex::new([-1,  1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //back (0, -1, 0)
        Vertex::new([ 1, -1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
    ];

    let mesh = events.canvas.borrow_mut().factory.create_mesh(&vertex_data);

    let index_data: &[u8] = &[
         0,  1,  2,  2,  3,  0, // top
         4,  6,  5,  6,  4,  7, // bottom
         8,  9, 10, 10, 11,  8, // right
        12, 14, 13, 14, 12, 16, // left
        16, 18, 17, 18, 16, 19, // front
        20, 21, 22, 22, 23, 20, // back
    ];

    let slice = events.canvas.borrow_mut().factory.create_buffer_index(index_data)
                       .to_slice(gfx::PrimitiveType::TriangleList);

    let tinfo = gfx::tex::TextureInfo {
        width: 1, height: 1, depth: 1, levels: 1,
        kind: gfx::tex::TextureKind::Texture2D,
        format: gfx::tex::RGBA8,
    };
    let img_info = tinfo.to_image_info();
    let texture = events.canvas.borrow_mut().factory.create_texture(tinfo).unwrap();
    events.canvas.borrow_mut().factory.update_texture(
        &texture,
        &img_info,
        &[0x20u8, 0xA0, 0xC0, 0x00],
        Some(gfx::tex::TextureKind::Texture2D)
    ).unwrap();

    let sampler = events.canvas.borrow_mut().factory.create_sampler(
        gfx::tex::SamplerInfo::new(gfx::tex::FilterMethod::Bilinear,
            gfx::tex::WrapMode::Clamp));

    let program = {
        let canvas = &mut *events.canvas.borrow_mut();
        let vertex = gfx::ShaderSource {
            glsl_120: Some(include_bytes!("cube_120.glslv")),
            glsl_150: Some(include_bytes!("cube_150.glslv")),
            .. gfx::ShaderSource::empty()
        };
        let fragment = gfx::ShaderSource {
            glsl_120: Some(include_bytes!("cube_120.glslf")),
            glsl_150: Some(include_bytes!("cube_150.glslf")),
            .. gfx::ShaderSource::empty()
        };
        canvas.factory.link_program_source(vertex, fragment,
            &canvas.device.get_capabilities()).unwrap()
    };

    let mut data = Params {
        u_model_view_proj: vecmath::mat4_id(),
        t_color: (texture, Some(sampler)),
    };

    let model = vecmath::mat4_id();
    let projection = CameraPerspective {
        fov: 90.0, near_clip: 0.1, far_clip: 1000.0,
        aspect_ratio: (win_width as f32) / (win_height as f32)
    }.projection();
    let mut first_person = FirstPerson::new([0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd());
    let state = gfx::DrawState::new().depth(gfx::state::Comparison::LessEqual, true);

    for e in events {
        first_person.event(&e);

        e.draw_3d(|canvas| {
            let args = e.render_args().unwrap();
            canvas.renderer.clear(
                gfx::ClearData {
                    color: [0.3, 0.3, 0.3, 1.0],
                    depth: 1.0,
                    stencil: 0,
                },
                gfx::COLOR | gfx::DEPTH,
                &canvas.output
            );
            data.u_model_view_proj = model_view_projection(
                model,
                first_person.camera(args.ext_dt).orthogonal(),
                projection
            );
            canvas.renderer.draw(&(&mesh, slice.clone(), &program, &data, &state),
                &canvas.output).unwrap();
        });
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use super::{Args, Configuration};
use client::Context;
use model::{Message, Permissions};
use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;

pub type Check = Fn(&mut Context, &Message, &mut Args, &Arc<Command>) -> bool
                     + Send
                     + Sync
                     + 'static;
pub type Exec = Fn(&mut Context, &Message, Args) -> Result<(), Error> + Send + Sync + 'static;
pub type Help = Fn(&mut Context, &Message, HashMap<String, Arc<CommandGroup>>, Args)
                   -> Result<(), Error>
                    + Send
                    + Sync
                    + 'static;
pub type BeforeHook = Fn(&mut Context, &Message, &str) -> bool + Send + Sync + 'static;
pub type AfterHook = Fn(&mut Context, &Message, &str, Result<(), Error>) + Send + Sync + 'static;
pub(crate) type InternalCommand = Arc<Command>;
pub type PrefixCheck = Fn(&mut Context, &Message) -> Option<String> + Send + Sync + 'static;

pub enum CommandOrAlias {
    Alias(String),
    Command(InternalCommand),
}

/// An error from a command.
#[derive(Clone, Debug)]
pub struct Error(String);

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl<E: StdError> From<E> for Error {
    fn from(e: E) -> Self {
        Error(format!("{}", e))
    }
}

impl From<Custom> for Error {
    fn from(Custom(e): Custom) -> Self {
        Error(e)
    }
}

/// A custom "variant" of [`Error`]
///
/// [`Error`]: #struct.Error.html
#[derive(Clone, Debug)]
pub struct Custom(String);

impl From<String> for Custom {
    fn from(e: String) -> Custom {
        Custom(e)
    }
}

/// Command function type. Allows to access internal framework things inside
/// your commands.
pub enum CommandType {
    StringResponse(String),
    Basic(Box<Exec>),
    WithCommands(Box<Help>),
}

pub struct CommandGroup {
    pub prefix: Option<String>,
    pub commands: HashMap<String, CommandOrAlias>,
    /// Some fields taken from Command
    pub bucket: Option<String>,
    pub required_permissions: Permissions,
    pub allowed_roles: Vec<String>,
    pub help_available: bool,
    pub dm_only: bool,
    pub guild_only: bool,
    pub owners_only: bool,
}

/// Command struct used to store commands internally.
pub struct Command {
    /// A set of checks to be called prior to executing the command. The checks
    /// will short-circuit on the first check that returns `false`.
    pub checks: Vec<Box<Check>>,
    /// Function called when the command is called.
    pub exec: CommandType,
    /// Ratelimit bucket.
    pub bucket: Option<String>,
    /// Command description, used by other commands.
    pub desc: Option<String>,
    /// Example arguments, used by other commands.
    pub example: Option<String>,
    /// Command usage schema, used by other commands.
    pub usage: Option<String>,
    /// Minumum amount of arguments that should be passed.
    pub min_args: Option<i32>,
    /// Maximum amount of arguments that can be passed.
    pub max_args: Option<i32>,
    /// Permissions required to use this command.
    pub required_permissions: Permissions,
    /// Roles allowed to use this command.
    pub allowed_roles: Vec<String>,
    /// Whether command should be displayed in help list or not, used by other commands.
    pub help_available: bool,
    /// Whether command can be used only privately or not.
    pub dm_only: bool,
    /// Whether command can be used only in guilds or not.
    pub guild_only: bool,
    /// Whether command can only be used by owners or not.
    pub owners_only: bool,
    pub(crate) aliases: Vec<String>,
}

impl Command {
    pub fn new<F>(f: F) -> Self
        where F: Fn(&mut Context, &Message, Args) -> Result<(), Error> + Send + Sync + 'static {
        Command {
            exec: CommandType::Basic(Box::new(f)),
            ..Command::default()
        }
    }
}

impl Default for Command {
    fn default() -> Command {
        Command {
            aliases: Vec::new(),
            checks: Vec::default(),
            exec: CommandType::Basic(Box::new(|_, _, _| Ok(()))),
            desc: None,
            usage: None,
            example: None,
            min_args: None,
            bucket: None,
            max_args: None,
            required_permissions: Permissions::empty(),
            dm_only: false,
            guild_only: false,
            help_available: true,
            owners_only: false,
            allowed_roles: Vec::new(),
        }
    }
}

pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Option<Vec<usize>> {
    if !conf.prefixes.is_empty() || conf.dynamic_prefix.is_some() {
        // Find out if they were mentioned. If not, determine if the prefix
        // was used. If not, return None.
        let mut positions: Vec<usize> = vec![];

        if let Some(mention_end) = find_mention_end(&msg.content, conf) {
            positions.push(mention_end);
        } else if let Some(ref func) = conf.dynamic_prefix {
            if let Some(x) = func(ctx, msg) {
                if msg.content.starts_with(&x) {
                    positions.push(x.len());
                }
            } else {
                for n in &conf.prefixes {
                    if msg.content.starts_with(n) {
                        positions.push(n.len());
                    }
                }
            }
        } else {
            for n in &conf.prefixes {
                if msg.content.starts_with(n) {
                    positions.push(n.len());
                }
            }
        };

        if positions.is_empty() {
            return None;
        }

        if conf.allow_whitespace {
            let pos = *unsafe { positions.get_unchecked(0) };

            positions.insert(0, pos + 1);
        }

        Some(positions)
    } else if conf.on_mention.is_some() {
        find_mention_end(&msg.content, conf).map(|mention_end| {
            let mut positions = vec![mention_end];

            if conf.allow_whitespace {
                positions.insert(0, mention_end + 1);
            }

            positions
        })
    } else {
        None
    }
}

fn find_mention_end(content: &str, conf: &Configuration) -> Option<usize> {
    conf.on_mention.as_ref().and_then(|mentions| {
        mentions
            .iter()
            .find(|mention| content.starts_with(&mention[..]))
            .map(|m| m.len())
    })
}
<commit_msg>Use display instead of std::error::Error<commit_after>use std::sync::Arc;
use super::{Args, Configuration};
use client::Context;
use model::{Message, Permissions};
use std::collections::HashMap;
use std::fmt;

pub type Check = Fn(&mut Context, &Message, &mut Args, &Arc<Command>) -> bool
                     + Send
                     + Sync
                     + 'static;
pub type Exec = Fn(&mut Context, &Message, Args) -> Result<(), Error> + Send + Sync + 'static;
pub type Help = Fn(&mut Context, &Message, HashMap<String, Arc<CommandGroup>>, Args)
                   -> Result<(), Error>
                    + Send
                    + Sync
                    + 'static;
pub type BeforeHook = Fn(&mut Context, &Message, &str) -> bool + Send + Sync + 'static;
pub type AfterHook = Fn(&mut Context, &Message, &str, Result<(), Error>) + Send + Sync + 'static;
pub(crate) type InternalCommand = Arc<Command>;
pub type PrefixCheck = Fn(&mut Context, &Message) -> Option<String> + Send + Sync + 'static;

pub enum CommandOrAlias {
    Alias(String),
    Command(InternalCommand),
}

/// An error from a command.
#[derive(Clone, Debug)]
pub struct Error(String);

impl<D: fmt::Display> From<D> for Error {
    fn from(d: D) -> Self {
        Error(format!("{}", d))
    }
}

/// Command function type. Allows to access internal framework things inside
/// your commands.
pub enum CommandType {
    StringResponse(String),
    Basic(Box<Exec>),
    WithCommands(Box<Help>),
}

pub struct CommandGroup {
    pub prefix: Option<String>,
    pub commands: HashMap<String, CommandOrAlias>,
    /// Some fields taken from Command
    pub bucket: Option<String>,
    pub required_permissions: Permissions,
    pub allowed_roles: Vec<String>,
    pub help_available: bool,
    pub dm_only: bool,
    pub guild_only: bool,
    pub owners_only: bool,
}

/// Command struct used to store commands internally.
pub struct Command {
    /// A set of checks to be called prior to executing the command. The checks
    /// will short-circuit on the first check that returns `false`.
    pub checks: Vec<Box<Check>>,
    /// Function called when the command is called.
    pub exec: CommandType,
    /// Ratelimit bucket.
    pub bucket: Option<String>,
    /// Command description, used by other commands.
    pub desc: Option<String>,
    /// Example arguments, used by other commands.
    pub example: Option<String>,
    /// Command usage schema, used by other commands.
    pub usage: Option<String>,
    /// Minumum amount of arguments that should be passed.
    pub min_args: Option<i32>,
    /// Maximum amount of arguments that can be passed.
    pub max_args: Option<i32>,
    /// Permissions required to use this command.
    pub required_permissions: Permissions,
    /// Roles allowed to use this command.
    pub allowed_roles: Vec<String>,
    /// Whether command should be displayed in help list or not, used by other commands.
    pub help_available: bool,
    /// Whether command can be used only privately or not.
    pub dm_only: bool,
    /// Whether command can be used only in guilds or not.
    pub guild_only: bool,
    /// Whether command can only be used by owners or not.
    pub owners_only: bool,
    pub(crate) aliases: Vec<String>,
}

impl Command {
    pub fn new<F>(f: F) -> Self
        where F: Fn(&mut Context, &Message, Args) -> Result<(), Error> + Send + Sync + 'static {
        Command {
            exec: CommandType::Basic(Box::new(f)),
            ..Command::default()
        }
    }
}

impl Default for Command {
    fn default() -> Command {
        Command {
            aliases: Vec::new(),
            checks: Vec::default(),
            exec: CommandType::Basic(Box::new(|_, _, _| Ok(()))),
            desc: None,
            usage: None,
            example: None,
            min_args: None,
            bucket: None,
            max_args: None,
            required_permissions: Permissions::empty(),
            dm_only: false,
            guild_only: false,
            help_available: true,
            owners_only: false,
            allowed_roles: Vec::new(),
        }
    }
}

pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Option<Vec<usize>> {
    if !conf.prefixes.is_empty() || conf.dynamic_prefix.is_some() {
        // Find out if they were mentioned. If not, determine if the prefix
        // was used. If not, return None.
        let mut positions: Vec<usize> = vec![];

        if let Some(mention_end) = find_mention_end(&msg.content, conf) {
            positions.push(mention_end);
        } else if let Some(ref func) = conf.dynamic_prefix {
            if let Some(x) = func(ctx, msg) {
                if msg.content.starts_with(&x) {
                    positions.push(x.len());
                }
            } else {
                for n in &conf.prefixes {
                    if msg.content.starts_with(n) {
                        positions.push(n.len());
                    }
                }
            }
        } else {
            for n in &conf.prefixes {
                if msg.content.starts_with(n) {
                    positions.push(n.len());
                }
            }
        };

        if positions.is_empty() {
            return None;
        }

        if conf.allow_whitespace {
            let pos = *unsafe { positions.get_unchecked(0) };

            positions.insert(0, pos + 1);
        }

        Some(positions)
    } else if conf.on_mention.is_some() {
        find_mention_end(&msg.content, conf).map(|mention_end| {
            let mut positions = vec![mention_end];

            if conf.allow_whitespace {
                positions.insert(0, mention_end + 1);
            }

            positions
        })
    } else {
        None
    }
}

fn find_mention_end(content: &str, conf: &Configuration) -> Option<usize> {
    conf.on_mention.as_ref().and_then(|mentions| {
        mentions
            .iter()
            .find(|mention| content.starts_with(&mention[..]))
            .map(|m| m.len())
    })
}
<|endoftext|>


<commit_before>use prometheus::*;

lazy_static! {
    pub static ref EXPORTER_FAILURE_COUNTER: Counter = register_counter!("glusterfs_failures", "Failures internal to glusterfs_exporter").unwrap();

    pub static ref PROFILE_OP_GAUGE: GaugeVec = register_gauge_vec!("glusterfs_profile_ops", "Glusterfs volume profile Ops", &["volume"]).unwrap();
    pub static ref BLOCKSTATS_COUNTER: GaugeVec = register_gauge_vec!("glusterfs_blockstats", "Glusterfs volume block stats", &["volume", "optype", "size_bucket"]).unwrap();
    pub static ref BLOCKSTATS_TOTAL_COUNTER: GaugeVec = register_gauge_vec!("glusterfs_blockstats_total", "Glusterfs volume total block stats", &["volume", "brick", "optype"]).unwrap();
    pub static ref FOPSTATS_AVGLATENCY: GaugeVec = register_gauge_vec!("glusterfs_fop_avglatency", "Glusterfs volume file operation avg latency", &["volume", "brick", "fop_name"]).unwrap();
    pub static ref FOPSTATS_MINLATENCY: GaugeVec = register_gauge_vec!("glusterfs_fop_minlatency", "Glusterfs volume file operation min latency", &["volume", "brick", "fop_name"]).unwrap();
    pub static ref FOPSTATS_MAXLATENCY: GaugeVec = register_gauge_vec!("glusterfs_fop_maxlatency", "Glusterfs volume file operation max latency", &["volume", "brick", "fop_name"]).unwrap();
    pub static ref FOPSTATS_COUNTER: GaugeVec = register_gauge_vec!("glusterfs_fop_hits", "Glusterfs volume file operation hits", &["volume", "brick", "fop_name"]).unwrap();
}
<commit_msg>properly initialize BLOCKSTATS_COUNTER metric<commit_after>use prometheus::*;

lazy_static! {
    pub static ref EXPORTER_FAILURE_COUNTER: Counter = register_counter!("glusterfs_failures", "Failures internal to glusterfs_exporter").unwrap();

    pub static ref PROFILE_OP_GAUGE: GaugeVec = register_gauge_vec!("glusterfs_profile_ops", "Glusterfs volume profile Ops", &["volume"]).unwrap();
    pub static ref BLOCKSTATS_COUNTER: GaugeVec = register_gauge_vec!("glusterfs_blockstats", "Glusterfs volume block stats", &["volume", "brick", "optype", "size_bucket"]).unwrap();
    pub static ref BLOCKSTATS_TOTAL_COUNTER: GaugeVec = register_gauge_vec!("glusterfs_blockstats_total", "Glusterfs volume total block stats", &["volume", "brick", "optype"]).unwrap();
    pub static ref FOPSTATS_AVGLATENCY: GaugeVec = register_gauge_vec!("glusterfs_fop_avglatency", "Glusterfs volume file operation avg latency", &["volume", "brick", "fop_name"]).unwrap();
    pub static ref FOPSTATS_MINLATENCY: GaugeVec = register_gauge_vec!("glusterfs_fop_minlatency", "Glusterfs volume file operation min latency", &["volume", "brick", "fop_name"]).unwrap();
    pub static ref FOPSTATS_MAXLATENCY: GaugeVec = register_gauge_vec!("glusterfs_fop_maxlatency", "Glusterfs volume file operation max latency", &["volume", "brick", "fop_name"]).unwrap();
    pub static ref FOPSTATS_COUNTER: GaugeVec = register_gauge_vec!("glusterfs_fop_hits", "Glusterfs volume file operation hits", &["volume", "brick", "fop_name"]).unwrap();
}
<|endoftext|>


<commit_before>// Copyright (C) 2020 Julen Ruiz Aizpuru, https://github.com/julen

//! # Plurr
//!
//! A library for handling plurals/genders/conditionals.

use std::collections::HashMap;

mod plurals;
mod utils;

const _PLURAL: &str = "_PLURAL";

#[derive(Debug, PartialEq)]
pub enum PlurrError {
    EmptyPlaceholder,
    EmptyVariants,
    NotZeroOrPositiveValue,
    UnmatchedOpeningBrace,
    UnmatchedClosingBrace,
    UndefinedParameter,
    UndefinedParameterPairs,
}

type PlurrParams = HashMap<String, String>;

#[derive(Debug)]
pub struct Plurr<'a> {
    locale: &'a str,
    auto_plurals: bool,
    strict: bool,
    params: PlurrParams,
}

impl<'a> Default for Plurr<'a> {
    fn default() -> Self {
        Plurr::new()
    }
}

impl<'a> Plurr<'a> {
    pub fn new() -> Self {
        Self {
            locale: "en",
            auto_plurals: true,
            strict: true,
            params: PlurrParams::new(),
        }
    }

    /// Sets the locale for Plurr. If this is not called or an incompatible
    /// `locale_code` is provided, it defaults to English.
    pub fn locale(&mut self, locale_code: &'a str) -> &mut Self {
        self.locale = locale_code;
        self
    }

    /// Toggles the auto-plural behavior.
    /// * With auto-plurals, Plurr will automatically select the plural form for
    /// the current locale.
    /// * Without auto-plurals, you will need to manually provide an `N_PLURAL`
    /// parameter before calling `format()`.
    pub fn auto_plurals(&mut self, auto_plurals: bool) -> &mut Self {
        self.auto_plurals = auto_plurals;
        self
    }

    /// Toggles strict mode. In strict mode, errors are not skipped.
    pub fn strict(&mut self, strict: bool) -> &mut Self {
        self.strict = strict;
        self
    }

    /// Stores a parameter value. Parameters must be fed before calling
    /// `format()`.
    pub fn param<T: ToString>(&mut self, key: &str, value: T) -> &mut Self {
        self.params.insert(key.to_string(), value.to_string());
        self
    }

    /// Formats a Plurr string and returns the evaluated string.
    ///
    /// For the format spec please check out the project
    /// [README](https://github.com/loctools/plurr/blob/master/README.md).
    ///
    /// # Examples
    ///
    /// ```
    /// use plurr::Plurr;
    ///
    /// let mut p = Plurr::new();
    /// let s = "Delete {N_PLURAL:{N} file|{N} files}?";
    ///
    /// assert_eq!(
    ///     p.param("N", "5").format(s),
    ///     Ok("Delete 5 files?".to_string())
    /// );
    /// ```
    pub fn format(&mut self, value: &str) -> Result<String, PlurrError> {
        let mut bracket_count = 0;
        let mut blocks = vec!["".to_string()];

        for chunk in utils::split_string(value, "{", "}") {
            if chunk == "{" {
                bracket_count += 1;
                blocks.push("".to_string());
                continue;
            }

            if chunk == "}" {
                bracket_count -= 1;
                if bracket_count < 0 {
                    return Err(PlurrError::UnmatchedClosingBrace);
                }

                let block = blocks.pop().unwrap();
                let colon_pos_maybe = block.find(':');

                let name = match colon_pos_maybe {
                    Some(colon_pos) => {
                        if self.strict && colon_pos == 0 {
                            return Err(PlurrError::EmptyPlaceholder);
                        }
                        // Multiple choices
                        &block[0..colon_pos]
                    }
                    // Simple placeholder
                    None => &block,
                };

                if !self.params.contains_key(name) {
                    let p_pos = name.find(_PLURAL);
                    if self.auto_plurals
                        && p_pos.is_some()
                        && p_pos.unwrap() == name.len() - _PLURAL.len()
                    {
                        let prefix = name[0..p_pos.unwrap()].to_string();
                        if !self.params.contains_key(&prefix) && self.strict {
                            return Err(PlurrError::UndefinedParameterPairs);
                        }

                        // This is where the actual parameter replacing happens
                        let prefix_value_maybe = self.params.get(&prefix).unwrap();
                        let prefix_value;
                        match prefix_value_maybe.parse::<usize>() {
                            Ok(parsed_value) => prefix_value = parsed_value,
                            Err(_) => {
                                if self.strict {
                                    return Err(PlurrError::NotZeroOrPositiveValue);
                                }
                                prefix_value = 0;
                            }
                        }

                        self.params.insert(
                            name.to_owned(),
                            plurals::plural_func(&self.locale, prefix_value).to_string(),
                        );
                    } else if self.strict {
                        return Err(PlurrError::UndefinedParameter);
                    }
                }

                let result = match colon_pos_maybe {
                    Some(colon_pos) => {
                        // multiple choices
                        let block_len = block.len();

                        if self.strict && colon_pos == block_len - 1 {
                            return Err(PlurrError::EmptyVariants);
                        }

                        let value = self.params.get(name).unwrap();
                        let mut choice_idx: usize = value.parse().unwrap_or(0);

                        let content_start = colon_pos + 1;
                        let clean_block = &block[content_start..];

                        let parts: Vec<&str> = clean_block.split('|').collect();
                        if choice_idx >= parts.len() {
                            choice_idx = parts.len() - 1;
                        }
                        parts[choice_idx]
                    }
                    None => self.params.get(name).unwrap(),
                };

                let index = blocks.len() - 1;
                blocks[index].push_str(result);

                continue;
            }

            let index = blocks.len() - 1;
            blocks[index].push_str(&chunk);
        }

        if bracket_count > 0 {
            return Err(PlurrError::UnmatchedOpeningBrace);
        }

        // Clear params for subsequent calls
        self.params.clear();

        Ok(blocks[0].clone())
    }
}
<commit_msg>Cleanup: simplify prefix value assignment<commit_after>// Copyright (C) 2020 Julen Ruiz Aizpuru, https://github.com/julen

//! # Plurr
//!
//! A library for handling plurals/genders/conditionals.

use std::collections::HashMap;

mod plurals;
mod utils;

const _PLURAL: &str = "_PLURAL";

#[derive(Debug, PartialEq)]
pub enum PlurrError {
    EmptyPlaceholder,
    EmptyVariants,
    NotZeroOrPositiveValue,
    UnmatchedOpeningBrace,
    UnmatchedClosingBrace,
    UndefinedParameter,
    UndefinedParameterPairs,
}

type PlurrParams = HashMap<String, String>;

#[derive(Debug)]
pub struct Plurr<'a> {
    locale: &'a str,
    auto_plurals: bool,
    strict: bool,
    params: PlurrParams,
}

impl<'a> Default for Plurr<'a> {
    fn default() -> Self {
        Plurr::new()
    }
}

impl<'a> Plurr<'a> {
    pub fn new() -> Self {
        Self {
            locale: "en",
            auto_plurals: true,
            strict: true,
            params: PlurrParams::new(),
        }
    }

    /// Sets the locale for Plurr. If this is not called or an incompatible
    /// `locale_code` is provided, it defaults to English.
    pub fn locale(&mut self, locale_code: &'a str) -> &mut Self {
        self.locale = locale_code;
        self
    }

    /// Toggles the auto-plural behavior.
    /// * With auto-plurals, Plurr will automatically select the plural form for
    /// the current locale.
    /// * Without auto-plurals, you will need to manually provide an `N_PLURAL`
    /// parameter before calling `format()`.
    pub fn auto_plurals(&mut self, auto_plurals: bool) -> &mut Self {
        self.auto_plurals = auto_plurals;
        self
    }

    /// Toggles strict mode. In strict mode, errors are not skipped.
    pub fn strict(&mut self, strict: bool) -> &mut Self {
        self.strict = strict;
        self
    }

    /// Stores a parameter value. Parameters must be fed before calling
    /// `format()`.
    pub fn param<T: ToString>(&mut self, key: &str, value: T) -> &mut Self {
        self.params.insert(key.to_string(), value.to_string());
        self
    }

    /// Formats a Plurr string and returns the evaluated string.
    ///
    /// For the format spec please check out the project
    /// [README](https://github.com/loctools/plurr/blob/master/README.md).
    ///
    /// # Examples
    ///
    /// ```
    /// use plurr::Plurr;
    ///
    /// let mut p = Plurr::new();
    /// let s = "Delete {N_PLURAL:{N} file|{N} files}?";
    ///
    /// assert_eq!(
    ///     p.param("N", "5").format(s),
    ///     Ok("Delete 5 files?".to_string())
    /// );
    /// ```
    pub fn format(&mut self, value: &str) -> Result<String, PlurrError> {
        let mut bracket_count = 0;
        let mut blocks = vec!["".to_string()];

        for chunk in utils::split_string(value, "{", "}") {
            if chunk == "{" {
                bracket_count += 1;
                blocks.push("".to_string());
                continue;
            }

            if chunk == "}" {
                bracket_count -= 1;
                if bracket_count < 0 {
                    return Err(PlurrError::UnmatchedClosingBrace);
                }

                let block = blocks.pop().unwrap();
                let colon_pos_maybe = block.find(':');

                let name = match colon_pos_maybe {
                    Some(colon_pos) => {
                        if self.strict && colon_pos == 0 {
                            return Err(PlurrError::EmptyPlaceholder);
                        }
                        // Multiple choices
                        &block[0..colon_pos]
                    }
                    // Simple placeholder
                    None => &block,
                };

                if !self.params.contains_key(name) {
                    let p_pos = name.find(_PLURAL);
                    if self.auto_plurals
                        && p_pos.is_some()
                        && p_pos.unwrap() == name.len() - _PLURAL.len()
                    {
                        let prefix = name[0..p_pos.unwrap()].to_string();
                        if !self.params.contains_key(&prefix) && self.strict {
                            return Err(PlurrError::UndefinedParameterPairs);
                        }

                        // This is where the actual parameter replacing happens
                        let prefix_value_maybe = self.params.get(&prefix).unwrap();
                        let prefix_value = match prefix_value_maybe.parse::<usize>() {
                            Ok(parsed_value) => parsed_value,
                            Err(_) => {
                                if self.strict {
                                    return Err(PlurrError::NotZeroOrPositiveValue);
                                }
                                0
                            }
                        };

                        self.params.insert(
                            name.to_owned(),
                            plurals::plural_func(&self.locale, prefix_value).to_string(),
                        );
                    } else if self.strict {
                        return Err(PlurrError::UndefinedParameter);
                    }
                }

                let result = match colon_pos_maybe {
                    Some(colon_pos) => {
                        // multiple choices
                        let block_len = block.len();

                        if self.strict && colon_pos == block_len - 1 {
                            return Err(PlurrError::EmptyVariants);
                        }

                        let value = self.params.get(name).unwrap();
                        let mut choice_idx: usize = value.parse().unwrap_or(0);

                        let content_start = colon_pos + 1;
                        let clean_block = &block[content_start..];

                        let parts: Vec<&str> = clean_block.split('|').collect();
                        if choice_idx >= parts.len() {
                            choice_idx = parts.len() - 1;
                        }
                        parts[choice_idx]
                    }
                    None => self.params.get(name).unwrap(),
                };

                let index = blocks.len() - 1;
                blocks[index].push_str(result);

                continue;
            }

            let index = blocks.len() - 1;
            blocks[index].push_str(&chunk);
        }

        if bracket_count > 0 {
            return Err(PlurrError::UnmatchedOpeningBrace);
        }

        // Clear params for subsequent calls
        self.params.clear();

        Ok(blocks[0].clone())
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::c_char;

use std::ffi::{
    CStr,
    CString,
};

#[no_mangle]
pub extern fn hello() {
    println!("hello")
}

#[no_mangle]
pub extern fn add(a: i64, b: i64) -> i64 {
    a + b
}

#[no_mangle]
pub extern fn length(str: *const c_char) -> i64 {
    let c_str = unsafe { CStr::from_ptr(str) };

    c_str.to_bytes().len() as i64
}

fn to_str<'a>(c_str: *const c_char) -> &'a str {
    unsafe { CStr::from_ptr(c_str) }
        .to_str()
        .unwrap()

}

fn to_c_string(string: String) -> *const c_char {
    CString::new(string)
        .unwrap()
        .into_raw()
}

#[no_mangle]
pub extern fn duplicate(
    count: i64,
    c_msg: *const c_char,
) -> *mut c_char {
    let msg = to_str(c_msg);

    let out = std::iter::repeat(msg)
        .take(count as usize)
        .collect::<String>();

    CString::new(out)
        .unwrap()
        .into_raw()
}

#[no_mangle]
pub extern fn release(msg: *mut c_char) {
    unsafe { CString::from_raw(msg) };
}

#[repr(C)]
pub struct DuplicateString {
    msg: *const c_char,
    count: i32,
}

#[no_mangle]
pub extern fn add_duplicate_strings(
    c_ds1: *const DuplicateString,
    c_ds2: *const DuplicateString,
    c_result: *mut DuplicateString,
) {
    let (ds1, ds2, mut result) = unsafe { (
        &*c_ds1,
        &*c_ds2,
        &mut *c_result
    ) };

    let ds1msg = to_str(ds1.msg);
    let ds2msg = to_str(ds2.msg);

    result.msg = to_c_string([ds1msg, ds2msg].join("|"));
    result.count = ds1.count + ds2.count;
}

#[repr(C)]
pub struct Point {
    x: i32,
    y: i32,
}

#[no_mangle]
pub extern fn add_points(
    c_p1: *const Point,
    c_p2: *const Point,
    c_result: *mut Point,
) {
    let (p1, p2, mut result) = unsafe { (&*c_p1, &*c_p2, &mut *c_result) };

    result.x = p1.x + p2.x;
    result.y = p1.y + p2.y;
}
<commit_msg>Add todo on mut vs. const for to_c_string<commit_after>extern crate libc;

use libc::c_char;

use std::ffi::{
    CStr,
    CString,
};

#[no_mangle]
pub extern fn hello() {
    println!("hello")
}

#[no_mangle]
pub extern fn add(a: i64, b: i64) -> i64 {
    a + b
}

#[no_mangle]
pub extern fn length(str: *const c_char) -> i64 {
    let c_str = unsafe { CStr::from_ptr(str) };

    c_str.to_bytes().len() as i64
}

fn to_str<'a>(c_str: *const c_char) -> &'a str {
    unsafe { CStr::from_ptr(c_str) }
        .to_str()
        .unwrap()

}

fn to_c_string(string: String) -> *const c_char {
    CString::new(string)
        .unwrap()
        .into_raw()
}

#[no_mangle]
pub extern fn duplicate(
    count: i64,
    c_msg: *const c_char,
) -> *mut c_char {
    let msg = to_str(c_msg);

    let out = std::iter::repeat(msg)
        .take(count as usize)
        .collect::<String>();

    CString::new(out)  // TODO unable to to_c_string here.. (mut vs. const)
        .unwrap()
        .into_raw()
}

#[no_mangle]
pub extern fn release(msg: *mut c_char) {
    unsafe { CString::from_raw(msg) };
}

#[repr(C)]
pub struct DuplicateString {
    msg: *const c_char,
    count: i32,
}

#[no_mangle]
pub extern fn add_duplicate_strings(
    c_ds1: *const DuplicateString,
    c_ds2: *const DuplicateString,
    c_result: *mut DuplicateString,
) {
    let (ds1, ds2, mut result) = unsafe { (
        &*c_ds1,
        &*c_ds2,
        &mut *c_result
    ) };

    let ds1msg = to_str(ds1.msg);
    let ds2msg = to_str(ds2.msg);

    result.msg = to_c_string([ds1msg, ds2msg].join("|"));
    result.count = ds1.count + ds2.count;
}

#[repr(C)]
pub struct Point {
    x: i32,
    y: i32,
}

#[no_mangle]
pub extern fn add_points(
    c_p1: *const Point,
    c_p2: *const Point,
    c_result: *mut Point,
) {
    let (p1, p2, mut result) = unsafe { (&*c_p1, &*c_p2, &mut *c_result) };

    result.x = p1.x + p2.x;
    result.y = p1.y + p2.y;
}
<|endoftext|>


<commit_before>extern crate chrono;

use chrono::prelude::*;
use regex::{Regex, Captures};

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
    RepetitiveEvent(RepetitiveEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

#[derive(Debug)]
pub struct RepetitiveEventImpl{
    pub event_start_time : DateTime<Utc>,
    pub event_wait_time : chrono::Duration,
    pub event_text : String,
}

const MOMENT_DAY_REGEX: &str =
    r"(?:(?P<m_day>[\d]+))?(?:-(?P<m_month>[\d]+))?(?:-(?P<m_year>[\d]+))?";

const MOMENT_TIME_REGEX: &str =
    r"(?P<m_hour>[\d]+)(?:.(?P<m_minute>[\d]+))?";

// const MOMENT_REGEX: &str =
    // MOM

const DURATION_REGEX: &str =
    r"(:?(?P<d_day>[\d]*)[D|d|Д|д])?(:?(?P<d_hour>[\d]*)[H|h|Ч|ч])?(:?(?P<d_minute>[\d]*)[M|m|М|м])?(:?(?P<d_second>[\d]*)[S|s|С|с])?";

pub fn parse_command(command_line : String, user_timezone: i32) -> Command {
    let command_line = String::from(command_line.trim());
    let mut result;
    result = try_parse_for(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_at(&command_line, user_timezone);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_rep(&command_line, user_timezone);
    if result.is_some() {
        return result.unwrap();
    }

    warn!("parse_command: line {} doesn't match any regex", command_line);
    Command::BadCommand
}

fn try_parse_at(command_line : &String, user_timezone: i32) -> Option<Command>{
    // let reg = String::from(r"^(at|в)\s*") + MOMENT_REGEX + r" (?P<main_text>.*)";
    let reg = String::from(r"^") + MOMENT_DAY_REGEX + r"\s*(at|в)\s*" + MOMENT_TIME_REGEX + r" (?P<main_text>.*)";
    let time_format = Regex::new(&reg[..]).unwrap();

    let date_captures = time_format.captures(command_line);

    if date_captures.is_none() {
        return None;
    }
    let date_captures = date_captures.unwrap();
    let text = date_captures.name("main_text").unwrap().as_str();

    if let Some(t) = get_datetime_from_capture(&date_captures, user_timezone){
        return Some(Command::OneTimeEvent
                (OneTimeEventImpl
                    { event_text : String::from(text)
                    , event_time : t}
                ));
    }
    None
}

fn try_parse_for(command_line : &String) -> Option<Command>{
    let reg = String::from("^") + DURATION_REGEX + r"(?P<divider> )(?P<main_text>.*)";
    let reg = Regex::new(&reg[..]).unwrap();

    let capture = reg.captures(command_line);
    if capture.is_none(){
        return None;
    }
    let capture = capture.unwrap();

    let text = capture.name("main_text").unwrap().as_str();
    let dt = get_duration_from_capture(&capture);
    if dt.is_none() {
        return None;
    }

    Some(Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : Utc::now() + dt.unwrap() }
        ))
}

fn try_parse_rep(command_line: &String, user_timezone: i32) -> Option<Command> {
    let reg = String::from("^") + r"rep\s*" + MOMENT_DAY_REGEX
                                + r"\s+"    + MOMENT_TIME_REGEX
                                + r"\s+"    + DURATION_REGEX
                                + r"(?P<divider> )(?P<main_text>.*)";
    let reg = Regex::new(&reg[..]).unwrap();

    let capture = reg.captures(command_line);
    if capture.is_none(){
        return None;
    }
    let capture = capture.unwrap();

    let text = capture.name("main_text").unwrap().as_str();
    let time = get_datetime_from_capture(&capture, user_timezone);
    let dt = get_duration_from_capture(&capture);
    if time.is_none() || dt.is_none() {
        return None;
    }

    Some(Command::RepetitiveEvent(RepetitiveEventImpl
        {
            event_start_time : time.unwrap(),
            event_wait_time : dt.unwrap(),
            event_text : String::from(text),
        }
    ))
}

fn get_duration_from_capture(cap: &Captures) -> Option<chrono::Duration>{
    let day:    i64 = cap.name("d_day").map_or    (0, |c| c.as_str().parse().unwrap() );
    let hour:   i64 = cap.name("d_hour").map_or   (0, |c| c.as_str().parse().unwrap() );
    let minute: i64 = cap.name("d_minute").map_or (0, |c| c.as_str().parse().unwrap() );
    let second: i64 = cap.name("d_second").map_or (0, |c| c.as_str().parse().unwrap() );

    if day == 0 && hour == 0 && minute == 0 && second == 0 {
        return None;
    }

    Some(chrono::Duration::seconds(day * (60*60*24)
                                 + hour * (60*60)
                                 + minute * 60
                                 + second))
}

fn get_datetime_from_capture(cap: &Captures, tz: i32) -> Option<DateTime<Utc>>{
    println!("UTC now: {}", Utc::now());
    let dt = chrono::Duration::seconds((tz as i64) * 60 * 60);
    let now = Utc::now() - dt;

    let day     = cap.name("m_day").map_or(now.day(),       |c| c.as_str().parse().unwrap());
    let month   = cap.name("m_month").map_or(now.month(),   |c| c.as_str().parse().unwrap());
    let year    = cap.name("m_year").map_or(now.year(),     |c| c.as_str().parse().unwrap());
    let minute  = cap.name("m_minute").map_or(0,            |c| c.as_str().parse().unwrap());

    let hour    = cap.name("m_hour").unwrap().as_str().parse().unwrap();

    use chrono::offset::LocalResult::Single;
    if let Single(date) = Utc.ymd_opt(year, month, day) {
        if let Some(datetime) = date.and_hms_opt(hour, minute, 0){
            return Some(datetime + dt);
        }
    }
    None
}

//-------- TESTS ---------------------------------------------------------------

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

    #[test]
    fn parse_for_general() {
        let command = String::from("1d2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((1 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (3 as i64) * 60           // minutes
                                        + (4 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    fn parse_for_hm() {
        let command = String::from("2h30m");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (30 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    // #[ignore]
    fn parse_for_negative() {
        let command = String::from("1d-2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    // #[ignore]
    fn parse_for_misspell() {
        let command = String::from("1d2j3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    fn parse_at_tests() {
        {
            let command = String::from("24-10 at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), 10, 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }

        {
            let command = String::from("24 at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
        {
            let command = String::from("at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), now.day()).and_hms(18, 30, 0);

            let result = try_parse_at(&command_text, 0);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
        {
            let command = String::from("at 18");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), now.day()).and_hms(18, now.minute(), 0);

            let result = try_parse_at(&command_text, 0);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
    }

    #[test]
    fn parse_wrong_at_tests() {
        {
            let command = String::from("30-02 at 18");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("30-20 at 18");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("at 25");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("at 23.60");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
    }

    #[test]
    fn parse_rep_general() {
        let command = String::from("rep 06-10 10.00 5m");
        let text = "test rep";
        let command_text = command + " " + text;
        let now = Utc::now();
        let t = Utc.ymd(now.year(), 10, 6).and_hms(10-3, 0, 0);
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (0 as i64) * (60*60)      // hours
                                        + (5 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_rep(&command_text, -3);
        assert!(result.is_some());
        match result.unwrap(){
            RepetitiveEvent(res) => {
                assert_eq!(res.event_start_time, t);
                assert_eq!(res.event_wait_time, dt);
                assert_eq!(res.event_text, text);
            },
            _ => panic!("Wrong command type")
        };
    }

    fn time_moment_eq(t1: DateTime<Utc>, t2: DateTime<Utc>) -> bool{
        t1.signed_duration_since(t2).num_milliseconds().abs() < 100
    }
}<commit_msg>Add upper case (At) to at command<commit_after>extern crate chrono;

use chrono::prelude::*;
use regex::{Regex, Captures};

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
    RepetitiveEvent(RepetitiveEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

#[derive(Debug)]
pub struct RepetitiveEventImpl{
    pub event_start_time : DateTime<Utc>,
    pub event_wait_time : chrono::Duration,
    pub event_text : String,
}

const MOMENT_DAY_REGEX: &str =
    r"(?:(?P<m_day>[\d]+))?(?:-(?P<m_month>[\d]+))?(?:-(?P<m_year>[\d]+))?";

const MOMENT_TIME_REGEX: &str =
    r"(?P<m_hour>[\d]+)(?:.(?P<m_minute>[\d]+))?";

// const MOMENT_REGEX: &str =
    // MOM

const DURATION_REGEX: &str =
    r"(:?(?P<d_day>[\d]*)[D|d|Д|д])?(:?(?P<d_hour>[\d]*)[H|h|Ч|ч])?(:?(?P<d_minute>[\d]*)[M|m|М|м])?(:?(?P<d_second>[\d]*)[S|s|С|с])?";

pub fn parse_command(command_line : String, user_timezone: i32) -> Command {
    let command_line = String::from(command_line.trim());
    let mut result;
    result = try_parse_for(&command_line);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_at(&command_line, user_timezone);
    if result.is_some() {
        return result.unwrap();
    }

    result = try_parse_rep(&command_line, user_timezone);
    if result.is_some() {
        return result.unwrap();
    }

    warn!("parse_command: line {} doesn't match any regex", command_line);
    Command::BadCommand
}

fn try_parse_at(command_line : &String, user_timezone: i32) -> Option<Command>{
    // let reg = String::from(r"^(at|в)\s*") + MOMENT_REGEX + r" (?P<main_text>.*)";
    let reg = String::from(r"^") + MOMENT_DAY_REGEX + r"\s*(at|At|в|В)\s*" + MOMENT_TIME_REGEX + r" (?P<main_text>.*)";
    let time_format = Regex::new(&reg[..]).unwrap();

    let date_captures = time_format.captures(command_line);

    if date_captures.is_none() {
        return None;
    }
    let date_captures = date_captures.unwrap();
    let text = date_captures.name("main_text").unwrap().as_str();

    if let Some(t) = get_datetime_from_capture(&date_captures, user_timezone){
        return Some(Command::OneTimeEvent
                (OneTimeEventImpl
                    { event_text : String::from(text)
                    , event_time : t}
                ));
    }
    None
}

fn try_parse_for(command_line : &String) -> Option<Command>{
    let reg = String::from("^") + DURATION_REGEX + r"(?P<divider> )(?P<main_text>.*)";
    let reg = Regex::new(&reg[..]).unwrap();

    let capture = reg.captures(command_line);
    if capture.is_none(){
        return None;
    }
    let capture = capture.unwrap();

    let text = capture.name("main_text").unwrap().as_str();
    let dt = get_duration_from_capture(&capture);
    if dt.is_none() {
        return None;
    }

    Some(Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : Utc::now() + dt.unwrap() }
        ))
}

fn try_parse_rep(command_line: &String, user_timezone: i32) -> Option<Command> {
    let reg = String::from("^") + r"rep\s*" + MOMENT_DAY_REGEX
                                + r"\s+"    + MOMENT_TIME_REGEX
                                + r"\s+"    + DURATION_REGEX
                                + r"(?P<divider> )(?P<main_text>.*)";
    let reg = Regex::new(&reg[..]).unwrap();

    let capture = reg.captures(command_line);
    if capture.is_none(){
        return None;
    }
    let capture = capture.unwrap();

    let text = capture.name("main_text").unwrap().as_str();
    let time = get_datetime_from_capture(&capture, user_timezone);
    let dt = get_duration_from_capture(&capture);
    if time.is_none() || dt.is_none() {
        return None;
    }

    Some(Command::RepetitiveEvent(RepetitiveEventImpl
        {
            event_start_time : time.unwrap(),
            event_wait_time : dt.unwrap(),
            event_text : String::from(text),
        }
    ))
}

fn get_duration_from_capture(cap: &Captures) -> Option<chrono::Duration>{
    let day:    i64 = cap.name("d_day").map_or    (0, |c| c.as_str().parse().unwrap() );
    let hour:   i64 = cap.name("d_hour").map_or   (0, |c| c.as_str().parse().unwrap() );
    let minute: i64 = cap.name("d_minute").map_or (0, |c| c.as_str().parse().unwrap() );
    let second: i64 = cap.name("d_second").map_or (0, |c| c.as_str().parse().unwrap() );

    if day == 0 && hour == 0 && minute == 0 && second == 0 {
        return None;
    }

    Some(chrono::Duration::seconds(day * (60*60*24)
                                 + hour * (60*60)
                                 + minute * 60
                                 + second))
}

fn get_datetime_from_capture(cap: &Captures, tz: i32) -> Option<DateTime<Utc>>{
    println!("UTC now: {}", Utc::now());
    let dt = chrono::Duration::seconds((tz as i64) * 60 * 60);
    let now = Utc::now() - dt;

    let day     = cap.name("m_day").map_or(now.day(),       |c| c.as_str().parse().unwrap());
    let month   = cap.name("m_month").map_or(now.month(),   |c| c.as_str().parse().unwrap());
    let year    = cap.name("m_year").map_or(now.year(),     |c| c.as_str().parse().unwrap());
    let minute  = cap.name("m_minute").map_or(0,            |c| c.as_str().parse().unwrap());

    let hour    = cap.name("m_hour").unwrap().as_str().parse().unwrap();

    use chrono::offset::LocalResult::Single;
    if let Single(date) = Utc.ymd_opt(year, month, day) {
        if let Some(datetime) = date.and_hms_opt(hour, minute, 0){
            return Some(datetime + dt);
        }
    }
    None
}

//-------- TESTS ---------------------------------------------------------------

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

    #[test]
    fn parse_for_general() {
        let command = String::from("1d2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((1 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (3 as i64) * 60           // minutes
                                        + (4 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    fn parse_for_hm() {
        let command = String::from("2h30m");
        let text = "some text";
        let command_text = command + " " + text;
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (2 as i64) * (60*60)      // hours
                                        + (30 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_for(&command_text);
        assert!(result.is_some());
        match result.unwrap(){
            OneTimeEvent(res) => {
                assert_eq!(res.event_text, text);
                assert!(time_moment_eq(res.event_time, Utc::now() + dt));
            },
            _ => panic!("Wrong command type")
        };
    }

    #[test]
    // #[ignore]
    fn parse_for_negative() {
        let command = String::from("1d-2h3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    // #[ignore]
    fn parse_for_misspell() {
        let command = String::from("1d2j3m4s");
        let text = "some text";
        let command_text = command + " " + text;
        let result = try_parse_for(&command_text);
        assert!(result.is_none());
    }

    #[test]
    fn parse_at_tests() {
        {
            let command = String::from("24-10 at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), 10, 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }

        {
            let command = String::from("24 at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), 24).and_hms(18-3, 30, 0);

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
        {
            let command = String::from("at 18.30");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), now.day()).and_hms(18, 30, 0);

            let result = try_parse_at(&command_text, 0);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
        {
            let command = String::from("at 18");
            let text = "some text";
            let command_text = command + " " + text;
            let now = Utc::now();
            let t = Utc.ymd(now.year(), now.month(), now.day()).and_hms(18, now.minute(), 0);

            let result = try_parse_at(&command_text, 0);
            assert!(result.is_some());
            match result.unwrap(){
                OneTimeEvent(res) => {
                    assert_eq!(res.event_text, text);
                    assert_eq!(res.event_time, t);
                },
                _ => panic!("Wrong command type")
            };
        }
    }

    #[test]
    fn parse_wrong_at_tests() {
        {
            let command = String::from("30-02 at 18");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("30-20 at 18");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("at 25");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
        {
            let command = String::from("at 23.60");
            let text = "some text";
            let command_text = command + " " + text;

            let result = try_parse_at(&command_text, -3);
            assert!(result.is_none());
        }
    }

    #[test]
    fn parse_rep_general() {
        let command = String::from("rep 06-10 10.00 5m");
        let text = "test rep";
        let command_text = command + " " + text;
        let now = Utc::now();
        let t = Utc.ymd(now.year(), 10, 6).and_hms(10-3, 0, 0);
        let dt = chrono::Duration::seconds((0 as i64) * (60*60*24)  // days
                                        + (0 as i64) * (60*60)      // hours
                                        + (5 as i64) * 60           // minutes
                                        + (0 as i64)                // seconds
                                        );
        let result = try_parse_rep(&command_text, -3);
        assert!(result.is_some());
        match result.unwrap(){
            RepetitiveEvent(res) => {
                assert_eq!(res.event_start_time, t);
                assert_eq!(res.event_wait_time, dt);
                assert_eq!(res.event_text, text);
            },
            _ => panic!("Wrong command type")
        };
    }

    fn time_moment_eq(t1: DateTime<Utc>, t2: DateTime<Utc>) -> bool{
        t1.signed_duration_since(t2).num_milliseconds().abs() < 100
    }
}<|endoftext|>