<commit_before>//! A [`Builder`] type for [`TwitterStream`](crate::TwitterStream).
//!
//! The Streaming API has two different endpoints: [`POST statuses/filter`] and
//! [`GET statuses/sample`]. `Builder` automatically determines which endpoint to use based on the
//! specified parameters. Specifically, when any of [`follow`][Builder::follow],
//! [`track`][Builder::track] and [`locations`][Builder::locations] parameters is specified,
//! `filter` will be used, and when none is specified, `sample` will be used.
//!
//! [`POST statuses/filter`]: https://developer.twitter.com/en/docs/tweets/filter-realtime/api-reference/post-statuses-filter
//! [`GET statuses/sample`]: https://developer.twitter.com/en/docs/tweets/filter-realtime/api-reference/post-statuses-filter
//!
//! `filter` yields public Tweets that match the filter predicates specified by the parameters,
//! and `sample` yields "a small random sample" of all public Tweets.
//!
//! ## Example
//!
//! ```rust,no_run
//! use futures::prelude::*;
//! use twitter_stream::{builder::BoundingBox, Token};
//!
//! # #[tokio::main]
//! # async fn main() {
//! let token = Token::from_parts("consumer_key", "consumer_secret", "access_key", "access_secret");
//!
//! const TOKYO: &'static [BoundingBox] = &[BoundingBox::new(139.56, 35.53, 139.92, 35.82)];
//!
//! // Prints geolocated English Tweets associated with Tokyo (the 23 special wards).
//! twitter_stream::Builder::new(token)
//!     .locations(TOKYO)
//!     .language("en")
//!     .listen()
//!     .try_flatten_stream()
//!     .try_for_each(|json| {
//!         println!("{}", json);
//!         future::ok(())
//!     })
//!     .await
//!     .unwrap();
//! # }
//! ```

mod bounding_box;

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

pub use bounding_box::BoundingBox;

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

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

use crate::service::HttpService;
use crate::util::fmt_join;
use crate::{FutureTwitterStream, Token};

/// A builder for [`TwitterStream`](crate::TwitterStream).
///
/// See the [`builder`][crate::builder] module documentation for details.
#[derive(Clone, Debug)]
pub struct Builder<'a, T = Token> {
    token: T,
    endpoint: Option<(RequestMethod, Uri)>,
    parameters: Parameters<'a>,
}

/// Parameters to the Streaming API.
#[derive(Clone, Debug, Default, oauth::Request)]
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>,
}

str_enum! {
    /// Represents the [`filter_level`] parameter in API requests.
    ///
    /// [`filter_level`]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#filter-level
    #[derive(Clone, Debug, PartialEq, Hash, Eq)]
    pub enum FilterLevel {
        /// `"none"`
        None = "none",
        /// `"low"`
        Low = "low",
        /// `"medium"`
        Medium = "medium",
    }
}

const FILTER: &str = "https://stream.twitter.com/1.1/statuses/filter.json";
const SAMPLE: &str = "https://stream.twitter.com/1.1/statuses/sample.json";

impl<'a, C, A> Builder<'a, Token<C, A>>
where
    C: Borrow<str>,
    A: Borrow<str>,
{
    /// Creates a builder.
    pub fn new(token: Token<C, A>) -> Self {
        Builder {
            token,
            endpoint: None,
            parameters: Parameters::default(),
        }
    }

    /// 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
    #[deprecated(since = "0.10.0", note = "Use `Builder::new` instead")]
    pub fn filter(token: Token<C, A>) -> Self {
        let mut ret = Self::new(token);
        ret.endpoint((RequestMethod::POST, Uri::from_static(FILTER)));
        ret
    }

    /// 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
    #[deprecated(since = "0.10.0", note = "Use `Builder::new` instead")]
    pub fn sample(token: Token<C, A>) -> Self {
        let mut ret = Self::new(token);
        ret.endpoint((RequestMethod::GET, Uri::from_static(SAMPLE)));
        ret
    }

    /// 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`](Builder::listen) except that it uses `client` to make HTTP request
    /// to the endpoint.
    ///
    /// `client` must be able to handle the `https` scheme.
    ///
    /// # 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
    ///
    /// ```no_run
    /// use tower::ServiceExt;
    ///
    /// # async fn doc() {
    /// # let mut client = hyper_pkg::Client::new();
    /// # let token = twitter_stream::Token::from_parts("", "", "", "");
    /// client.ready_and().await; // Ensure that the client is ready to send a request.
    /// let stream = twitter_stream::Builder::new(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: From<Vec<u8>>,
    {
        let req = prepare_request(
            self.endpoint.as_ref(),
            self.token.as_ref(),
            &self.parameters,
        );
        let response = client.call(req.map(Into::into));

        FutureTwitterStream { response }
    }
}

impl<'a, C, A> Builder<'a, Token<C, A>> {
    /// Set the API endpoint URI to be connected.
    ///
    /// This overrides the default behavior of automatically determining the endpoint to use.
    pub fn endpoint(&mut self, endpoint: impl Into<Option<(RequestMethod, Uri)>>) -> &mut Self {
        self.endpoint = endpoint.into();
        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 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)
    }
}

fn prepare_request(
    endpoint: Option<&(RequestMethod, Uri)>,
    token: Token<&str, &str>,
    parameters: &Parameters<'_>,
) -> http::Request<Vec<u8>> {
    let uri;
    let (method, endpoint) = if let Some(&(ref method, ref endpoint)) = endpoint {
        (method, endpoint)
    } else if parameters.follow.is_empty()
        && parameters.track.is_empty()
        && parameters.locations.is_empty()
    {
        uri = Uri::from_static(SAMPLE);
        (&RequestMethod::GET, &uri)
    } else {
        uri = Uri::from_static(FILTER);
        (&RequestMethod::POST, &uri)
    };

    let req = Request::builder().method(method.clone());

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

    let mut oauth = oauth::Builder::new(token.client.as_ref(), oauth::HmacSha1);
    oauth.token(token.token.as_ref());

    if RequestMethod::POST == method {
        let authorization = oauth.post(endpoint, parameters);
        let data = oauth::to_form_urlencoded(parameters);

        req.uri(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())
            .unwrap()
    } else {
        let authorization = oauth.build(method.as_ref(), endpoint, parameters);
        let uri = oauth::to_uri_query(endpoint.to_string(), parameters);

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

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 {
    fmt_join(BoundingBox::flatten_slice(locs).flat(), COMMA, f)
}

#[allow(clippy::trivially_copy_pass_by_ref)]
fn not(p: &bool) -> bool {
    !p
}
<commit_msg>Remove deprecated items<commit_after>//! A [`Builder`] type for [`TwitterStream`](crate::TwitterStream).
//!
//! The Streaming API has two different endpoints: [`POST statuses/filter`] and
//! [`GET statuses/sample`]. `Builder` automatically determines which endpoint to use based on the
//! specified parameters. Specifically, when any of [`follow`][Builder::follow],
//! [`track`][Builder::track] and [`locations`][Builder::locations] parameters is specified,
//! `filter` will be used, and when none is specified, `sample` will be used.
//!
//! [`POST statuses/filter`]: https://developer.twitter.com/en/docs/tweets/filter-realtime/api-reference/post-statuses-filter
//! [`GET statuses/sample`]: https://developer.twitter.com/en/docs/tweets/filter-realtime/api-reference/post-statuses-filter
//!
//! `filter` yields public Tweets that match the filter predicates specified by the parameters,
//! and `sample` yields "a small random sample" of all public Tweets.
//!
//! ## Example
//!
//! ```rust,no_run
//! use futures::prelude::*;
//! use twitter_stream::{builder::BoundingBox, Token};
//!
//! # #[tokio::main]
//! # async fn main() {
//! let token = Token::from_parts("consumer_key", "consumer_secret", "access_key", "access_secret");
//!
//! const TOKYO: &'static [BoundingBox] = &[BoundingBox::new(139.56, 35.53, 139.92, 35.82)];
//!
//! // Prints geolocated English Tweets associated with Tokyo (the 23 special wards).
//! twitter_stream::Builder::new(token)
//!     .locations(TOKYO)
//!     .language("en")
//!     .listen()
//!     .try_flatten_stream()
//!     .try_for_each(|json| {
//!         println!("{}", json);
//!         future::ok(())
//!     })
//!     .await
//!     .unwrap();
//! # }
//! ```

mod bounding_box;

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

pub use bounding_box::BoundingBox;

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

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

use crate::service::HttpService;
use crate::util::fmt_join;
use crate::{FutureTwitterStream, Token};

/// A builder for [`TwitterStream`](crate::TwitterStream).
///
/// See the [`builder`][crate::builder] module documentation for details.
#[derive(Clone, Debug)]
pub struct Builder<'a, T = Token> {
    token: T,
    endpoint: Option<(RequestMethod, Uri)>,
    parameters: Parameters<'a>,
}

/// Parameters to the Streaming API.
#[derive(Clone, Debug, Default, oauth::Request)]
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>,
}

str_enum! {
    /// Represents the [`filter_level`] parameter in API requests.
    ///
    /// [`filter_level`]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/basic-stream-parameters#filter-level
    #[derive(Clone, Debug, PartialEq, Hash, Eq)]
    pub enum FilterLevel {
        /// `"none"`
        None = "none",
        /// `"low"`
        Low = "low",
        /// `"medium"`
        Medium = "medium",
    }
}

const FILTER: &str = "https://stream.twitter.com/1.1/statuses/filter.json";
const SAMPLE: &str = "https://stream.twitter.com/1.1/statuses/sample.json";

impl<'a, C, A> Builder<'a, Token<C, A>>
where
    C: Borrow<str>,
    A: Borrow<str>,
{
    /// Creates a builder.
    pub fn new(token: Token<C, A>) -> Self {
        Builder {
            token,
            endpoint: None,
            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`](Builder::listen) except that it uses `client` to make HTTP request
    /// to the endpoint.
    ///
    /// `client` must be able to handle the `https` scheme.
    ///
    /// # 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
    ///
    /// ```no_run
    /// use tower::ServiceExt;
    ///
    /// # async fn doc() {
    /// # let mut client = hyper_pkg::Client::new();
    /// # let token = twitter_stream::Token::from_parts("", "", "", "");
    /// client.ready_and().await; // Ensure that the client is ready to send a request.
    /// let stream = twitter_stream::Builder::new(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: From<Vec<u8>>,
    {
        let req = prepare_request(
            self.endpoint.as_ref(),
            self.token.as_ref(),
            &self.parameters,
        );
        let response = client.call(req.map(Into::into));

        FutureTwitterStream { response }
    }
}

impl<'a, C, A> Builder<'a, Token<C, A>> {
    /// Set the API endpoint URI to be connected.
    ///
    /// This overrides the default behavior of automatically determining the endpoint to use.
    pub fn endpoint(&mut self, endpoint: impl Into<Option<(RequestMethod, Uri)>>) -> &mut Self {
        self.endpoint = endpoint.into();
        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 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)
    }
}

fn prepare_request(
    endpoint: Option<&(RequestMethod, Uri)>,
    token: Token<&str, &str>,
    parameters: &Parameters<'_>,
) -> http::Request<Vec<u8>> {
    let uri;
    let (method, endpoint) = if let Some(&(ref method, ref endpoint)) = endpoint {
        (method, endpoint)
    } else if parameters.follow.is_empty()
        && parameters.track.is_empty()
        && parameters.locations.is_empty()
    {
        uri = Uri::from_static(SAMPLE);
        (&RequestMethod::GET, &uri)
    } else {
        uri = Uri::from_static(FILTER);
        (&RequestMethod::POST, &uri)
    };

    let req = Request::builder().method(method.clone());

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

    let mut oauth = oauth::Builder::new(token.client.as_ref(), oauth::HmacSha1);
    oauth.token(token.token.as_ref());

    if RequestMethod::POST == method {
        let authorization = oauth.post(endpoint, parameters);
        let data = oauth::to_form_urlencoded(parameters);

        req.uri(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())
            .unwrap()
    } else {
        let authorization = oauth.build(method.as_ref(), endpoint, parameters);
        let uri = oauth::to_uri_query(endpoint.to_string(), parameters);

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

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 {
    fmt_join(BoundingBox::flatten_slice(locs).flat(), COMMA, f)
}

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


<commit_before>use {Value, Response};
use parser::parse_response;
use value::escape_xml;
use error::RequestError;

use xml::EventReader;
use hyper::client::{Client, Body};
use hyper::header::UserAgent;

use std::io::{self, Write};

/// A request to call a procedure.
pub struct Request<'a> {
    name: &'a str,
    args: Vec<(&'a str, Value)>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name: name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, name: &'a str, value: T) -> Self {
        self.args.push((name, value.into()));

        Request {
            name: self.name,
            args: self.args,
        }
    }

    /// Calls the method using the given `Client`.
    ///
    /// This will send the request to the `/` URL.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(self, client: Client) -> RequestResult {
        // First, build the body XML
        let mut body = Vec::new();
        try!(self.write_as_xml(&mut body));

        // Send XML-RPC request
        let response = try!(client.post("/")
            .header(UserAgent("Rust xmlrpc".to_string()))
            .body(Body::BufBody(&body, body.len()))
            .send());

        // FIXME Check that the response headers are correct

        // Read the response and parse it
        // FIXME `BufRead`?
        Ok(try!(parse_response(&mut EventReader::new(response))))
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        try!(write!(fmt, r#"<?xml version="1.1 encoding="utf-8"?>"#));
        try!(write!(fmt, r#"<methodCall>"#));
        try!(write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name)));
        try!(write!(fmt, r#"    <params>"#));
        for &(_, ref value) in &self.args {
            try!(write!(fmt, r#"        <param>"#));
            try!(value.format(fmt));
            try!(write!(fmt, r#"        </param>"#));
        }
        try!(write!(fmt, r#"    </params>"#));
        try!(write!(fmt, r#"</methodCall>"#));
        Ok(())
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<commit_msg>Add missing quote character in xml preamble<commit_after>use {Value, Response};
use parser::parse_response;
use value::escape_xml;
use error::RequestError;

use xml::EventReader;
use hyper::client::{Client, Body};
use hyper::header::UserAgent;

use std::io::{self, Write};

/// A request to call a procedure.
pub struct Request<'a> {
    name: &'a str,
    args: Vec<(&'a str, Value)>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name: name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, name: &'a str, value: T) -> Self {
        self.args.push((name, value.into()));

        Request {
            name: self.name,
            args: self.args,
        }
    }

    /// Calls the method using the given `Client`.
    ///
    /// This will send the request to the `/` URL.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(self, client: Client) -> RequestResult {
        // First, build the body XML
        let mut body = Vec::new();
        try!(self.write_as_xml(&mut body));

        // Send XML-RPC request
        let response = try!(client.post("/")
            .header(UserAgent("Rust xmlrpc".to_string()))
            .body(Body::BufBody(&body, body.len()))
            .send());

        // FIXME Check that the response headers are correct

        // Read the response and parse it
        // FIXME `BufRead`?
        Ok(try!(parse_response(&mut EventReader::new(response))))
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        try!(write!(fmt, r#"<?xml version="1.1" encoding="utf-8"?>"#));
        try!(write!(fmt, r#"<methodCall>"#));
        try!(write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name)));
        try!(write!(fmt, r#"    <params>"#));
        for &(_, ref value) in &self.args {
            try!(write!(fmt, r#"        <param>"#));
            try!(value.format(fmt));
            try!(write!(fmt, r#"        </param>"#));
        }
        try!(write!(fmt, r#"    </params>"#));
        try!(write!(fmt, r#"</methodCall>"#));
        Ok(())
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::ops::Drop;

use basic_block::*;
use value::*;
use context::*;
use bindings::*;

pub struct IRBuilder(pub(super) LLVMBuilderRef);

impl IRBuilder {
    pub fn new() -> IRBuilder {
        IRBuilder(unsafe { LLVMCreateBuilder() })
    }

    pub fn new_in_context(cont: Context) -> IRBuilder {
        IRBuilder(unsafe { LLVMCreateBuilderInContext(cont.0) })
    }

    pub fn position_at_end(&self, bb: BasicBlock) {
        unsafe {
            LLVMPositionBuilderAtEnd(self.0, bb.0);
        }
    }

    pub fn insertion_block(&self) -> BasicBlock {
        BasicBlock(unsafe { LLVMGetInsertBlock(self.0) })
    }

    pub fn ret_void(&self) -> Value {
        Value(unsafe { LLVMBuildRetVoid(self.0) })
    }

    pub fn ret(&self, val: Value) -> Value {
        Value(unsafe { LLVMBuildRet(self.0, val.0) })
    }

    pub fn br(&self, br: BasicBlock) -> Value {
        Value(unsafe { LLVMBuildBr(self.0, br.0) })
    }
}

impl Drop for IRBuilder {
    fn drop(&mut self) {
        unsafe {
            LLVMDisposeBuilder(self.0);
        }
    }
}

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

    #[test]
    fn test_insertion_block_and_position_at_end() {
        let modl = Module::new("test");
        let f = modl.add_function("testf", FunctionType::new(Type::int32(), &vec![], false));
        let entry_b = f.append_bb("entry");
        let builder = IRBuilder::new();
        builder.position_at_end(f.entry_bb().unwrap());
        assert_eq!(builder.insertion_block(), entry_b);
    }
}
<commit_msg>builder: cond_br; fixes in insertion_block<commit_after>extern crate libc;

use std::ops::Drop;

use basic_block::*;
use value::*;
use context::*;
use bindings::*;

pub struct IRBuilder(pub(super) LLVMBuilderRef);

impl IRBuilder {
    pub fn new() -> IRBuilder {
        IRBuilder(unsafe { LLVMCreateBuilder() })
    }

    pub fn new_in_context(cont: Context) -> IRBuilder {
        IRBuilder(unsafe { LLVMCreateBuilderInContext(cont.0) })
    }

    pub fn position_at_end(&self, bb: BasicBlock) {
        unsafe {
            LLVMPositionBuilderAtEnd(self.0, bb.0);
        }
    }

    pub fn insertion_block(&self) -> Option<BasicBlock> {
        let r = unsafe { LLVMGetInsertBlock(self.0) };
        if r.is_null() { None } else { Some(BasicBlock(r)) }
    }

    pub fn ret_void(&self) -> Value {
        Value(unsafe { LLVMBuildRetVoid(self.0) })
    }

    pub fn ret(&self, val: Value) -> Value {
        Value(unsafe { LLVMBuildRet(self.0, val.0) })
    }

    pub fn br(&self, br: BasicBlock) -> Value {
        Value(unsafe { LLVMBuildBr(self.0, br.0) })
    }

    pub fn cond_br(&self, cond: Value, then: BasicBlock, els: BasicBlock) -> Value {
        Value(unsafe { LLVMBuildCondBr(self.0, cond.0, then.0, els.0) })
    }
}

impl Drop for IRBuilder {
    fn drop(&mut self) {
        unsafe {
            LLVMDisposeBuilder(self.0);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::IRBuilder;
    use ::module::*;
    use ::types::*;
    use ::value::*;

    #[test]
    fn test_insertion_block_and_position_at_end() {
        let modl = Module::new("test");
        let f = modl.add_function("testf", FunctionType::new(Type::int32(), &vec![], false));
        let entry_b = f.append_bb("entry");
        let builder = IRBuilder::new();
        builder.position_at_end(f.entry_bb().unwrap());
        assert_eq!(builder.insertion_block().unwrap(), entry_b);
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate "termbox-sys" as termbox;

use std::error::Error;
use std::kinds::marker;

use termbox::RawEvent;
use libc::{c_int, c_uint};

#[deriving(Copy)]
pub enum Event {
    KeyEvent(u8, u16, u32),
    ResizeEvent(i32, i32),
    NoEvent
}

#[deriving(Copy)]
#[repr(C,u16)]
pub enum Color {
    Default = TB_DEFAULT as int,
    Black = TB_BLACK as int,
    Red = TB_RED as int,
    Green = TB_GREEN as int,
    Yellow = TB_YELLOW as int,
    Blue = TB_BLUE as int,
    Magenta = TB_MAGENTA as int,
    Cyan = TB_CYAN as int,
    White = TB_WHITE as int,
}

impl Color {
    pub fn to_style(&self) -> Style {
        Style { bits: *self as u16 & TB_NORMAL_COLOR.bits }
    }
}

const TB_DEFAULT: u16 = 0x00;
const TB_BLACK: u16 = 0x01;
const TB_RED: u16 = 0x02;
const TB_GREEN: u16 = 0x03;
const TB_YELLOW: u16 = 0x04;
const TB_BLUE: u16 = 0x05;
const TB_MAGENTA: u16 = 0x06;
const TB_CYAN: u16 = 0x07;
const TB_WHITE: u16 = 0x08;

bitflags! {
    #[repr(C)]
    flags Style: u16 {
        const TB_NORMAL_COLOR = 0x000F,
        const TB_BOLD = 0x0100,
        const TB_UNDERLINE = 0x0200,
        const TB_REVERSE = 0x0400,
        const TB_ATTRIB = TB_BOLD.bits | TB_UNDERLINE.bits | TB_REVERSE.bits,
    }
}

fn nil_raw_event() -> RawEvent {
    RawEvent{etype: 0, emod: 0, key: 0, ch: 0, w: 0, h: 0}
}

// FIXME: Rust doesn't support this enum representation.
// #[deriving(Copy,FromPrimitive,Show)]
// #[repr(C,int)]
// pub enum EventErrorKind {
//     EOF = -1,
// }
// pub type EventError = Option<EventErrorKind>;
#[allow(non_snake_case)]
pub mod EventErrorKind {
    #[deriving(Copy,Show)]
    pub struct EOF;
}

pub type EventError = Option<EventErrorKind::EOF>;

pub type EventResult<T> = Result<T, EventError>;

impl Error for EventError {
    fn description(&self) -> &str {
        match *self {
            Some(EventErrorKind::EOF) => "End of file",
            None => "Unknown error."
        }
    }
}

fn unpack_event(ev_type: c_int, ev: &RawEvent) -> EventResult<Event> {
    match ev_type {
        0 => Ok(Event::NoEvent),
        1 => Ok(Event::KeyEvent(ev.emod, ev.key, ev.ch)),
        2 => Ok(Event::ResizeEvent(ev.w, ev.h)),
        // FIXME: Rust doesn't support this error representation
        // res => FromPrimitive::from_int(res as int),
        -1 => Err(Some(EventErrorKind::EOF)),
        _ => Err(None)
    }
}

#[deriving(Copy,FromPrimitive,Show)]
#[repr(C,int)]
pub enum InitError {
    Unknown = 0,
    UnsupportedTerminal = -1,
    FailedToOpenTty = -2,
    PipeTrapError = -3,
}

impl Error for InitError {
    fn description(&self) -> &str {
        match *self {
            InitError::Unknown => "Unknown error.",
            InitError::UnsupportedTerminal => "Unsupported terminal.",
            InitError::FailedToOpenTty => "Failed to open TTY.",
            InitError::PipeTrapError =>
                "Pipe trap error.  \
                 Termbox uses unix pipes in order to deliver a message from a signal handler to \
                 the main event reading loop.",
        }
    }
}

#[allow(missing_copy_implementations)]
pub struct RustBox {
    no_sync: marker::NoSync, // Termbox is not thread safe
}

impl RustBox {
    pub fn new() -> Result<RustBox, InitError> {
        unsafe {
            match termbox::tb_init() {
                0 => Ok(RustBox { no_sync: marker::NoSync }),
                res => match FromPrimitive::from_int(res as int) {
                    Some(e) => Err(e),
                    None => Err(InitError::Unknown)
                }
            }
        }
    }

    pub fn width(&self) -> uint {
        unsafe { termbox::tb_width() as uint }
    }

    pub fn height(&self) -> uint {
        unsafe { termbox::tb_height() as uint }
    }

    pub fn clear(&self) {
        unsafe { termbox::tb_clear() }
    }

    pub fn present(&self) {
        unsafe { termbox::tb_present() }
    }

    pub fn set_cursor(&self, x: int, y: int) {
        unsafe { termbox::tb_set_cursor(x as c_int, y as c_int) }
    }

    // Unsafe because u8 is not guaranteed to be a UTF-8 character
    pub unsafe fn change_cell(&self, x: uint, y: uint, ch: u32, fg: u16, bg: u16) {
        termbox::tb_change_cell(x as c_uint, y as c_uint, ch, fg, bg)
    }

    pub fn print(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, s: String) {
        let fg = fg.to_style() | (sty & TB_ATTRIB);
        let bg = bg.to_style();
        for (i, ch) in s.as_slice().chars().enumerate() {
            unsafe {
                self.change_cell(x+i, y, ch as u32, fg.bits(), bg.bits());
            }
        }
    }

    pub fn print_char(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, ch: char) {
        let fg = fg.to_style() | (sty & TB_ATTRIB);
        let bg = bg.to_style();
        unsafe {
            self.change_cell(x, y, ch as u32, fg.bits(), bg.bits());
        }
    }

    pub fn poll_event(&self) -> EventResult<Event> {
        unsafe {
            let ev = nil_raw_event();
            let rc = termbox::tb_poll_event(&ev as *const RawEvent);
            unpack_event(rc, &ev)
        }
    }

    /// Returns Err(()) on EOF
    pub fn peek_event(&self, timeout: uint) -> EventResult<Event> {
        unsafe {
            let ev = nil_raw_event();
            let rc = termbox::tb_peek_event(&ev as *const RawEvent, timeout as c_uint);
            unpack_event(rc, &ev)
        }
    }
}

impl Drop for RustBox {
    fn drop(&mut self) {
        unsafe {
            termbox::tb_shutdown();
        }
    }
}
<commit_msg>Make RustBox actually thread safe.<commit_after>extern crate libc;
extern crate "termbox-sys" as termbox;

pub use self::style::{Style, TB_BOLD, TB_UNDERLINE, TB_REVERSE};

use std::sync::atomic::{mod, AtomicBool};
use std::error::Error;
use std::kinds::marker;

use termbox::RawEvent;
use libc::{c_int, c_uint};

#[deriving(Copy)]
pub enum Event {
    KeyEvent(u8, u16, u32),
    ResizeEvent(i32, i32),
    NoEvent
}

#[deriving(Copy)]
#[repr(C,u16)]
pub enum Color {
    Default = TB_DEFAULT as int,
    Black = TB_BLACK as int,
    Red = TB_RED as int,
    Green = TB_GREEN as int,
    Yellow = TB_YELLOW as int,
    Blue = TB_BLUE as int,
    Magenta = TB_MAGENTA as int,
    Cyan = TB_CYAN as int,
    White = TB_WHITE as int,
}

const TB_DEFAULT: u16 = 0x00;
const TB_BLACK: u16 = 0x01;
const TB_RED: u16 = 0x02;
const TB_GREEN: u16 = 0x03;
const TB_YELLOW: u16 = 0x04;
const TB_BLUE: u16 = 0x05;
const TB_MAGENTA: u16 = 0x06;
const TB_CYAN: u16 = 0x07;
const TB_WHITE: u16 = 0x08;

mod style {
    bitflags! {
        #[repr(C)]
        flags Style: u16 {
            const TB_NORMAL_COLOR = 0x000F,
            const TB_BOLD = 0x0100,
            const TB_UNDERLINE = 0x0200,
            const TB_REVERSE = 0x0400,
            const TB_ATTRIB = TB_BOLD.bits | TB_UNDERLINE.bits | TB_REVERSE.bits,
        }
    }

    impl Style {
        pub fn from_color(color: super::Color) -> Style {
            Style { bits: color as u16 & TB_NORMAL_COLOR.bits }
        }
    }
}

const NIL_RAW_EVENT: RawEvent = RawEvent { etype: 0, emod: 0, key: 0, ch: 0, w: 0, h: 0 };

// FIXME: Rust doesn't support this enum representation.
// #[deriving(Copy,FromPrimitive,Show)]
// #[repr(C,int)]
// pub enum EventErrorKind {
//     EOF = -1,
// }
// pub type EventError = Option<EventErrorKind>;
#[allow(non_snake_case)]
pub mod EventErrorKind {
    #[deriving(Copy,Show)]
    pub struct EOF;
}

pub type EventError = Option<EventErrorKind::EOF>;

pub type EventResult<T> = Result<T, EventError>;

impl Error for EventError {
    fn description(&self) -> &str {
        match *self {
            Some(EventErrorKind::EOF) => "End of file",
            None => "Unknown error."
        }
    }
}

fn unpack_event(ev_type: c_int, ev: &RawEvent) -> EventResult<Event> {
    match ev_type {
        0 => Ok(Event::NoEvent),
        1 => Ok(Event::KeyEvent(ev.emod, ev.key, ev.ch)),
        2 => Ok(Event::ResizeEvent(ev.w, ev.h)),
        // FIXME: Rust doesn't support this error representation
        // res => FromPrimitive::from_int(res as int),
        -1 => Err(Some(EventErrorKind::EOF)),
        _ => Err(None)
    }
}

#[deriving(Copy,FromPrimitive,Show)]
#[repr(C,int)]
pub enum InitErrorKind {
    AlreadyOpen = 0,
    UnsupportedTerminal = -1,
    FailedToOpenTty = -2,
    PipeTrapError = -3,
}

pub type InitError = Option<InitErrorKind>;

impl Error for InitError {
    fn description(&self) -> &str {
        match *self {
            Some(InitErrorKind::AlreadyOpen) => "Termbox is already open.",
            Some(InitErrorKind::UnsupportedTerminal) => "Unsupported terminal.",
            Some(InitErrorKind::FailedToOpenTty) => "Failed to open TTY.",
            Some(InitErrorKind::PipeTrapError) =>
                "Pipe trap error.  \
                 Termbox uses unix pipes in order to deliver a message from a signal handler to \
                 the main event reading loop.",
            None => "Unknown error."
        }
    }
}

// The state of the RustBox is protected by the lock.  Yay, global state!
static RUSTBOX_RUNNING: AtomicBool = atomic::INIT_ATOMIC_BOOL;

/// true iff RustBox is currently running.  Beware of races here--don't rely on this for anything
/// critical unless you happen to know that RustBox cannot change state when it is called (a good
/// usecase would be checking to see if it's worth risking double printing backtraces to avoid
/// having them swallowed up by RustBox).
pub fn running() -> bool {
    RUSTBOX_RUNNING.load(atomic::SeqCst)
}

#[allow(missing_copy_implementations)]
pub struct RustBox {
    no_sync: marker::NoSync, // Termbox is not thread safe
}

impl RustBox {
    pub fn new() -> Result<RustBox, InitError> {
        // Ensure that we are not already running and simultaneously set RUSTBOX_RUNNING using an
        // atomic swap.  This ensures that contending threads don't trample each other.
        if RUSTBOX_RUNNING.swap(true, atomic::SeqCst) {
            // The Rustbox was already running.
            Err(Some(InitErrorKind::AlreadyOpen))
        } else {
            // The Rustbox was not already running.
            unsafe {
                match termbox::tb_init() {
                    0 => Ok(RustBox { no_sync: marker::NoSync }),
                    res => {
                        // Remember to unset RUSTBOX_RUNNING.
                        // Probably don't need SeqCst, but as noted elsewhere, better safe than
                        // sorry.
                        RUSTBOX_RUNNING.store(false, atomic::SeqCst);
                        Err(FromPrimitive::from_int(res as int))
                    }
                }
            }
        }
    }

    pub fn width(&self) -> uint {
        unsafe { termbox::tb_width() as uint }
    }

    pub fn height(&self) -> uint {
        unsafe { termbox::tb_height() as uint }
    }

    pub fn clear(&self) {
        unsafe { termbox::tb_clear() }
    }

    pub fn present(&self) {
        unsafe { termbox::tb_present() }
    }

    pub fn set_cursor(&self, x: int, y: int) {
        unsafe { termbox::tb_set_cursor(x as c_int, y as c_int) }
    }

    // Unsafe because u8 is not guaranteed to be a UTF-8 character
    pub unsafe fn change_cell(&self, x: uint, y: uint, ch: u32, fg: u16, bg: u16) {
        termbox::tb_change_cell(x as c_uint, y as c_uint, ch, fg, bg)
    }

    pub fn print(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, s: String) {
        let fg = Style::from_color(fg) | (sty & self::style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        for (i, ch) in s.as_slice().chars().enumerate() {
            unsafe {
                self.change_cell(x+i, y, ch as u32, fg.bits(), bg.bits());
            }
        }
    }

    pub fn print_char(&self, x: uint, y: uint, sty: Style, fg: Color, bg: Color, ch: char) {
        let fg = Style::from_color(fg) | (sty & self::style::TB_ATTRIB);
        let bg = Style::from_color(bg);
        unsafe {
            self.change_cell(x, y, ch as u32, fg.bits(), bg.bits());
        }
    }

    pub fn poll_event(&self) -> EventResult<Event> {
        unsafe {
            let ev = NIL_RAW_EVENT;
            let rc = termbox::tb_poll_event(&ev as *const RawEvent);
            unpack_event(rc, &ev)
        }
    }

    /// Returns Err(()) on EOF
    pub fn peek_event(&self, timeout: uint) -> EventResult<Event> {
        unsafe {
            let ev = NIL_RAW_EVENT;
            let rc = termbox::tb_peek_event(&ev as *const RawEvent, timeout as c_uint);
            unpack_event(rc, &ev)
        }
    }
}

impl Drop for RustBox {
    fn drop(&mut self) {
        // Since only one instance of the RustBox is ever accessible, we should not
        // need to do this atomically.
        // Note: we should definitely have RUSTBOX_RUNNING = true here.
        unsafe {
            termbox::tb_shutdown();
        }
        // Indicate that we're free now.  We could probably get away with lower atomicity here,
        // but there's no reason to take that chance.
        RUSTBOX_RUNNING.store(false, atomic::SeqCst);
    }
}
<|endoftext|>


<commit_before>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug,Clone,Copy)]
/// Represents a position in the source file.
/// Both line and column are represented by a 1-based
/// index, since this struct exists only to provide
/// context to the user.
pub struct Position {
    line: usize,
    column: usize,
}

impl Position {
    fn initial() -> Position {
        Position {
            line: 1,
            column: 1,
        }
    }

    fn increment_column(&mut self) -> () {
        self.column += 1;
    }

    fn increment_line(&mut self) -> () {
        self.line += 1;
        self.column = 1;
    }
}

pub type Lexeme = String;

#[derive(Debug)]
pub struct TokenWithContext {
    pub token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    pub lexeme: Lexeme,
    /// Position of the first character of the token
    pub position: Position,
}

#[derive(Debug,Clone)]
pub enum ScannerError {
    MissingStringTerminator(Position),
    UnexpectedCharacter(char, Position),
}

struct Scanner<'a> {
    current_position: Position,
    current_lexeme: String,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' => true,
        '\r' => true,
        '\t' => true,
        '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            current_position: Position::initial(),
            current_lexeme: "".into(),
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => {
                match self.source.peek() {
                    Some(&p2) => check1(p1) && check2(p2),
                    None => false,
                }
            }
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        let next = self.source.next();
        match next {
            Some(c) => {
                self.current_lexeme.push(c);
                if c == '\n' {
                    self.current_position.increment_line();
                } else {
                    self.current_position.increment_column();;
                }
            }
            None => (),
        }
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn advance_while(&mut self, condition: &Fn(char) -> bool) -> () {
        while self.peek_check(condition) {
            self.advance();
        }
    }

    fn add_context(&mut self, token: Token, initial_position: Position) -> TokenWithContext {
        let result = TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            position: initial_position,
        };
        self.current_lexeme = "".into();
        result
    }

    fn string(&mut self) -> Result<Token, ScannerError> {
        self.advance_while(&|c| c != '"' && c!= '\n');
        if !self.advance_if_match('"') {
            self.current_lexeme = "".into();
            return Err(ScannerError::MissingStringTerminator(self.current_position));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self.current_lexeme.chars().skip(1).take(literal_length).collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        self.advance_while(&is_digit);
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            self.advance_while(&is_digit);
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        self.advance_while(&is_alphanumeric);
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Option<Result<TokenWithContext, ScannerError>> {
        let initial_position = self.current_position;
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => return None,
        };
        let token = match next_char {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.advance_if_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    self.advance_while(&|c| c != '\n');
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            '"' => {
                match self.string() {
                    Ok(token) => token,
                    Err(error) => return Some(Err(error)),
                }
            }
            c if is_whitespace(c) => Token::Whitespace,
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                // TODO: check if position is off by one
                self.current_lexeme = "".into();
                return Some(Err(ScannerError::UnexpectedCharacter(c, self.current_position)));
            }
        };
        Some(Ok(self.add_context(token, initial_position)))
    }
}

pub fn scan(source: &String) -> (Vec<TokenWithContext>, Vec<ScannerError>) {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    let mut errors = Vec::new();
    while let Some(result) = scanner.scan_next() {
        match result {
            Ok(token_with_context) => {
                match token_with_context.token {
                    // Ignoring tokens we don't care about
                    Token::Comment => {}
                    Token::Whitespace => {}
                    _ => tokens.push(token_with_context),
                };
            }
            Err(error) => errors.push(error),
        }
    }
    (tokens, errors)
}

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

    #[test]
    fn single_token() {
        let (tokens, _) = scan(&"+".into());
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let (tokens, _) = scan(&"1+2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn expression_with_whitespaces() {
        let (tokens, _) = scan(&"1 + 2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn assignement_with_comment() {
        let (tokens, _) = scan(&"var a = 1.0; // A comment".into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
    }

    #[test]
    fn multiline_statements() {
        let (tokens, _) = scan(&r#"var a = 1.0;
                              var b = "Hello";"#
            .into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[1].position.line, 1);
        assert_eq!(tokens[1].position.column, 5);
        assert_eq!(tokens[9].position.line, 2);
        assert_eq!(tokens[9].position.column, 46); // There are 30 spaces before the text!
    }

    #[test]
    fn expression_with_bad_character() {
        let (tokens, errors) = scan(&"1 + $2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(errors.len(), 1);
    }

    #[test]
    fn statements_with_unterminated_string() {
        let (tokens, errors) = scan(&r#"var a = "Unterminated;
                              var b = "Hello";"#
            .into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        // Literal not reported, it wasn't terminated
        // Semicolon got embedded into the string
        assert_eq!(tokens[3].token, Token::Var);
        assert_eq!(tokens[4].token, Token::Identifier("b".into()));
        assert_eq!(tokens[5].token, Token::Equal);
        assert_eq!(tokens[6].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[7].token, Token::Semicolon);
        assert_eq!(errors.len(), 1);
    }

}
<commit_msg>cargo fmt<commit_after>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug,Clone,Copy)]
/// Represents a position in the source file.
/// Both line and column are represented by a 1-based
/// index, since this struct exists only to provide
/// context to the user.
pub struct Position {
    line: usize,
    column: usize,
}

impl Position {
    fn initial() -> Position {
        Position {
            line: 1,
            column: 1,
        }
    }

    fn increment_column(&mut self) -> () {
        self.column += 1;
    }

    fn increment_line(&mut self) -> () {
        self.line += 1;
        self.column = 1;
    }
}

pub type Lexeme = String;

#[derive(Debug)]
pub struct TokenWithContext {
    pub token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    pub lexeme: Lexeme,
    /// Position of the first character of the token
    pub position: Position,
}

#[derive(Debug,Clone)]
pub enum ScannerError {
    MissingStringTerminator(Position),
    UnexpectedCharacter(char, Position),
}

struct Scanner<'a> {
    current_position: Position,
    current_lexeme: String,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' => true,
        '\r' => true,
        '\t' => true,
        '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            current_position: Position::initial(),
            current_lexeme: "".into(),
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => {
                match self.source.peek() {
                    Some(&p2) => check1(p1) && check2(p2),
                    None => false,
                }
            }
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        let next = self.source.next();
        match next {
            Some(c) => {
                self.current_lexeme.push(c);
                if c == '\n' {
                    self.current_position.increment_line();
                } else {
                    self.current_position.increment_column();;
                }
            }
            None => (),
        }
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn advance_while(&mut self, condition: &Fn(char) -> bool) -> () {
        while self.peek_check(condition) {
            self.advance();
        }
    }

    fn add_context(&mut self, token: Token, initial_position: Position) -> TokenWithContext {
        let result = TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            position: initial_position,
        };
        self.current_lexeme = "".into();
        result
    }

    fn string(&mut self) -> Result<Token, ScannerError> {
        self.advance_while(&|c| c != '"' && c != '\n');
        if !self.advance_if_match('"') {
            self.current_lexeme = "".into();
            return Err(ScannerError::MissingStringTerminator(self.current_position));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self.current_lexeme.chars().skip(1).take(literal_length).collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        self.advance_while(&is_digit);
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            self.advance_while(&is_digit);
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        self.advance_while(&is_alphanumeric);
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Option<Result<TokenWithContext, ScannerError>> {
        let initial_position = self.current_position;
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => return None,
        };
        let token = match next_char {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.advance_if_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    self.advance_while(&|c| c != '\n');
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            '"' => {
                match self.string() {
                    Ok(token) => token,
                    Err(error) => return Some(Err(error)),
                }
            }
            c if is_whitespace(c) => Token::Whitespace,
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                // TODO: check if position is off by one
                self.current_lexeme = "".into();
                return Some(Err(ScannerError::UnexpectedCharacter(c, self.current_position)));
            }
        };
        Some(Ok(self.add_context(token, initial_position)))
    }
}

pub fn scan(source: &String) -> (Vec<TokenWithContext>, Vec<ScannerError>) {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    let mut errors = Vec::new();
    while let Some(result) = scanner.scan_next() {
        match result {
            Ok(token_with_context) => {
                match token_with_context.token {
                    // Ignoring tokens we don't care about
                    Token::Comment => {}
                    Token::Whitespace => {}
                    _ => tokens.push(token_with_context),
                };
            }
            Err(error) => errors.push(error),
        }
    }
    (tokens, errors)
}

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

    #[test]
    fn single_token() {
        let (tokens, _) = scan(&"+".into());
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let (tokens, _) = scan(&"1+2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn expression_with_whitespaces() {
        let (tokens, _) = scan(&"1 + 2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
    }

    #[test]
    fn assignement_with_comment() {
        let (tokens, _) = scan(&"var a = 1.0; // A comment".into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
    }

    #[test]
    fn multiline_statements() {
        let (tokens, _) = scan(&r#"var a = 1.0;
                              var b = "Hello";"#
            .into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[1].position.line, 1);
        assert_eq!(tokens[1].position.column, 5);
        assert_eq!(tokens[9].position.line, 2);
        assert_eq!(tokens[9].position.column, 46); // There are 30 spaces before the text!
    }

    #[test]
    fn expression_with_bad_character() {
        let (tokens, errors) = scan(&"1 + $2".into());
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(errors.len(), 1);
    }

    #[test]
    fn statements_with_unterminated_string() {
        let (tokens, errors) = scan(&r#"var a = "Unterminated;
                              var b = "Hello";"#
            .into());
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        // Literal not reported, it wasn't terminated
        // Semicolon got embedded into the string
        assert_eq!(tokens[3].token, Token::Var);
        assert_eq!(tokens[4].token, Token::Identifier("b".into()));
        assert_eq!(tokens[5].token, Token::Equal);
        assert_eq!(tokens[6].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[7].token, Token::Semicolon);
        assert_eq!(errors.len(), 1);
    }

}
<|endoftext|>


<commit_before>use semver::Version;

use libimagstore::store::Entry;

use builtin::header::version::gt::VersionGt;
use builtin::header::version::lt::VersionLt;
use filters::filter::Filter;
use ops::and::And;
use ops::not::Not;

pub struct VersionInRange {
    and: And,
}

impl VersionInRange {

    pub fn new(lowerbound: Version, upperbound: Version) -> VersionInRange {
        VersionInRange { and: VersionGt::new(lowerbound).and(Box::new(VersionLt::new(upperbound))) }
    }

}

impl Filter<Entry> for VersionInRange {

    fn filter(&self, e: &Entry) -> bool {
        self.and.filter(e)
    }

}

pub struct VersionOutOfRange {
    not: Not
}

impl VersionOutOfRange {

    pub fn new(lowerbound: Version, upperbound: Version) -> VersionOutOfRange {
        VersionOutOfRange { not: VersionInRange::new(lowerbound, upperbound).not() }
    }

}

impl Filter<Entry> for VersionOutOfRange {

    fn filter(&self, e: &Entry) -> bool {
        self.not.filter(e)
    }

}

<commit_msg>libimagentryfilter: Update VersionInRange<commit_after>use semver::Version;

use libimagstore::store::Entry;

use builtin::header::version::gt::VersionGt;
use builtin::header::version::lt::VersionLt;
use filters::filter::Filter;
use filters::ops::and::And;
use filters::ops::not::Not;

pub struct VersionInRange {
    and: And<VersionGt, VersionLt>,
}

impl VersionInRange {

    pub fn new(lowerbound: Version, upperbound: Version) -> VersionInRange {
        VersionInRange { and: VersionGt::new(lowerbound).and(VersionLt::new(upperbound)) }
    }

}

impl Filter<Entry> for VersionInRange {

    fn filter(&self, e: &Entry) -> bool {
        self.and.filter(e)
    }

}

pub struct VersionOutOfRange {
    not: Not<VersionInRange>
}

impl VersionOutOfRange {

    pub fn new(lowerbound: Version, upperbound: Version) -> VersionOutOfRange {
        VersionOutOfRange { not: VersionInRange::new(lowerbound, upperbound).not() }
    }

}

impl Filter<Entry> for VersionOutOfRange {

    fn filter(&self, e: &Entry) -> bool {
        self.not.filter(e)
    }

}

<|endoftext|>


<commit_before>//! # Scraper module.
//!
//! The Scraper module fetch metrics to Prometheus.
use std::thread;
use std::time::Duration;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use time;
use std::cmp;
use hyper;
use std::io::prelude::*;
use std::fs;
use std::fs::File;
use std::error::Error;
use std::path::Path;

use config;

/// Thread sleeping time.
const REST_TIME: u64 = 10;

/// Scraper loop.
pub fn scraper(scraper: &config::Scraper,
               parameters: &config::Parameters,
               sigint: Arc<AtomicBool>) {
    loop {
        let start = time::now_utc();

        match fetch(scraper, parameters) {
            Err(err) => error!("fetch fail: {}", err),
            Ok(_) => info!("fetch success"),
        }

        let elapsed = (time::now_utc() - start).num_milliseconds() as u64;
        let sleep_time = if elapsed > scraper.period {
            REST_TIME
        } else {
            cmp::max(scraper.period - elapsed, REST_TIME)
        };
        for _ in 0..sleep_time / REST_TIME {
            thread::sleep(Duration::from_millis(REST_TIME));
            if sigint.load(Ordering::Relaxed) {
                return;
            }
        }
    }
}

/// Fetch retrieve metrics from Prometheus.
fn fetch(scraper: &config::Scraper, parameters: &config::Parameters) -> Result<(), Box<Error>> {
    debug!("fetch {}", &scraper.url);

    // Fetch metrics
    let mut client = hyper::Client::new();
    client.set_write_timeout(Some(Duration::from_secs(parameters.timeout)));
    client.set_read_timeout(Some(Duration::from_secs(parameters.timeout)));

    let mut res = try!(client.get(&scraper.url).send());
    if !res.status.is_success() {
        return Err(From::from("non 200 received"));
    }

    // Read body
    let mut body = String::new();
    try!(res.read_to_string(&mut body));
    trace!("data {}", &body);

    // Get now as millis
    let start = time::now_utc();
    let now = start.to_timespec().sec * 1000 * 1000 + (start.to_timespec().nsec as i64 / 1000);

    let dir = Path::new(&parameters.source_dir);
    let temp_file = dir.join(format!("{}.tmp", scraper.name));
    debug!("write to tmp file {}", format!("{:?}", temp_file));
    {
        // Open tmp file
        let mut file = try!(File::create(&temp_file));

        for line in body.lines() {
            let line = match scraper.format {
                config::ScraperFormat::Sensision => String::from(line.trim()),
                config::ScraperFormat::Prometheus => {
                    match format_prometheus(line.trim(), now) {
                        Err(_) => {
                            warn!("bad row {}", &line);
                            continue;
                        }
                        Ok(v) => v,
                    }
                }
            };

            if line.is_empty() {
                continue;
            }

            if scraper.metrics.is_some() {
                if !scraper.metrics.as_ref().unwrap().is_match(&line) {
                    continue;
                }
            }

            try!(file.write(line.as_bytes()));
            try!(file.write(b"\n"));
        }

        try!(file.flush());
    }

    // Rotate scraped file
    let dest_file = dir.join(format!("{}-{}.metrics", scraper.name, now));
    debug!("rotate tmp file to {}", format!("{:?}", dest_file));
    try!(fs::rename(&temp_file, &dest_file));

    Ok(())
}

/// Format Warp10 metrics from Prometheus one.
fn format_prometheus(line: &str, now: i64) -> Result<String, Box<Error>> {
    // Skip comments
    if line.starts_with("#") {
        return Ok(String::new());
    }

    // Extract Prometheus metric
    let index = if line.contains("{") {
        try!(line.rfind('}').ok_or("bad class"))
    } else {
        try!(line.find(' ').ok_or("bad class"))
    };
    let (class, v) = line.split_at(index + 1);
    let mut tokens = v.split_whitespace();

    let value = try!(tokens.next().ok_or("no value"));
    let timestamp = tokens
        .next()
        .map(|v| i64::from_str_radix(v, 10).map(|v| v * 1000).unwrap_or(now))
        .unwrap_or(now);

    // Format class
    let mut parts = class.splitn(2, "{");
    let class = String::from(try!(parts.next().ok_or("no_class")));
    let class = class.trim();
    let plabels = parts.next();
    let slabels = if plabels.is_some() {
        let mut labels = plabels.unwrap().split("\",")
            .map(|v| v.replace("=", "%3D")) // escape
            .map(|v| v.replace("%3D\"", "=")) // remove left double quote
            .map(|v| v.replace("\"}", "")) // remove right double quote
            .map(|v| v.replace(",", "%2C")) // escape
            .map(|v| v.replace("}", "%7D")) // escape
            .map(|v| v.replace(r"\\", r"\")) // unescape
            .map(|v| v.replace("\\\"", "\"")) // unescape
            .map(|v| v.replace(r"\n", "%0A")) // unescape
            .fold(String::new(), |acc, x| {
                // skip invalid values
                if !x.contains("=") {
                    return acc
                }
                acc + &x + ","
            });
        labels.pop();
        labels
    } else {
        String::new()
    };

    let class = format!("{}{{{}}}", class, slabels);

    Ok(format!("{}// {} {}", timestamp, class, value))
}
<commit_msg>fix : adding https scheme<commit_after>//! # Scraper module.
//!
//! The Scraper module fetch metrics to Prometheus.
use std::thread;
use std::time::Duration;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use time;
use std::cmp;
use hyper;
use hyper_native_tls::NativeTlsClient;
use std::io::prelude::*;
use std::fs;
use std::fs::File;
use std::error::Error;
use std::path::Path;

use config;

/// Thread sleeping time.
const REST_TIME: u64 = 10;

/// Scraper loop.
pub fn scraper(scraper: &config::Scraper,
               parameters: &config::Parameters,
               sigint: Arc<AtomicBool>) {
    loop {
        let start = time::now_utc();

        match fetch(scraper, parameters) {
            Err(err) => error!("fetch fail: {}", err),
            Ok(_) => info!("fetch success"),
        }

        let elapsed = (time::now_utc() - start).num_milliseconds() as u64;
        let sleep_time = if elapsed > scraper.period {
            REST_TIME
        } else {
            cmp::max(scraper.period - elapsed, REST_TIME)
        };
        for _ in 0..sleep_time / REST_TIME {
            thread::sleep(Duration::from_millis(REST_TIME));
            if sigint.load(Ordering::Relaxed) {
                return;
            }
        }
    }
}

/// Fetch retrieve metrics from Prometheus.
fn fetch(scraper: &config::Scraper, parameters: &config::Parameters) -> Result<(), Box<Error>> {
    debug!("fetch {}", &scraper.url);

    // Fetch metrics
    let ssl = NativeTlsClient::new().unwrap();
    let connector = hyper::net::HttpsConnector::new(ssl);
    let mut client = hyper::Client::with_connector(connector);

    client.set_write_timeout(Some(Duration::from_secs(parameters.timeout)));
    client.set_read_timeout(Some(Duration::from_secs(parameters.timeout)));

    let mut res = try!(client.get(&scraper.url).send());
    if !res.status.is_success() {
        return Err(From::from("non 200 received"));
    }

    // Read body
    let mut body = String::new();
    try!(res.read_to_string(&mut body));
    trace!("data {}", &body);

    // Get now as millis
    let start = time::now_utc();
    let now = start.to_timespec().sec * 1000 * 1000 + (start.to_timespec().nsec as i64 / 1000);

    let dir = Path::new(&parameters.source_dir);
    let temp_file = dir.join(format!("{}.tmp", scraper.name));
    debug!("write to tmp file {}", format!("{:?}", temp_file));
    {
        // Open tmp file
        let mut file = try!(File::create(&temp_file));

        for line in body.lines() {
            let line = match scraper.format {
                config::ScraperFormat::Sensision => String::from(line.trim()),
                config::ScraperFormat::Prometheus => {
                    match format_prometheus(line.trim(), now) {
                        Err(_) => {
                            warn!("bad row {}", &line);
                            continue;
                        }
                        Ok(v) => v,
                    }
                }
            };

            if line.is_empty() {
                continue;
            }

            if scraper.metrics.is_some() {
                if !scraper.metrics.as_ref().unwrap().is_match(&line) {
                    continue;
                }
            }

            try!(file.write(line.as_bytes()));
            try!(file.write(b"\n"));
        }

        try!(file.flush());
    }

    // Rotate scraped file
    let dest_file = dir.join(format!("{}-{}.metrics", scraper.name, now));
    debug!("rotate tmp file to {}", format!("{:?}", dest_file));
    try!(fs::rename(&temp_file, &dest_file));

    Ok(())
}

/// Format Warp10 metrics from Prometheus one.
fn format_prometheus(line: &str, now: i64) -> Result<String, Box<Error>> {
    // Skip comments
    if line.starts_with("#") {
        return Ok(String::new());
    }

    // Extract Prometheus metric
    let index = if line.contains("{") {
        try!(line.rfind('}').ok_or("bad class"))
    } else {
        try!(line.find(' ').ok_or("bad class"))
    };
    let (class, v) = line.split_at(index + 1);
    let mut tokens = v.split_whitespace();

    let value = try!(tokens.next().ok_or("no value"));
    let timestamp = tokens
        .next()
        .map(|v| i64::from_str_radix(v, 10).map(|v| v * 1000).unwrap_or(now))
        .unwrap_or(now);

    // Format class
    let mut parts = class.splitn(2, "{");
    let class = String::from(try!(parts.next().ok_or("no_class")));
    let class = class.trim();
    let plabels = parts.next();
    let slabels = if plabels.is_some() {
        let mut labels = plabels.unwrap().split("\",")
            .map(|v| v.replace("=", "%3D")) // escape
            .map(|v| v.replace("%3D\"", "=")) // remove left double quote
            .map(|v| v.replace("\"}", "")) // remove right double quote
            .map(|v| v.replace(",", "%2C")) // escape
            .map(|v| v.replace("}", "%7D")) // escape
            .map(|v| v.replace(r"\\", r"\")) // unescape
            .map(|v| v.replace("\\\"", "\"")) // unescape
            .map(|v| v.replace(r"\n", "%0A")) // unescape
            .fold(String::new(), |acc, x| {
                // skip invalid values
                if !x.contains("=") {
                    return acc
                }
                acc + &x + ","
            });
        labels.pop();
        labels
    } else {
        String::new()
    };

    let class = format!("{}{{{}}}", class, slabels);

    Ok(format!("{}// {} {}", timestamp, class, value))
}
<|endoftext|>


<commit_before>pub struct SimplexVertex {
    /// Support point for proxy_a
    pub w_a: Vec2,
    /// Support point for proxy_b
    pub w_b: Vec2,
    /// w_b - w_a
    pub w: Vec2,
    /// barycentric coordinate for closest point
    pub a: f32,
    /// w_a index
    pub index_a: i32,
    /// w_b index
    pub index_b: i32
}

impl SimplexVertex {
    pub fn new() -> SimplexVertex {
        SimplexVertex {
            w_a: Vec2::zero(),
            w_b: Vec2::zero(),
            w: Vec2::zero(),
            a: 0.0,
            index_a: 0,
            index_b: 0
        }
    }
}
<commit_msg>Fixing simplex vertex<commit_after>use super::super::common::Vec2;

pub struct SimplexVertex {
    /// Support point for proxy_a
    pub w_a: Vec2,
    /// Support point for proxy_b
    pub w_b: Vec2,
    /// w_b - w_a
    pub w: Vec2,
    /// barycentric coordinate for closest point
    pub a: f32,
    /// w_a index
    pub index_a: i32,
    /// w_b index
    pub index_b: i32
}

impl SimplexVertex {
    pub fn new() -> SimplexVertex {
        SimplexVertex {
            w_a: Vec2::zero(),
            w_b: Vec2::zero(),
            w: Vec2::zero(),
            a: 0.0,
            index_a: 0,
            index_b: 0
        }
    }
}
<|endoftext|>