<commit_before>extern crate futures;
extern crate hyper;
extern crate tokio_core;

use futures::Future;
use futures::stream::Stream;

use hyper::Client;

type Event = hyper::Chunk;

pub struct ChangesStream {
    db: hyper::Url,
    lp: tokio_core::reactor::Core,
    handlers: Vec<Box<Fn(&Event)>>,
}

impl ChangesStream {
    pub fn new(db: String) -> ChangesStream {
        ChangesStream {
            db: db.parse().unwrap(),
            lp: tokio_core::reactor::Core::new().unwrap(),
            handlers: vec![],
        }
    }

    pub fn on<F: Fn(&Event) + 'static>(&mut self, handler: F) {
        self.handlers.push(Box::new(handler));
    }

    pub fn run(mut self) {
        let client = Client::new(&self.lp.handle()).unwrap();

        let handlers = self.handlers;
        self.lp
            .run(client.get(self.db).and_then(move |res| {
                assert!(res.status().is_success());

                res.body().for_each(move |chunk| {
                    for handler in &handlers {
                        // let event = serde_json::decode(chunk);
                        handler(&chunk);
                    }
                    Ok(())
                })
            }))
            .unwrap();

    }
}
<commit_msg>add documetation<commit_after>//! This is documentation for the `changes-stream` crate.
//!
//! The `changes-stream` crate is designed to give you a readable stream of
//! chunked data, upon which you can register multiple handlers, that are
//! called on Read of the data chunk.

extern crate futures;
extern crate hyper;
extern crate tokio_core;

use futures::Future;
use futures::stream::Stream;

use hyper::Client;

type Event = hyper::Chunk;

/// A structure to generate a readable stream on which you can register handlers.
///
/// Internally, the `ChangesStream` struct holds 3 members:
///
/// | Member      | Type                                  | Notes                                                                   |
/// |-------------|---------------------------------------|-------------------------------------------------------------------------|
/// | `db`        | `String`                              | A url pointing to the data you'd like to stream.                        |
/// | `lp`        | [`tokio_core::reactor::Core`]         | The event loop                                                          |
/// | `handlers`  | `Vec<F> where F: Fn(&`[`hyper::Chunk`]`)` | A vector of handlers to be called on each Chunk from the Stream on Read |
///
/// [`tokio_core::reactor::Core`]: ../tokio_core/reactor/struct.Core.html
/// [`hyper::Chunk`]: ../hyper/struct.Chunk.html
pub struct ChangesStream {
    db: hyper::Url,
    lp: tokio_core::reactor::Core,
    handlers: Vec<Box<Fn(&Event)>>,
}

impl ChangesStream {

    /// Constructs a new `ChangesStream` struct
    ///
    /// Takes a single argument, `db`, which represents the
    /// url of the data you wish to stream.
    ///
    /// Every `ChangesStream` struct is initialized with
    /// an event loop ([`tokio_core::reactor::Core`]) and an
    /// empty vector of handlers. See above for more details.
    ///
    /// [`tokio_core::reactor::Core`]: ../tokio_core/reactor/struct.Core.html
    ///
    /// For example, to create a new `ChangesStream` struct
    /// for the npmjs registry, you would write:
    ///
    /// ```no_run
    /// # extern crate changes_stream;
    /// # extern crate futures;
    /// #
    /// # use std::io;
    /// # use std::io::Write;
    /// #
    /// # use changes_stream::ChangesStream;
    /// #
    /// # fn main() {
    ///     let url = "https://replicate.npmjs.com/_changes".to_string();
    ///     let mut changes = ChangesStream::new(url);
    /// #
    /// #   changes.on(|change| {
    /// #       io::stdout().write_all(&change).unwrap();
    /// #   });
    /// #
    /// #   changes.run();
    /// # }
    /// ```
    pub fn new(db: String) -> ChangesStream {
        ChangesStream {
            db: db.parse().unwrap(),
            lp: tokio_core::reactor::Core::new().unwrap(),
            handlers: vec![],
        }
    }

    /// Registers a handler. A handler is simply a function
    /// you'd like to call on a chunk from the stream at the
    /// time the chunk is read.
    ///
    /// `.on()` takes a single argument, a closure. The
    /// closure you pass should take a single [`hyper::Chunk`]
    /// as an argument.
    ///
    /// [`hyper::Chunk`]: ../hyper/struct.Chunk.html
    ///
    /// For example, to write the data in a chunk to standard
    /// out, you would write:
    ///
    /// ```no_run
    /// # extern crate changes_stream;
    /// # extern crate futures;
    /// #
    /// # use std::io;
    /// # use std::io::Write;
    /// #
    /// # use changes_stream::ChangesStream;
    /// #
    /// # fn main() {
    /// #   let url = "https://replicate.npmjs.com/_changes".to_string();
    /// #   let mut changes = ChangesStream::new(url);
    /// #
    ///    changes.on(|change| {
    ///        io::stdout().write_all(&change).unwrap();
    ///    });
    /// #
    /// #   changes.run();
    /// # }
    /// ```
    pub fn on<F: Fn(&Event) + 'static>(&mut self, handler: F) {
        self.handlers.push(Box::new(handler));
    }

    /// Runs the `ChangesStream` struct's event loop, `lp`.
    ///
    /// Call this after you have regsitered all handlers using
    /// `on`.
    ///
    /// Takes no arguments.
    ///
    /// For example:
    ///
    /// ```no_run
    /// # extern crate changes_stream;
    /// # extern crate futures;
    /// #
    /// # use std::io;
    /// # use std::io::Write;
    /// #
    /// # use changes_stream::ChangesStream;
    /// #
    /// # fn main() {
    /// #   let url = "https://replicate.npmjs.com/_changes".to_string();
    /// #   let mut changes = ChangesStream::new(url);
    /// #
    /// #   changes.on(|change| {
    /// #       io::stdout().write_all(&change).unwrap();
    /// #   });
    /// #
    ///    changes.run();
    /// # }
    /// ```
    pub fn run(mut self) {
        let client = Client::new(&self.lp.handle()).unwrap();

        let handlers = self.handlers;
        self.lp
            .run(client.get(self.db).and_then(move |res| {
                assert!(res.status().is_success());

                res.body().for_each(move |chunk| {
                    for handler in &handlers {
                        // let event = serde_json::decode(chunk);
                        handler(&chunk);
                    }
                    Ok(())
                })
            }))
            .unwrap();

    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate rustc_serialize;

mod error;
mod util;
pub mod types;

pub use types::*;
pub use error::*;
use util::Params;

use rustc_serialize::{json, Decodable};
use std::io::Read;
use hyper::{Client, Url};
use hyper::client::IntoUrl;
use hyper::header::Connection;

/// API-URL prefix
pub const API_URL : &'static str = "https://api.telegram.org/bot";

/// Main type for sending requests to the Telegram bot API.
pub struct Api {
    url: Url,
    client: Client,
}

impl Api {
    // =======================================================================
    // Constructors
    // =======================================================================
    /// Creates a new bot with the given token. If the token is completely
    /// invalid (resulting in an invalid API-URL), the function will panic.
    /// However, the function will not check if the given token is a valid
    /// Telegram token. You can call `get_me` to execute a test request.
    pub fn new(token: String) -> Api {
        let url = match Url::parse(&format!("{}{}/dummy", API_URL, token)) {
            Ok(url) => url,
            Err(e) => panic!("Invalid token! ({})", e),
        };
        Api {
            url: url,
            client: Client::new(),
        }
    }

    // =======================================================================
    // Methods corresponding directly to a API method
    // =======================================================================
    /// Corresponds to the "getMe" method of the API.
    pub fn get_me(&mut self) -> Result<User> {
        // Execute request with empty parameter list
        self.send_request("getMe", Params::new())
    }

    /// Corresponds to the "sendMessage" method of the API.
    pub fn send_message(&mut self, chat_id: Integer, text: String,
                        disable_web_page_preview: Option<bool>,
                        reply_to_message_id: Option<Integer>,
                        reply_markup: Option<ReplyMarkup>)
                        -> Result<Message> {
        // Prepare parameters
        let mut params = Params::new();
        params.add_get("chat_id", chat_id);
        params.add_get("text", text);
        params.add_get_opt("disable_web_page_preview", disable_web_page_preview);
        params.add_get_opt("reply_to_message_id", reply_to_message_id);
        try!(params.add_get_json_opt("reply_markup", reply_markup));

        // Execute request
        self.send_request("sendMessage", params)
    }

    /// Corresponds to the "forwardMessage" method of the API.
    pub fn forward_message(&mut self, chat_id: Integer, from_chat_id: Integer,
                           message_id: Integer) -> Result<Message> {
        // Prepare parameters
        let mut params = Params::new();
        params.add_get("chat_id", chat_id);
        params.add_get("from_chat_id", from_chat_id);
        params.add_get("message_id", message_id);

        // Execute request
        self.send_request("forwardMessage", params)
    }

    /// Corresponds to the "sendLocation" method of the API.
    pub fn send_location(&mut self, chat_id: Integer, latitude: Float,
                         longitude: Float, reply_to_message_id: Option<Integer>,
                         reply_markup: Option<ReplyMarkup>)
                         -> Result<Message> {
        // Prepare parameters
        let mut params = Params::new();
        params.add_get("chat_id", chat_id);
        params.add_get("latitude", latitude);
        params.add_get("longitude", longitude);
        params.add_get_opt("reply_to_message_id", reply_to_message_id);
        try!(params.add_get_json_opt("reply_markup", reply_markup));

        // Execute request
        self.send_request("sendLocation", params)
    }

    /// Corresponds to the "sendChatAction" method of the API.
    pub fn send_chat_action(&mut self, chat_id: Integer, action: ChatAction)
                            -> Result<bool> {
        let mut params = Params::new();
        params.add_get("chat_id", chat_id);
        params.add_get("action", action);

        // Execute request
        self.send_request("sendChatAction", params)
    }

    /// Corresponds to the "getUserProfilePhotos" method of the API.
    pub fn get_user_profile_photos(&mut self, user_id: Integer,
                                   offset: Option<Integer>,
                                   limit: Option<Integer>)
                                   -> Result<UserProfilePhotos> {
        let mut params = Params::new();
        params.add_get("user_id", user_id);
        params.add_get_opt("offset", offset);
        params.add_get_opt("limit", limit);

        // Execute request
        self.send_request("getUserProfilePhotos", params)
    }

    /// Corresponds to the "getUpdates" method of the API.
    ///
    /// **Note:**
    /// The method will not set the offset parameter on its own. To receive
    /// updates in a more high level way, see `long_poll`.
    pub fn get_updates(&mut self, offset: Option<Integer>,
                       limit: Option<Integer>, timeout: Option<Integer>)
                       -> Result<Vec<Update>> {
        // Prepare parameters
        let mut params = Params::new();
        params.add_get_opt("offset", offset);
        params.add_get_opt("limit", limit);
        params.add_get_opt("timeout", timeout);

        // Execute request
        self.send_request("getUpdates", params)
    }

    pub fn set_webhook<U: IntoUrl>(&mut self, url: Option<U>) -> Result<bool> {
        let u = url.map_or("".into(), |u| u.into_url().unwrap().to_string());

        // Prepare parameters
        let mut params = Params::new();
        params.add_get("url", u);

        // Execute request
        self.send_request("setWebhook", params)
    }

    // =======================================================================
    // Methods for receiving updates
    // =======================================================================
    /// Receive and handle updates via "getUpdates".
    ///
    /// This method will repeatedly call `get_updates` to receive new updates.
    /// It will then call the given handler for every update and increase the
    /// update offset accordingly, so the handler will never be called with
    /// the same update twice.
    /// The `timeout` parameter influences how long (in seconds) each poll may
    /// last. Defaults to 30.
    /// The handler gets a mutable reference to the bot since borrowing it
    /// from the outer scope won't work. When the handler returns an `Err`
    /// value the bot will stop listening for updates and `long_poll` will
    /// return the Error. If you want to stop listening you can just return
    /// `Error::UserInterrupt`.
    ///
    /// **Note:**
    /// If the bot is restarted, but the last received updates are not yet
    /// confirmed (the last poll was not empty), there will be some duplicate
    /// updates.
    // pub fn long_poll<H>(&mut self, timeout: Option<Integer>, mut handler: H)
    //                     -> Result<()>
    //                     where H: FnMut(&mut Api, Update) -> Result<()> {
    //     // Calculate final timeout: Given or default (30s)
    //     let timeout = timeout.or(Some(30));

    //     loop {
    //         // Receive updates with correct offset
    //         let offset = Some(self.offset);
    //         let updates = try!(self.get_updates(offset, None, timeout));

    //         // For every update: Increase the offset and call the handler.
    //         for u in updates {
    //             if u.update_id >= self.offset {
    //                 self.offset = u.update_id + 1;
    //             }

    //             try!(handler(self, u));
    //         }
    //     }
    // }

    pub fn listener(&self, method: ListeningMethod) -> Listener {
        Listener {
            method: method,
            confirmed: 0,
            url: self.url.clone(),
            client: Client::new(),
        }
    }

    // =======================================================================
    // Private methods
    // =======================================================================
    fn send_request<T: Decodable>(&mut self, method: &str, p: Params)
        -> Result<T>
    {
        Self::request(&self.client, &self.url, method, p)
    }

    fn request<T: Decodable>(client: &Client, url: &Url,
                             method: &str, params: Params) -> Result<T> {
        // Prepare URL for request: Clone and change the last path fragment
        // to the method name and append GET parameters.
        let mut url = url.clone();
        url.path_mut().map(|path| {         // if theres a path: Change it
            path.last_mut().map(|last| {    // if its not empty: Change last...
                *last = method.into()       // ... into method name
            })
        });

        // For all (str, String) pairs: Map to (str, str) and append it to URL
        let it = params.get_params().into_iter().map(|&(k, ref v)| (k, &**v));
        url.set_query_from_pairs(it);

        // Prepare HTTP Request
        let req = client.get(url).header(Connection::close());

        // Send request and check if it failed
        let mut resp = try!(req.send());

        // Read response into String and return error if it failed
        let mut body = String::new();
        try!(resp.read_to_string(&mut body));

        // Try to decode response as JSON representing a Response
        match try!(json::decode(&body)) {
            // If the response says that there was an error: Return API-Error
            // with the given description.
            Response { ok: false, description: Some(desc), ..} => {
                Err(Error::Api(desc))
            },
            // If response is "ok": Return the result.
            Response { ok: true, result: Some(res), ..} => {
                Ok(res)
            },
            // This should never occur: If "ok"==false, "description" should
            // always be Some. If "ok"==true, then "result" should always be
            // Some. We could also panic in this case.
            _ => Err(Error::InvalidState("Invalid server response".into())),
        }
    }
}

/// Different method how to listen for new updates. Currently `LongPoll` is
/// the only method supported by this library. The Telegram API offers a
/// webhook method which is not yet implemented here.
pub enum ListeningMethod {
    LongPoll(Option<Integer>),
}

/// A listening handler returns this type to signal the listening method either
/// to stop or to continue. If a handler returns `Stop`, the update it was
/// passed counts as "handled" and won't be handled again.
pub enum HandlerResult {
    Continue,
    Stop
}

/// Offers methods to easily receive new updates via the specified method. This
/// should be used instead of calling methods like `get_updates` yourself.
///
/// To create a listener, you first have to create a `Api` object and call
/// `listener` on it. In order to make listening easier in a concurrent
/// environment, the `Listener` object and the `Api` object don't share any
/// internal state. This makes creating a `Listener` a bit more expensive, but
/// it's usually sufficient to create only one `Listener` once.
pub struct Listener {
    method: ListeningMethod,
    confirmed: Integer,
    url: Url,
    client: Client,
}

impl Listener {
    /// Receive and handle updates with the given closure.
    ///
    /// This method will use the specified listening method to receive new
    /// updates and will then call the given handler for every update. Normally
    /// the handler won't ever be called for the same update twice (see Note
    /// below).
    /// When the handler returns an `Err` value, this function will stop
    /// listening and return the same `Err`. If you want to stop listening you
    /// can return `Ok(HandlerResult::Stop)` instead of an `Err` value.
    ///
    /// When returning an `Ok` value, the update that was passed to the handler
    /// is considered handled and won't be passed to a handler again. On the
    /// other hand if an `Err` is returned, the update is not considered handled
    /// so it will be passed to a handler the next time again.
    ///
    /// **Note:**
    /// If you are listening via `LongPoll` method and your handler panics or
    /// the program is aborted in a not natural way (ctrl + c), the  handler
    /// might receive some already handled updates a second time.
    pub fn listen<H>(&mut self, mut handler: H) -> Result<()>
        where H: FnMut(Update) -> Result<HandlerResult>
    {
        match self.method {
            ListeningMethod::LongPoll(timeout) => {
                // `handled_until` will hold the id of the last handled update
                let mut handled_until = self.confirmed;

                // Calculate final timeout: Given or default (30s)
                let timeout = timeout.or(Some(30));

                loop {
                    // Receive updates with correct offset. We don't specify a
                    // limit (Telegram limits to 100 automatically).
                    let mut params = Params::new();
                    params.add_get_opt("offset", Some(handled_until));
                    params.add_get_opt("timeout", timeout);

                    // Execute request and increas
                    let updates : Vec<Update> = try!(Api::request(
                        &self.client, &self.url, "getUpdates", params
                    ));
                    self.confirmed = handled_until;

                    // For every update: Increase the offset & call the handler.
                    for u in updates {
                        let update_id = u.update_id;

                        // Execute the handler and save it's result.
                        let res = handler(u);

                        // If an error was returned: Confirm the update before
                        // (if necessary) and return the given error.
                        if let Err(e) = res {
                            // Send a last request to confirm already handled updates.
                            let mut params = Params::new();
                            params.add_get("offset", handled_until);
                            params.add_get("timeout", 0);
                            params.add_get("limit", 0);

                            let _ : Result<Vec<Update>> = Api::request(
                                &self.client, &self.url, "getUpdates", params
                            );
                            self.confirmed = handled_until;

                            return Err(e);
                        }

                        // The update is now considered "handled". The
                        // if-condition should always be true.
                        if update_id >= handled_until {
                            handled_until = update_id + 1;
                        }

                        // If an Ok(Stop) was returned, stop listening now with
                        // confirmed update.
                        if let Ok(HandlerResult::Stop) = res {
                            // Send a last request to confirm already handled updates.
                            let mut params = Params::new();
                            params.add_get("offset", handled_until);
                            params.add_get("timeout", 0);
                            params.add_get("limit", 0);

                            let _ : Result<Vec<Update>> = Api::request(
                                &self.client, &self.url, "getUpdates", params
                            );
                            self.confirmed = handled_until;

                            return Ok(());
                        }
                    }
                }
            }
        }
    }
}
<commit_msg>Add `channel` method to `Listener`<commit_after>extern crate hyper;
extern crate rustc_serialize;

mod error;
mod util;
pub mod types;

pub use types::*;
pub use error::*;
use util::Params;

use rustc_serialize::{json, Decodable};
use std::io::Read;
use std::sync::mpsc;
use std::thread;
use hyper::{Client, Url};
use hyper::client::IntoUrl;
use hyper::header::Connection;

/// API-URL prefix
pub const API_URL : &'static str = "https://api.telegram.org/bot";

/// Main type for sending requests to the Telegram bot API.
pub struct Api {
    url: Url,
    client: Client,
}

impl Api {
    // =======================================================================
    // Constructors
    // =======================================================================
    /// Creates a new bot with the given token. If the token is completely
    /// invalid (resulting in an invalid API-URL), the function will panic.
    /// However, the function will not check if the given token is a valid
    /// Telegram token. You can call `get_me` to execute a test request.
    pub fn new(token: String) -> Api {
        let url = match Url::parse(&format!("{}{}/dummy", API_URL, token)) {
            Ok(url) => url,
            Err(e) => panic!("Invalid token! ({})", e),
        };
        Api {
            url: url,
            client: Client::new(),
        }
    }

    // =======================================================================
    // Methods corresponding directly to a API method
    // =======================================================================
    /// Corresponds to the "getMe" method of the API.
    pub fn get_me(&mut self) -> Result<User> {
        // Execute request with empty parameter list
        self.send_request("getMe", Params::new())
    }

    /// Corresponds to the "sendMessage" method of the API.
    pub fn send_message(&mut self, chat_id: Integer, text: String,
                        disable_web_page_preview: Option<bool>,
                        reply_to_message_id: Option<Integer>,
                        reply_markup: Option<ReplyMarkup>)
                        -> Result<Message> {
        // Prepare parameters
        let mut params = Params::new();
        params.add_get("chat_id", chat_id);
        params.add_get("text", text);
        params.add_get_opt("disable_web_page_preview", disable_web_page_preview);
        params.add_get_opt("reply_to_message_id", reply_to_message_id);
        try!(params.add_get_json_opt("reply_markup", reply_markup));

        // Execute request
        self.send_request("sendMessage", params)
    }

    /// Corresponds to the "forwardMessage" method of the API.
    pub fn forward_message(&mut self, chat_id: Integer, from_chat_id: Integer,
                           message_id: Integer) -> Result<Message> {
        // Prepare parameters
        let mut params = Params::new();
        params.add_get("chat_id", chat_id);
        params.add_get("from_chat_id", from_chat_id);
        params.add_get("message_id", message_id);

        // Execute request
        self.send_request("forwardMessage", params)
    }

    /// Corresponds to the "sendLocation" method of the API.
    pub fn send_location(&mut self, chat_id: Integer, latitude: Float,
                         longitude: Float, reply_to_message_id: Option<Integer>,
                         reply_markup: Option<ReplyMarkup>)
                         -> Result<Message> {
        // Prepare parameters
        let mut params = Params::new();
        params.add_get("chat_id", chat_id);
        params.add_get("latitude", latitude);
        params.add_get("longitude", longitude);
        params.add_get_opt("reply_to_message_id", reply_to_message_id);
        try!(params.add_get_json_opt("reply_markup", reply_markup));

        // Execute request
        self.send_request("sendLocation", params)
    }

    /// Corresponds to the "sendChatAction" method of the API.
    pub fn send_chat_action(&mut self, chat_id: Integer, action: ChatAction)
                            -> Result<bool> {
        let mut params = Params::new();
        params.add_get("chat_id", chat_id);
        params.add_get("action", action);

        // Execute request
        self.send_request("sendChatAction", params)
    }

    /// Corresponds to the "getUserProfilePhotos" method of the API.
    pub fn get_user_profile_photos(&mut self, user_id: Integer,
                                   offset: Option<Integer>,
                                   limit: Option<Integer>)
                                   -> Result<UserProfilePhotos> {
        let mut params = Params::new();
        params.add_get("user_id", user_id);
        params.add_get_opt("offset", offset);
        params.add_get_opt("limit", limit);

        // Execute request
        self.send_request("getUserProfilePhotos", params)
    }

    /// Corresponds to the "getUpdates" method of the API.
    ///
    /// **Note:**
    /// The method will not set the offset parameter on its own. To receive
    /// updates in a more high level way, see `long_poll`.
    pub fn get_updates(&mut self, offset: Option<Integer>,
                       limit: Option<Integer>, timeout: Option<Integer>)
                       -> Result<Vec<Update>> {
        // Prepare parameters
        let mut params = Params::new();
        params.add_get_opt("offset", offset);
        params.add_get_opt("limit", limit);
        params.add_get_opt("timeout", timeout);

        // Execute request
        self.send_request("getUpdates", params)
    }

    pub fn set_webhook<U: IntoUrl>(&mut self, url: Option<U>) -> Result<bool> {
        let u = url.map_or("".into(), |u| u.into_url().unwrap().to_string());

        // Prepare parameters
        let mut params = Params::new();
        params.add_get("url", u);

        // Execute request
        self.send_request("setWebhook", params)
    }

    // =======================================================================
    // Methods for receiving updates
    // =======================================================================
    /// Receive and handle updates via "getUpdates".
    ///
    /// This method will repeatedly call `get_updates` to receive new updates.
    /// It will then call the given handler for every update and increase the
    /// update offset accordingly, so the handler will never be called with
    /// the same update twice.
    /// The `timeout` parameter influences how long (in seconds) each poll may
    /// last. Defaults to 30.
    /// The handler gets a mutable reference to the bot since borrowing it
    /// from the outer scope won't work. When the handler returns an `Err`
    /// value the bot will stop listening for updates and `long_poll` will
    /// return the Error. If you want to stop listening you can just return
    /// `Error::UserInterrupt`.
    ///
    /// **Note:**
    /// If the bot is restarted, but the last received updates are not yet
    /// confirmed (the last poll was not empty), there will be some duplicate
    /// updates.
    // pub fn long_poll<H>(&mut self, timeout: Option<Integer>, mut handler: H)
    //                     -> Result<()>
    //                     where H: FnMut(&mut Api, Update) -> Result<()> {
    //     // Calculate final timeout: Given or default (30s)
    //     let timeout = timeout.or(Some(30));

    //     loop {
    //         // Receive updates with correct offset
    //         let offset = Some(self.offset);
    //         let updates = try!(self.get_updates(offset, None, timeout));

    //         // For every update: Increase the offset and call the handler.
    //         for u in updates {
    //             if u.update_id >= self.offset {
    //                 self.offset = u.update_id + 1;
    //             }

    //             try!(handler(self, u));
    //         }
    //     }
    // }

    pub fn listener(&self, method: ListeningMethod) -> Listener {
        Listener {
            method: method,
            confirmed: 0,
            url: self.url.clone(),
            client: Client::new(),
        }
    }

    // =======================================================================
    // Private methods
    // =======================================================================
    fn send_request<T: Decodable>(&mut self, method: &str, p: Params)
        -> Result<T>
    {
        Self::request(&self.client, &self.url, method, p)
    }

    fn request<T: Decodable>(client: &Client, url: &Url,
                             method: &str, params: Params) -> Result<T> {
        // Prepare URL for request: Clone and change the last path fragment
        // to the method name and append GET parameters.
        let mut url = url.clone();
        url.path_mut().map(|path| {         // if theres a path: Change it
            path.last_mut().map(|last| {    // if its not empty: Change last...
                *last = method.into()       // ... into method name
            })
        });

        // For all (str, String) pairs: Map to (str, str) and append it to URL
        let it = params.get_params().into_iter().map(|&(k, ref v)| (k, &**v));
        url.set_query_from_pairs(it);

        // Prepare HTTP Request
        let req = client.get(url).header(Connection::close());

        // Send request and check if it failed
        let mut resp = try!(req.send());

        // Read response into String and return error if it failed
        let mut body = String::new();
        try!(resp.read_to_string(&mut body));

        // Try to decode response as JSON representing a Response
        match try!(json::decode(&body)) {
            // If the response says that there was an error: Return API-Error
            // with the given description.
            Response { ok: false, description: Some(desc), ..} => {
                Err(Error::Api(desc))
            },
            // If response is "ok": Return the result.
            Response { ok: true, result: Some(res), ..} => {
                Ok(res)
            },
            // This should never occur: If "ok"==false, "description" should
            // always be Some. If "ok"==true, then "result" should always be
            // Some. We could also panic in this case.
            _ => Err(Error::InvalidState("Invalid server response".into())),
        }
    }
}

/// Different method how to listen for new updates. Currently `LongPoll` is
/// the only method supported by this library. The Telegram API offers a
/// webhook method which is not yet implemented here.
pub enum ListeningMethod {
    LongPoll(Option<Integer>),
}

/// A listening handler returns this type to signal the listening method either
/// to stop or to continue. If a handler returns `Stop`, the update it was
/// passed counts as "handled" and won't be handled again.
pub enum HandlerResult {
    Continue,
    Stop
}

/// Offers methods to easily receive new updates via the specified method. This
/// should be used instead of calling methods like `get_updates` yourself.
///
/// To create a listener, you first have to create a `Api` object and call
/// `listener` on it. In order to make listening easier in a concurrent
/// environment, the `Listener` object and the `Api` object don't share any
/// internal state. This makes creating a `Listener` a bit more expensive, but
/// it's usually sufficient to create only one `Listener` once.
pub struct Listener {
    method: ListeningMethod,
    confirmed: Integer,
    url: Url,
    client: Client,
}

impl Listener {
    /// Receive and handle updates with the given closure.
    ///
    /// This method will use the specified listening method to receive new
    /// updates and will then call the given handler for every update. Normally
    /// the handler won't ever be called for the same update twice (see Note
    /// below).
    /// When the handler returns an `Err` value, this function will stop
    /// listening and return the same `Err`. If you want to stop listening you
    /// can return `Ok(HandlerResult::Stop)` instead of an `Err` value.
    ///
    /// When returning an `Ok` value, the update that was passed to the handler
    /// is considered handled and won't be passed to a handler again. On the
    /// other hand if an `Err` is returned, the update is not considered handled
    /// so it will be passed to a handler the next time again.
    ///
    /// **Note:**
    /// If you are listening via `LongPoll` method and your handler panics or
    /// the program is aborted in a not natural way (ctrl + c), the  handler
    /// might receive some already handled updates a second time.
    pub fn listen<H>(&mut self, mut handler: H) -> Result<()>
        where H: FnMut(Update) -> Result<HandlerResult>
    {
        match self.method {
            ListeningMethod::LongPoll(timeout) => {
                // `handled_until` will hold the id of the last handled update
                let mut handled_until = self.confirmed;

                // Calculate final timeout: Given or default (30s)
                let timeout = timeout.or(Some(30));

                loop {
                    // Receive updates with correct offset. We don't specify a
                    // limit (Telegram limits to 100 automatically).
                    let mut params = Params::new();
                    params.add_get_opt("offset", Some(handled_until));
                    params.add_get_opt("timeout", timeout);

                    // Execute request
                    let updates : Vec<Update> = try!(Api::request(
                        &self.client, &self.url, "getUpdates", params
                    ));
                    self.confirmed = handled_until;

                    // For every update: Increase the offset & call the handler.
                    for u in updates {
                        let update_id = u.update_id;

                        // Execute the handler and save it's result.
                        let res = handler(u);

                        // If an error was returned: Confirm the update before
                        // (if necessary) and return the given error.
                        if let Err(e) = res {
                            // Send a last request to confirm already handled
                            // updates.
                            let mut params = Params::new();
                            params.add_get("offset", handled_until);
                            params.add_get("timeout", 0);
                            params.add_get("limit", 0);

                            let _ : Result<Vec<Update>> = Api::request(
                                &self.client, &self.url, "getUpdates", params
                            );
                            self.confirmed = handled_until;

                            return Err(e);
                        }

                        // The update is now considered "handled". The
                        // if-condition should always be true.
                        if update_id >= handled_until {
                            handled_until = update_id + 1;
                        }

                        // If an Ok(Stop) was returned, stop listening now with
                        // confirmed update.
                        if let Ok(HandlerResult::Stop) = res {
                            // Send a last request to confirm already handled
                            // updates.
                            let mut params = Params::new();
                            params.add_get("offset", handled_until);
                            params.add_get("timeout", 0);
                            params.add_get("limit", 0);

                            let _ : Result<Vec<Update>> = Api::request(
                                &self.client, &self.url, "getUpdates", params
                            );
                            self.confirmed = handled_until;

                            return Ok(());
                        }
                    }
                }
            }
        }
    }

    /// Consumes `self` and returns a sender-receiver pair. You can receive
    /// new updates through the Receiver. Each update needs to be confirmed
    /// with a `Result<HandlerResult>` before the next update can be handled.
    ///
    /// This means that handling updates isn't done in parallel. The only
    /// advantage of this function over the `listen` function is that you can
    /// ask the receiver, if a new update has arrived. This is useful if you
    /// want to handle different events in one thread. E.g. a remainder bot
    /// gets active on every received message AND on timed events.
    pub fn channel(mut self)
        -> (mpsc::Sender<Result<HandlerResult>>, mpsc::Receiver<Update>)
    {
        // Create channels for sending updates and handle result
        let (update_tx, update_rx) = mpsc::channel();
        let (res_tx, res_rx) = mpsc::channel();

        // Listen for new updates in a new thread. Sadly we cannot easily
        // return the result of `listen`, so we just discard it.
        thread::spawn(move || {
            let _ = self.listen(|u| {
                // Send received update and return if the receiver hung up.
                if let Err(_) = update_tx.send(u) {
                    return Ok(HandlerResult::Stop);
                }

                // Receive handle result. If the channel hung up: Stop.
                res_rx.recv().unwrap_or(Ok(HandlerResult::Stop))
            });
        });

        (res_tx, update_rx)
    }
}
<|endoftext|>


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

//! The time oracle service for Exonum.

#![deny(missing_debug_implementations, missing_docs)]

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

use iron::prelude::*;
use iron::Handler;
use router::Router;

use std::time::SystemTime;

use exonum::blockchain::{Blockchain, Service, ServiceContext, Schema, Transaction, ApiContext};
use exonum::messages::{RawTransaction, Message};
use exonum::encoding::serialize::json::reexport::Value;
use exonum::storage::{Fork, Snapshot, MapIndex, Entry};
use exonum::crypto::PublicKey;
use exonum::encoding;
use exonum::helpers::fabric::{ServiceFactory, Context};
use exonum::api::Api;

/// Time service id.
const SERVICE_ID: u16 = 4;
/// `TxTime` transaction id.
const TX_TIME_ID: u16 = 1;
/// Time service name.
const SERVICE_NAME: &str = "exonum_time";

encoding_struct! {
    /// Time information.
    struct Time {
        const SIZE = 12;

        /// Field that stores `SystemTime`.
        field time:     SystemTime  [00 => 12]
    }
}

/// `Exonum-time` service database schema.
#[derive(Debug)]
pub struct TimeSchema<T> {
    view: T,
}

impl<T: AsRef<Snapshot>> TimeSchema<T> {
    /// Constructs schema for the given `snapshot`.
    pub fn new(view: T) -> Self {
        TimeSchema { view }
    }

    /// Returns the table that stores `Time` struct for every validator.
    pub fn validators_time(&self) -> MapIndex<&Snapshot, PublicKey, Time> {
        MapIndex::new(
            format!("{}.validators_time", SERVICE_NAME),
            self.view.as_ref(),
        )
    }

    /// Returns stored `Time`.
    pub fn time(&self) -> Entry<&Snapshot, Time> {
        Entry::new(format!("{}.time", SERVICE_NAME), self.view.as_ref())
    }
}

impl<'a> TimeSchema<&'a mut Fork> {
    /// Mutable reference to the ['validators_time'][1] index.
    ///
    /// [1]: struct.TimeSchema.html#method.validators_time
    pub fn validators_time_mut(&mut self) -> MapIndex<&mut Fork, PublicKey, Time> {
        MapIndex::new(format!("{}.validators_time", SERVICE_NAME), self.view)
    }

    /// Mutable reference to the ['time'][1] index.
    ///
    /// [1]: struct.TimeSchema.html#method.time
    pub fn time_mut(&mut self) -> Entry<&mut Fork, Time> {
        Entry::new(format!("{}.time", SERVICE_NAME), self.view)
    }
}

message! {
    /// Transaction that is sent by the validator after the commit of the block.
    struct TxTime {
        const TYPE = SERVICE_ID;
        const ID = TX_TIME_ID;
        const SIZE = 44;
        /// Validator's time.
        field time:     SystemTime  [00 => 12]
        /// Validator's public key.
        field pub_key:  &PublicKey  [12 => 44]
    }
}

impl Transaction for TxTime {
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    fn execute(&self, view: &mut Fork) {
        let validator_keys = Schema::new(&view).actual_configuration().validator_keys;

        // The transaction must be signed by the validator.
        let signed = validator_keys.iter().any(|&validator| {
            validator.service_key == *self.pub_key()
        });
        if !signed {
            return;
        }

        let mut schema = TimeSchema::new(view);
        match schema.validators_time().get(self.pub_key()) {
            // The validator time in the storage should be less than in the transaction.
            Some(ref storage_time) if storage_time.time() >= self.time() => {
                return;
            }
            // Write the time for the validator.
            _ => {
                schema.validators_time_mut().put(
                    self.pub_key(),
                    Time::new(self.time()),
                )
            }
        }

        // Find all known times for the validators.
        let mut validators_time: Vec<SystemTime>;
        {
            let idx = schema.validators_time();
            validators_time = idx.iter()
                .filter_map(|pair| {
                    validator_keys
                        .iter()
                        .find(|validator| validator.service_key == pair.0)
                        .map(|_| pair.1.time())
                })
                .collect();
        }

        // The largest number of Byzantine nodes.
        let max_byzantine_nodes = (validator_keys.len() - 1) / 3;
        if validators_time.len() <= 2 * max_byzantine_nodes {
            return;
        }
        // Ordering time from highest to lowest.
        validators_time.sort_by(|a, b| b.cmp(a));

        match schema.time().get() {
            // Selected time should be longer than the time in the storage.
            Some(ref current_time)
                if current_time.time() >= validators_time[max_byzantine_nodes] => {
                return;
            }
            _ => {
                // Change the time in the storage.
                schema.time_mut().set(Time::new(
                    validators_time[max_byzantine_nodes],
                ));
            }
        }
    }
}

/// Implements the node API.
#[derive(Clone)]
struct TimeApi {
    blockchain: Blockchain,
}

encoding_struct! {
    /// Structure for saving validator's public key and last known local time.
    struct ValidatorTime {
        const SIZE = 44;

        /// Validator's public key.
        field public_key:     &PublicKey      [00 => 32]
        /// Validator's time.
        field time:           SystemTime      [32 => 44]
    }
}

/// Shortcut to get data from storage.
impl TimeApi {
    /// Endpoint for getting value of the time that is saved in storage.
    fn get_current_time(&self, _: &mut Request) -> IronResult<Response> {
        let view = self.blockchain.snapshot();
        let schema = TimeSchema::new(&view);
        let current_time = schema.time().get();
        self.ok_response(&serde_json::to_value(current_time).unwrap())
    }

    /// Endpoint for getting time values for all validators.
    fn get_validators_time(&self, _: &mut Request) -> IronResult<Response> {
        let view = self.blockchain.snapshot();
        let schema = TimeSchema::new(&view);
        let idx = schema.validators_time();

        let validators_time: Vec<_> = idx.iter()
            .map(|(public_key, time)| {
                ValidatorTime::new(&public_key, time.time())
            })
            .collect();

        self.ok_response(&serde_json::to_value(validators_time).unwrap())
    }

    fn wire_private(&self, router: &mut Router) {
        let self_ = self.clone();
        let get_validators_time = move |req: &mut Request| self_.get_validators_time(req);
        router.get(
            "/validators_time",
            get_validators_time,
            "get_validators_time",
        );
    }
}

impl Api for TimeApi {
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let get_current_time = move |req: &mut Request| self_.get_current_time(req);
        router.get("/current_time", get_current_time, "get_current_time");
    }
}

/// A helper trait that provides the node with a current time.
pub trait TimeProvider: Send + Sync + ::std::fmt::Debug {
    /// Returns the current time.
    fn current_time(&self) -> SystemTime;
}

#[derive(Debug)]
struct SystemTimeProvider;

impl TimeProvider for SystemTimeProvider {
    fn current_time(&self) -> SystemTime {
        SystemTime::now()
    }
}

/// Define the service.
#[derive(Debug)]
pub struct TimeService {
    /// Current time.
    time: Box<TimeProvider>,
}

impl Default for TimeService {
    fn default() -> TimeService {
        TimeService { time: Box::new(SystemTimeProvider) as Box<TimeProvider> }
    }
}

impl TimeService {
    /// Create a new `TimeService`.
    pub fn new() -> TimeService {
        TimeService::default()
    }

    /// Create a new `TimeService` with time provider `T`.
    pub fn with_provider<T: Into<Box<TimeProvider>>>(time_provider: T) -> TimeService {
        TimeService { time: time_provider.into() }
    }
}

impl Service for TimeService {
    fn service_name(&self) -> &'static str {
        SERVICE_NAME
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        match raw.message_type() {
            TX_TIME_ID => Ok(Box::new(TxTime::from_raw(raw)?)),
            _ => {
                let error =
                    encoding::Error::IncorrectMessageType { message_type: raw.message_type() };
                Err(error)
            }
        }
    }

    fn initialize(&self, _fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Creates transaction after commit of the block.
    fn handle_commit(&self, context: &ServiceContext) {
        // The transaction must be created by the validator.
        if context.validator_id().is_none() {
            return;
        }
        let (pub_key, sec_key) = (*context.public_key(), context.secret_key().clone());
        context
            .transaction_sender()
            .send(Box::new(
                TxTime::new(self.time.current_time(), &pub_key, &sec_key),
            ))
            .unwrap();
    }

    fn private_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = TimeApi { blockchain: ctx.blockchain().clone() };
        api.wire_private(&mut router);
        Some(Box::new(router))
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = TimeApi { blockchain: ctx.blockchain().clone() };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

/// A time service creator for the `NodeBuilder`.
#[derive(Debug)]
pub struct TimeServiceFactory;

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

//! The time oracle service for Exonum.

#![deny(missing_debug_implementations, missing_docs)]

extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

use iron::prelude::*;
use iron::Handler;
use router::Router;

use std::time::SystemTime;

use exonum::blockchain::{Blockchain, Service, ServiceContext, Schema, Transaction, ApiContext};
use exonum::messages::{RawTransaction, Message};
use exonum::encoding::serialize::json::reexport::Value;
use exonum::storage::{Fork, Snapshot, MapIndex, Entry};
use exonum::crypto::PublicKey;
use exonum::encoding;
use exonum::helpers::fabric::{ServiceFactory, Context};
use exonum::api::Api;

/// Time service id.
const SERVICE_ID: u16 = 4;
/// `TxTime` transaction id.
const TX_TIME_ID: u16 = 1;
/// Time service name.
const SERVICE_NAME: &str = "exonum_time";

encoding_struct! {
    /// Time information.
    struct Time {
        const SIZE = 12;

        /// Field that stores `SystemTime`.
        field time:     SystemTime  [00 => 12]
    }
}

/// `Exonum-time` service database schema.
#[derive(Debug)]
pub struct TimeSchema<T> {
    view: T,
}

impl<T: AsRef<Snapshot>> TimeSchema<T> {
    /// Constructs schema for the given `snapshot`.
    pub fn new(view: T) -> Self {
        TimeSchema { view }
    }

    /// Returns the table that stores `Time` struct for every validator.
    pub fn validators_time(&self) -> MapIndex<&Snapshot, PublicKey, Time> {
        MapIndex::new(
            format!("{}.validators_time", SERVICE_NAME),
            self.view.as_ref(),
        )
    }

    /// Returns stored `Time`.
    pub fn time(&self) -> Entry<&Snapshot, Time> {
        Entry::new(format!("{}.time", SERVICE_NAME), self.view.as_ref())
    }
}

impl<'a> TimeSchema<&'a mut Fork> {
    /// Mutable reference to the ['validators_time'][1] index.
    ///
    /// [1]: struct.TimeSchema.html#method.validators_time
    pub fn validators_time_mut(&mut self) -> MapIndex<&mut Fork, PublicKey, Time> {
        MapIndex::new(format!("{}.validators_time", SERVICE_NAME), self.view)
    }

    /// Mutable reference to the ['time'][1] index.
    ///
    /// [1]: struct.TimeSchema.html#method.time
    pub fn time_mut(&mut self) -> Entry<&mut Fork, Time> {
        Entry::new(format!("{}.time", SERVICE_NAME), self.view)
    }
}

message! {
    /// Transaction that is sent by the validator after the commit of the block.
    struct TxTime {
        const TYPE = SERVICE_ID;
        const ID = TX_TIME_ID;
        const SIZE = 44;
        /// Validator's time.
        field time:     SystemTime  [00 => 12]
        /// Validator's public key.
        field pub_key:  &PublicKey  [12 => 44]
    }
}

impl Transaction for TxTime {
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    fn execute(&self, view: &mut Fork) {
        let validator_keys = Schema::new(&view).actual_configuration().validator_keys;

        // The transaction must be signed by the validator.
        let signed = validator_keys.iter().any(|&validator| {
            validator.service_key == *self.pub_key()
        });
        if !signed {
            return;
        }

        let mut schema = TimeSchema::new(view);
        match schema.validators_time().get(self.pub_key()) {
            // The validator time in the storage should be less than in the transaction.
            Some(ref storage_time) if storage_time.time() >= self.time() => {
                return;
            }
            // Write the time for the validator.
            _ => {
                schema.validators_time_mut().put(
                    self.pub_key(),
                    Time::new(self.time()),
                )
            }
        }

        // Find all known times for the validators.
        let mut validators_time: Vec<SystemTime>;
        {
            let idx = schema.validators_time();
            validators_time = idx.iter()
                .filter_map(|pair| {
                    validator_keys
                        .iter()
                        .find(|validator| validator.service_key == pair.0)
                        .map(|_| pair.1.time())
                })
                .collect();
        }

        // The largest number of Byzantine nodes.
        let max_byzantine_nodes = (validator_keys.len() - 1) / 3;
        if validators_time.len() <= 2 * max_byzantine_nodes {
            return;
        }
        // Ordering time from highest to lowest.
        validators_time.sort_by(|a, b| b.cmp(a));

        match schema.time().get() {
            // Selected time should be longer than the time in the storage.
            Some(ref current_time)
                if current_time.time() >= validators_time[max_byzantine_nodes] => {
                return;
            }
            _ => {
                // Change the time in the storage.
                schema.time_mut().set(Time::new(
                    validators_time[max_byzantine_nodes],
                ));
            }
        }
    }
}

/// Implements the node API.
#[derive(Clone)]
struct TimeApi {
    blockchain: Blockchain,
}

/// Structure for saving validator's public key and last known local time.
#[derive(Serialize)]
struct ValidatorTime {
    /// Validator's public key.
    public_key: PublicKey,
    /// Validator's time.
    time: SystemTime,
}

/// Shortcut to get data from storage.
impl TimeApi {
    /// Endpoint for getting value of the time that is saved in storage.
    fn get_current_time(&self, _: &mut Request) -> IronResult<Response> {
        let view = self.blockchain.snapshot();
        let schema = TimeSchema::new(&view);
        let current_time = schema.time().get();
        self.ok_response(&serde_json::to_value(current_time).unwrap())
    }

    /// Endpoint for getting time values for all validators.
    fn get_validators_time(&self, _: &mut Request) -> IronResult<Response> {
        let view = self.blockchain.snapshot();
        let schema = TimeSchema::new(&view);
        let idx = schema.validators_time();

        let validators_time: Vec<_> = idx.iter()
            .map(|(public_key, time)| {
                ValidatorTime {
                    public_key,
                    time: time.time(),
                }
            })
            .collect();

        self.ok_response(&serde_json::to_value(validators_time).unwrap())
    }

    fn wire_private(&self, router: &mut Router) {
        let self_ = self.clone();
        let get_validators_time = move |req: &mut Request| self_.get_validators_time(req);
        router.get(
            "/validators_time",
            get_validators_time,
            "get_validators_time",
        );
    }
}

impl Api for TimeApi {
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let get_current_time = move |req: &mut Request| self_.get_current_time(req);
        router.get("/current_time", get_current_time, "get_current_time");
    }
}

/// A helper trait that provides the node with a current time.
pub trait TimeProvider: Send + Sync + ::std::fmt::Debug {
    /// Returns the current time.
    fn current_time(&self) -> SystemTime;
}

#[derive(Debug)]
struct SystemTimeProvider;

impl TimeProvider for SystemTimeProvider {
    fn current_time(&self) -> SystemTime {
        SystemTime::now()
    }
}

/// Define the service.
#[derive(Debug)]
pub struct TimeService {
    /// Current time.
    time: Box<TimeProvider>,
}

impl Default for TimeService {
    fn default() -> TimeService {
        TimeService { time: Box::new(SystemTimeProvider) as Box<TimeProvider> }
    }
}

impl TimeService {
    /// Create a new `TimeService`.
    pub fn new() -> TimeService {
        TimeService::default()
    }

    /// Create a new `TimeService` with time provider `T`.
    pub fn with_provider<T: Into<Box<TimeProvider>>>(time_provider: T) -> TimeService {
        TimeService { time: time_provider.into() }
    }
}

impl Service for TimeService {
    fn service_name(&self) -> &'static str {
        SERVICE_NAME
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        match raw.message_type() {
            TX_TIME_ID => Ok(Box::new(TxTime::from_raw(raw)?)),
            _ => {
                let error =
                    encoding::Error::IncorrectMessageType { message_type: raw.message_type() };
                Err(error)
            }
        }
    }

    fn initialize(&self, _fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Creates transaction after commit of the block.
    fn handle_commit(&self, context: &ServiceContext) {
        // The transaction must be created by the validator.
        if context.validator_id().is_none() {
            return;
        }
        let (pub_key, sec_key) = (*context.public_key(), context.secret_key().clone());
        context
            .transaction_sender()
            .send(Box::new(
                TxTime::new(self.time.current_time(), &pub_key, &sec_key),
            ))
            .unwrap();
    }

    fn private_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = TimeApi { blockchain: ctx.blockchain().clone() };
        api.wire_private(&mut router);
        Some(Box::new(router))
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = TimeApi { blockchain: ctx.blockchain().clone() };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

/// A time service creator for the `NodeBuilder`.
#[derive(Debug)]
pub struct TimeServiceFactory;

impl ServiceFactory for TimeServiceFactory {
    fn make_service(&mut self, _: &Context) -> Box<Service> {
        Box::new(TimeService::new())
    }
}
<|endoftext|>


<commit_before>//! The goal of this crate is to provide Rust bindings to the Web APIs and to allow
//! a high degree of interoperability between Rust and JavaScript.
//!
//! ## Examples
//!
//! You can directly embed JavaScript code into Rust:
//!
//! ```rust
//! let message = "Hello, 世界!";
//! let result = js! {
//!     alert( @{message} );
//!     return 2 + 2 * 2;
//! };
//!
//! println!( "2 + 2 * 2 = {:?}", result );
//! ```
//!
//! Even closures are supported:
//!
//! ```rust
//! let print_hello = |name: String| {
//!     println!( "Hello, {}!", name );
//! };
//!
//! js! {
//!     var print_hello = @{print_hello};
//!     print_hello( "Bob" );
//!     print_hello.drop(); // Necessary to clean up the closure on Rust's side.
//! }
//! ```
//!
//! You can also pass arbitrary structures thanks to [serde]:
//!
//! ```rust
//! #[derive(Serialize)]
//! struct Person {
//!     name: String,
//!     age: i32
//! }
//!
//! js_serializable!( Person );
//!
//! js! {
//!     var person = @{person};
//!     console.log( person.name + " is " + person.age + " years old." );
//! };
//! ```
//!
//! [serde]: https://serde.rs/
//!
//! This crate also exposes a number of Web APIs, for example:
//!
//! ```rust
//! let button = document().query_selector( "#hide-button" ).unwrap();
//! button.add_event_listener( move |_: ClickEvent| {
//!     for anchor in document().query_selector_all( "#main a" ) {
//!         js!( @{anchor}.style = "display: none;"; );
//!     }
//! });
//! ```

#![deny(
    missing_docs,
    missing_debug_implementations,
    trivial_numeric_casts,
    unused_import_braces,
    unused_qualifications
)]
#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]
#![cfg_attr(not(feature = "nightly"), deny(unstable_features))]
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
#![recursion_limit="1500"]

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde as serde_crate;

#[cfg(any(test, feature = "serde_json"))]
extern crate serde_json;

#[cfg(all(test, feature = "serde"))]
#[macro_use]
extern crate serde_derive;

#[macro_use]
mod webcore;
mod webapi;
mod ecosystem;

pub use webcore::initialization::{
    initialize,
    event_loop
};
pub use webcore::value::{
    Undefined,
    Null,
    Value,
    Reference
};
pub use webcore::number::Number;
pub use webcore::object::Object;
pub use webcore::array::Array;

pub use webcore::unsafe_typed_array::UnsafeTypedArray;
pub use webcore::once::Once;

#[cfg(feature = "serde")]
/// A module with serde-related APIs.
pub mod serde {
    pub use ecosystem::serde::{
        ConversionError,
        Serde
    };
}

/// A module with bindings to the Web APIs.
pub mod web {
    pub use webapi::window::{
        Window,
        window
    };
    pub use webapi::document::{
        Document,
        document
    };
    pub use webapi::global::{
        set_timeout,
        alert
    };
    pub use webapi::cross_origin_setting::CrossOriginSetting;
    pub use webapi::date::Date;
    pub use webapi::event_target::{IEventTarget, EventTarget, EventListenerHandle};
    pub use webapi::window::RequestAnimationFrameHandle;
    pub use webapi::node::{INode, Node, CloneKind};
    pub use webapi::element::{IElement, Element};
    pub use webapi::text_node::TextNode;
    pub use webapi::html_element::{IHtmlElement, HtmlElement};
    pub use webapi::window_or_worker::IWindowOrWorker;
    pub use webapi::token_list::TokenList;
    pub use webapi::node_list::NodeList;
    pub use webapi::string_map::StringMap;
    pub use webapi::storage::Storage;
    pub use webapi::location::Location;
    pub use webapi::array_buffer::ArrayBuffer;
    pub use webapi::typed_array::TypedArray;
    pub use webapi::file_reader::{FileReader, FileReaderResult};
    pub use webapi::history::History;

    /// A module containing XMLHttpRequest and its ReadyState
    pub mod xml_http_request {
        pub use webapi::xml_http_request::{ XMLHttpRequest, ReadyState };
    }
    /// A module containing error types.
    pub mod error {
        pub use webapi::node::NotFoundError;
    }

    /// A module containing HTML DOM elements.
    pub mod html_element {
        pub use webapi::html_elements::ImageElement;
        pub use webapi::html_elements::InputElement;
        pub use webapi::html_elements::TextAreaElement;
    }

    /// A module containing JavaScript DOM events.
    pub mod event {
        pub use webapi::event::{
            ConcreteEvent,

            IEvent,
            IKeyboardEvent,
            IUiEvent,
            IMouseEvent,
            IFocusEvent,
            IProgressEvent,

            EventPhase,
            KeyboardLocation,
            ModifierKey,
            MouseButton,

            ChangeEvent,
            KeypressEvent,
            KeydownEvent,
            KeyupEvent,
            ClickEvent,
            DoubleClickEvent,
            FocusEvent,
            BlurEvent,
            HashChangeEvent,
            ResourceLoadEvent,
            ResourceAbortEvent,
            ResourceErrorEvent,
            ProgressEvent,
            LoadStartEvent,
            LoadEndEvent,
            ProgressLoadEvent,
            ProgressAbortEvent,
            ProgressErrorEvent,
            InputEvent,
            ReadyStateChange,
            PopStateEvent,
        };
    }
}

/// A module containing stable counterparts to currently
/// unstable Rust features.
pub mod unstable {
    pub use webcore::try_from::{
        TryFrom,
        TryInto
    };

    pub use webcore::void::Void;
}

#[doc(hidden)]
pub mod private {
    pub use webcore::ffi::exports::*;
    pub use webcore::serialization::{
        JsSerializable,
        JsSerializableOwned,
        PreallocatedArena,
        SerializedValue
    };

    pub use webcore::newtype::{
        IntoNewtype,
        Newtype
    };

    pub use webcore::value::{
        FromReference,
        FromReferenceUnchecked
    };

    #[cfg(feature = "serde")]
    pub use ecosystem::serde::{
        to_value,
        from_value
    };

    // This is to prevent an unused_mut warnings in macros, because an `allow` doesn't work apparently?
    #[allow(dead_code)]
    #[inline(always)]
    pub fn noop< T >( _: &mut T ) {}
}
<commit_msg>Actually export mouse{down,up,move} events<commit_after>//! The goal of this crate is to provide Rust bindings to the Web APIs and to allow
//! a high degree of interoperability between Rust and JavaScript.
//!
//! ## Examples
//!
//! You can directly embed JavaScript code into Rust:
//!
//! ```rust
//! let message = "Hello, 世界!";
//! let result = js! {
//!     alert( @{message} );
//!     return 2 + 2 * 2;
//! };
//!
//! println!( "2 + 2 * 2 = {:?}", result );
//! ```
//!
//! Even closures are supported:
//!
//! ```rust
//! let print_hello = |name: String| {
//!     println!( "Hello, {}!", name );
//! };
//!
//! js! {
//!     var print_hello = @{print_hello};
//!     print_hello( "Bob" );
//!     print_hello.drop(); // Necessary to clean up the closure on Rust's side.
//! }
//! ```
//!
//! You can also pass arbitrary structures thanks to [serde]:
//!
//! ```rust
//! #[derive(Serialize)]
//! struct Person {
//!     name: String,
//!     age: i32
//! }
//!
//! js_serializable!( Person );
//!
//! js! {
//!     var person = @{person};
//!     console.log( person.name + " is " + person.age + " years old." );
//! };
//! ```
//!
//! [serde]: https://serde.rs/
//!
//! This crate also exposes a number of Web APIs, for example:
//!
//! ```rust
//! let button = document().query_selector( "#hide-button" ).unwrap();
//! button.add_event_listener( move |_: ClickEvent| {
//!     for anchor in document().query_selector_all( "#main a" ) {
//!         js!( @{anchor}.style = "display: none;"; );
//!     }
//! });
//! ```

#![deny(
    missing_docs,
    missing_debug_implementations,
    trivial_numeric_casts,
    unused_import_braces,
    unused_qualifications
)]
#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]
#![cfg_attr(not(feature = "nightly"), deny(unstable_features))]
#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
#![recursion_limit="1500"]

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde as serde_crate;

#[cfg(any(test, feature = "serde_json"))]
extern crate serde_json;

#[cfg(all(test, feature = "serde"))]
#[macro_use]
extern crate serde_derive;

#[macro_use]
mod webcore;
mod webapi;
mod ecosystem;

pub use webcore::initialization::{
    initialize,
    event_loop
};
pub use webcore::value::{
    Undefined,
    Null,
    Value,
    Reference
};
pub use webcore::number::Number;
pub use webcore::object::Object;
pub use webcore::array::Array;

pub use webcore::unsafe_typed_array::UnsafeTypedArray;
pub use webcore::once::Once;

#[cfg(feature = "serde")]
/// A module with serde-related APIs.
pub mod serde {
    pub use ecosystem::serde::{
        ConversionError,
        Serde
    };
}

/// A module with bindings to the Web APIs.
pub mod web {
    pub use webapi::window::{
        Window,
        window
    };
    pub use webapi::document::{
        Document,
        document
    };
    pub use webapi::global::{
        set_timeout,
        alert
    };
    pub use webapi::cross_origin_setting::CrossOriginSetting;
    pub use webapi::date::Date;
    pub use webapi::event_target::{IEventTarget, EventTarget, EventListenerHandle};
    pub use webapi::window::RequestAnimationFrameHandle;
    pub use webapi::node::{INode, Node, CloneKind};
    pub use webapi::element::{IElement, Element};
    pub use webapi::text_node::TextNode;
    pub use webapi::html_element::{IHtmlElement, HtmlElement};
    pub use webapi::window_or_worker::IWindowOrWorker;
    pub use webapi::token_list::TokenList;
    pub use webapi::node_list::NodeList;
    pub use webapi::string_map::StringMap;
    pub use webapi::storage::Storage;
    pub use webapi::location::Location;
    pub use webapi::array_buffer::ArrayBuffer;
    pub use webapi::typed_array::TypedArray;
    pub use webapi::file_reader::{FileReader, FileReaderResult};
    pub use webapi::history::History;

    /// A module containing XMLHttpRequest and its ReadyState
    pub mod xml_http_request {
        pub use webapi::xml_http_request::{ XMLHttpRequest, ReadyState };
    }
    /// A module containing error types.
    pub mod error {
        pub use webapi::node::NotFoundError;
    }

    /// A module containing HTML DOM elements.
    pub mod html_element {
        pub use webapi::html_elements::ImageElement;
        pub use webapi::html_elements::InputElement;
        pub use webapi::html_elements::TextAreaElement;
    }

    /// A module containing JavaScript DOM events.
    pub mod event {
        pub use webapi::event::{
            ConcreteEvent,

            IEvent,
            IKeyboardEvent,
            IUiEvent,
            IMouseEvent,
            IFocusEvent,
            IProgressEvent,

            EventPhase,
            KeyboardLocation,
            ModifierKey,
            MouseButton,

            ChangeEvent,
            KeypressEvent,
            KeydownEvent,
            KeyupEvent,
            ClickEvent,
            DoubleClickEvent,
            MouseDownEvent,
            MouseUpEvent,
            MouseMoveEvent,
            FocusEvent,
            BlurEvent,
            HashChangeEvent,
            ResourceLoadEvent,
            ResourceAbortEvent,
            ResourceErrorEvent,
            ProgressEvent,
            LoadStartEvent,
            LoadEndEvent,
            ProgressLoadEvent,
            ProgressAbortEvent,
            ProgressErrorEvent,
            InputEvent,
            ReadyStateChange,
            PopStateEvent,
        };
    }
}

/// A module containing stable counterparts to currently
/// unstable Rust features.
pub mod unstable {
    pub use webcore::try_from::{
        TryFrom,
        TryInto
    };

    pub use webcore::void::Void;
}

#[doc(hidden)]
pub mod private {
    pub use webcore::ffi::exports::*;
    pub use webcore::serialization::{
        JsSerializable,
        JsSerializableOwned,
        PreallocatedArena,
        SerializedValue
    };

    pub use webcore::newtype::{
        IntoNewtype,
        Newtype
    };

    pub use webcore::value::{
        FromReference,
        FromReferenceUnchecked
    };

    #[cfg(feature = "serde")]
    pub use ecosystem::serde::{
        to_value,
        from_value
    };

    // This is to prevent an unused_mut warnings in macros, because an `allow` doesn't work apparently?
    #[allow(dead_code)]
    #[inline(always)]
    pub fn noop< T >( _: &mut T ) {}
}
<|endoftext|>


<commit_before>//! Basic character set detection for XML and HTML in Rust.
//!
//! ## Example
//!
//! ```rust
//! use std::io::Cursor;
//! extern crate xhtmlchardet;
//!
//! let text = b"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><channel><title>Example</title></channel>";
//! let mut text_cursor = Cursor::new(text);
//! let detected_charsets: Vec<String> = xhtmlchardet::detect(&mut text_cursor, None).unwrap();
//! assert_eq!(detected_charsets, vec!["iso-8859-1".to_string()]);
//! ```

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

#[derive(Debug)]
struct Bom(u8, u8, u8, u8);

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Flavour {
    UCS,
    UTF,
    EBCDIC,
    ASCII,
    Unknown,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum ByteOrder {
    BigEndian,
    LittleEndian,
    Unusual2143,
    Unusual3412,
    NotApplicable,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Width {
    EightBit = 8,
    SixteenBit = 16,
    ThirtyTwoBit = 32,
}

#[derive(Clone, Debug, PartialEq, Eq)]
struct Descriptor(Flavour, Width, ByteOrder);

// 32-Bit Encodings
const UCS_4_BE: Descriptor = Descriptor(Flavour::UCS, Width::ThirtyTwoBit, ByteOrder::BigEndian);
const UCS_4_LE: Descriptor = Descriptor(Flavour::UCS, Width::ThirtyTwoBit, ByteOrder::LittleEndian);
const UCS_4_2143: Descriptor =
    Descriptor(Flavour::UCS, Width::ThirtyTwoBit, ByteOrder::Unusual2143);
const UCS_4_3412: Descriptor =
    Descriptor(Flavour::UCS, Width::ThirtyTwoBit, ByteOrder::Unusual3412);

// 16-Bit Encodings
const UTF_16_BE: Descriptor = Descriptor(Flavour::UTF, Width::SixteenBit, ByteOrder::BigEndian);
const UTF_16_LE: Descriptor = Descriptor(Flavour::UTF, Width::SixteenBit, ByteOrder::LittleEndian);

const UTF_8: Descriptor = Descriptor(Flavour::UTF, Width::EightBit, ByteOrder::NotApplicable);
const EBCDIC: Descriptor = Descriptor(Flavour::EBCDIC, Width::EightBit, ByteOrder::NotApplicable);

// ASCII compatible encodings
const ASCII_32BIT_BE: Descriptor =
    Descriptor(Flavour::Unknown, Width::ThirtyTwoBit, ByteOrder::BigEndian);
const ASCII_32BIT_LE: Descriptor = Descriptor(
    Flavour::Unknown,
    Width::ThirtyTwoBit,
    ByteOrder::LittleEndian,
);
const ASCII_16BIT_BE: Descriptor =
    Descriptor(Flavour::Unknown, Width::SixteenBit, ByteOrder::BigEndian);
const ASCII_16BIT_LE: Descriptor =
    Descriptor(Flavour::Unknown, Width::SixteenBit, ByteOrder::LittleEndian);
const ASCII_8BIT: Descriptor =
    Descriptor(Flavour::ASCII, Width::EightBit, ByteOrder::NotApplicable);

/// Attempt to detect the character set of the supplied byte stream.
///
/// `reader` is expected to be positioned at the start of the stream. `detect` will read up to 512
/// bytes in order to determine the encoding.
///
/// The optional `hint` is a possible encoding name for the text that may have been received
/// externally to the text itself, such as from HTTP header.
///
/// ### Example
///
/// ```
/// use std::io::Cursor;
/// extern crate xhtmlchardet;
///
/// let text = b"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><channel><title>Example</title></channel>";
/// let mut text_cursor = Cursor::new(text.to_vec());
/// let detected_charsets = xhtmlchardet::detect(&mut text_cursor, None);
/// assert_eq!(detected_charsets.unwrap_or(vec![]), vec!["iso-8859-1".to_string()]);
/// ```
pub fn detect<R: Read>(reader: &mut R, hint: Option<String>) -> Result<Vec<String>, io::Error> {
    // Read the first 4 bytes and see if they help
    let mut first_four_bytes = [0u8; 4];
    reader.read_exact(&mut first_four_bytes)?;

    let bom = Bom(
        first_four_bytes[0],
        first_four_bytes[1],
        first_four_bytes[2],
        first_four_bytes[3],
    );

    let possible_encoding = detect_byte_order_mark(&bom);

    // Now that byte size may have been determined try reading the first 512ish bytes to read an
    // encoding declaration
    let mut buf = [0u8; 512];
    loop {
        match reader.read(&mut buf) {
            Ok(0) => return Ok(Vec::new()), // eof
            Ok(_n) => break,
            Err(err) if err.kind() == io::ErrorKind::Interrupted => {} // retry
            Err(err) => return Err(err),
        };
    }

    let mut candidates = Vec::with_capacity(3);

    // Look for encoding="", charset="?"?
    search("encoding=", &buf, possible_encoding.as_ref())
        .or_else(|| search("charset=", &buf, possible_encoding.as_ref()))
        .map(normalise)
        .map(|encoding| {
            push_if_not_contains(
                &mut candidates,
                endianify(&encoding, possible_encoding.as_ref()),
            )
        });

    // Consider hint
    hint.map(normalise).map(|encoding| {
        push_if_not_contains(
            &mut candidates,
            endianify(&encoding, possible_encoding.as_ref()),
        )
    });

    // Include info from BOM detection
    match possible_encoding {
        Some(UCS_4_LE) => Some("ucs-4le"),
        Some(UCS_4_BE) => Some("ucs-4be"),
        Some(UTF_16_LE) => Some("utf-16le"),
        Some(UTF_16_BE) => Some("utf-16be"),
        Some(Descriptor(Flavour::UTF, Width::EightBit, _)) => Some("utf-8"),
        Some(EBCDIC) => Some("ebcdic"),
        _ => None,
    }
    .map(|encoding| push_if_not_contains(&mut candidates, encoding.to_string()));

    // Otherwise test if UTF-8
    if candidates.is_empty() && std::str::from_utf8(&buf).is_ok() {
        candidates.push("utf-8".to_string());
    }

    Ok(candidates)
}

fn detect_byte_order_mark(bom: &Bom) -> Option<Descriptor> {
    // Can do below without the Bom type if slice pattern syntax becomes non-experimental
    // let possible = match first_four_bytes {
    //     // With Byte Order Mark
    //     [0x00, 0x00, 0xFE, 0xFF]             => Some("UCS-4BE"),
    //     [0xFF, 0xFE, 0x00, 0x00]             => Some("UCS-4LE"),
    //     [0x00, 0x00, 0xFF, 0xFE]             => Some("UCS-4OE"),
    //     [0xFE, 0xFF, 0x00, 0x00]             => Some("UCS-4EO"),
    //     [0xFE, 0xFF, c, d] if c > 0 && d > 0 => Some("UTF-16BE"),
    //     [0xFF, 0xFE, c, d] if c > 0 && d > 0 => Some("UCS-16LE"),
    //     [0xEF, 0xBB, 0xBF, _   ]             => Some("UTF-8"),

    //     //  Without Byte Order Mark
    //     [0x00, 0x00, 0x00, 0x3C] |
    //     [0x3C, 0x00, 0x00, 0x00] |
    //     [0x00, 0x00, 0x3C, 0x00] |
    //     [0x00, 0x3C, 0x00, 0x00]             => Some("32-bit"),
    //     [0x00, 0x3C, 0x00, 0x3F]             => Some("16-bit Big Endian"),
    //     [0x3C, 0x00, 0x3F, 0x00]             => Some("16-bit Little Endian"),
    //     [0x3C, 0x3F, 0x78, 0x6D]             => Some("8-bit"),
    //     [0x4C, 0x6F, 0xA7, 0x94]             => Some("EBCDIC"),
    //     // This may be UTF-8 without an encoding declaration as this is not required
    //     // for UTF-8
    //     _                                    => Some("Other"),
    // };

    // http://www.w3.org/TR/2004/REC-xml-20040204/#sec-guessing-no-ext-info
    match *bom {
        // With Byte Order Mark
        Bom(0x00, 0x00, 0xFE, 0xFF) => Some(UCS_4_BE),
        Bom(0xFF, 0xFE, 0x00, 0x00) => Some(UCS_4_LE),
        Bom(0x00, 0x00, 0xFF, 0xFE) => Some(UCS_4_2143),
        Bom(0xFE, 0xFF, 0x00, 0x00) => Some(UCS_4_3412),
        Bom(0xFE, 0xFF, c, d) if c > 0 || d > 0 => Some(UTF_16_BE),
        Bom(0xFF, 0xFE, c, d) if c > 0 || d > 0 => Some(UTF_16_LE),
        Bom(0xEF, 0xBB, 0xBF, _) => Some(UTF_8),

        //  Without Byte Order Mark
        Bom(0x00, 0x00, 0x00, 0x3C) => Some(ASCII_32BIT_BE),
        Bom(0x3C, 0x00, 0x00, 0x00) => Some(ASCII_32BIT_LE),
        Bom(0x00, 0x00, 0x3C, 0x00) => Some(Descriptor(
            Flavour::Unknown,
            Width::ThirtyTwoBit,
            ByteOrder::Unusual2143,
        )),
        Bom(0x00, 0x3C, 0x00, 0x00) => Some(Descriptor(
            Flavour::Unknown,
            Width::ThirtyTwoBit,
            ByteOrder::Unusual3412,
        )),
        Bom(0x00, 0x3C, 0x00, 0x3F) => Some(ASCII_16BIT_BE),
        Bom(0x3C, 0x00, 0x3F, 0x00) => Some(ASCII_16BIT_LE),
        Bom(0x3C, 0x3F, 0x78, 0x6D) => Some(ASCII_8BIT),
        Bom(0x4C, 0x6F, 0xA7, 0x94) => Some(EBCDIC),
        // This may be UTF-8 without an encoding declaration as this is not required
        // for UTF-8
        _ => None,
    }
}

fn normalise<S: AsRef<str>>(encoding: S) -> String {
    encoding
        .as_ref()
        .to_lowercase()
        .replace("us-ascii", "ascii")
        .replace("utf8", "utf-8")
        .replace("shift-jis", "shift_jis")
}

fn push_if_not_contains<T: PartialEq>(vec: &mut Vec<T>, item: T) {
    if !vec.contains(&item) {
        vec.push(item);
    }
}

fn endianify(encoding: &str, descriptor: Option<&Descriptor>) -> String {
    let ascii = ASCII_8BIT;
    let &Descriptor(_, _, ref order) = descriptor.unwrap_or(&ascii);

    match encoding {
        "utf-16" => match *order {
            ByteOrder::LittleEndian => "utf-16le".to_string(),
            ByteOrder::BigEndian => "utf-16be".to_string(),
            _ => encoding.to_string(),
        },
        _ => encoding.to_string(),
    }
}

fn search(needle: &str, haystack: &[u8], descriptor: Option<&Descriptor>) -> Option<String> {
    let ascii = ASCII_8BIT;
    let &Descriptor(_, ref width, ref order) = descriptor.unwrap_or(&ascii);
    let chunk_size = (*width as usize) / 8;

    let mut index = match *order {
        ByteOrder::NotApplicable | ByteOrder::LittleEndian => 0,
        ByteOrder::BigEndian => chunk_size - 1,
        ByteOrder::Unusual2143 => 2,
        ByteOrder::Unusual3412 => 1,
    };

    let mut ascii_bytes = Vec::with_capacity(haystack.len() / chunk_size);
    while index < haystack.len() {
        ascii_bytes.push(haystack[index]);
        index += chunk_size;
    }

    let ascii_haystack = String::from_utf8_lossy(&ascii_bytes);

    ascii_haystack.find(needle).map(|pos| {
        // Skip to the matching byte + length of the needle
        ascii_haystack[pos + needle.len()..]
            .chars()
            .skip_while(|char| *char == '"' || *char == '\'')
            .take_while(|char| *char != '"' && *char != '\'')
            .collect()
    })
}

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

    #[test]
    fn test_detect_empty() {
        let mut text_cursor = Cursor::new("");
        let detected_charsets = detect(&mut text_cursor, None);
        assert!(detected_charsets.is_err()); // UnexpectedEof
    }

    #[test]
    fn test_detect_4_bytes() {
        let mut text_cursor = Cursor::new("1234");
        let detected_charsets = detect(&mut text_cursor, None).unwrap();
        assert!(detected_charsets.is_empty());
    }
}
<commit_msg>Fix build on 1.24.0<commit_after>//! Basic character set detection for XML and HTML in Rust.
//!
//! ## Example
//!
//! ```rust
//! use std::io::Cursor;
//! extern crate xhtmlchardet;
//!
//! let text: &[u8] = b"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><channel><title>Example</title></channel>";
//! let mut text_cursor = Cursor::new(text);
//! let detected_charsets: Vec<String> = xhtmlchardet::detect(&mut text_cursor, None).unwrap();
//! assert_eq!(detected_charsets, vec!["iso-8859-1".to_string()]);
//! ```

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

#[derive(Debug)]
struct Bom(u8, u8, u8, u8);

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Flavour {
    UCS,
    UTF,
    EBCDIC,
    ASCII,
    Unknown,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum ByteOrder {
    BigEndian,
    LittleEndian,
    Unusual2143,
    Unusual3412,
    NotApplicable,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Width {
    EightBit = 8,
    SixteenBit = 16,
    ThirtyTwoBit = 32,
}

#[derive(Clone, Debug, PartialEq, Eq)]
struct Descriptor(Flavour, Width, ByteOrder);

// 32-Bit Encodings
const UCS_4_BE: Descriptor = Descriptor(Flavour::UCS, Width::ThirtyTwoBit, ByteOrder::BigEndian);
const UCS_4_LE: Descriptor = Descriptor(Flavour::UCS, Width::ThirtyTwoBit, ByteOrder::LittleEndian);
const UCS_4_2143: Descriptor =
    Descriptor(Flavour::UCS, Width::ThirtyTwoBit, ByteOrder::Unusual2143);
const UCS_4_3412: Descriptor =
    Descriptor(Flavour::UCS, Width::ThirtyTwoBit, ByteOrder::Unusual3412);

// 16-Bit Encodings
const UTF_16_BE: Descriptor = Descriptor(Flavour::UTF, Width::SixteenBit, ByteOrder::BigEndian);
const UTF_16_LE: Descriptor = Descriptor(Flavour::UTF, Width::SixteenBit, ByteOrder::LittleEndian);

const UTF_8: Descriptor = Descriptor(Flavour::UTF, Width::EightBit, ByteOrder::NotApplicable);
const EBCDIC: Descriptor = Descriptor(Flavour::EBCDIC, Width::EightBit, ByteOrder::NotApplicable);

// ASCII compatible encodings
const ASCII_32BIT_BE: Descriptor =
    Descriptor(Flavour::Unknown, Width::ThirtyTwoBit, ByteOrder::BigEndian);
const ASCII_32BIT_LE: Descriptor = Descriptor(
    Flavour::Unknown,
    Width::ThirtyTwoBit,
    ByteOrder::LittleEndian,
);
const ASCII_16BIT_BE: Descriptor =
    Descriptor(Flavour::Unknown, Width::SixteenBit, ByteOrder::BigEndian);
const ASCII_16BIT_LE: Descriptor =
    Descriptor(Flavour::Unknown, Width::SixteenBit, ByteOrder::LittleEndian);
const ASCII_8BIT: Descriptor =
    Descriptor(Flavour::ASCII, Width::EightBit, ByteOrder::NotApplicable);

/// Attempt to detect the character set of the supplied byte stream.
///
/// `reader` is expected to be positioned at the start of the stream. `detect` will read up to 512
/// bytes in order to determine the encoding.
///
/// The optional `hint` is a possible encoding name for the text that may have been received
/// externally to the text itself, such as from HTTP header.
///
/// ### Example
///
/// ```
/// use std::io::Cursor;
/// extern crate xhtmlchardet;
///
/// let text = b"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><channel><title>Example</title></channel>";
/// let mut text_cursor = Cursor::new(text.to_vec());
/// let detected_charsets = xhtmlchardet::detect(&mut text_cursor, None);
/// assert_eq!(detected_charsets.unwrap_or(vec![]), vec!["iso-8859-1".to_string()]);
/// ```
pub fn detect<R: Read>(reader: &mut R, hint: Option<String>) -> Result<Vec<String>, io::Error> {
    // Read the first 4 bytes and see if they help
    let mut first_four_bytes = [0u8; 4];
    reader.read_exact(&mut first_four_bytes)?;

    let bom = Bom(
        first_four_bytes[0],
        first_four_bytes[1],
        first_four_bytes[2],
        first_four_bytes[3],
    );

    let possible_encoding = detect_byte_order_mark(&bom);

    // Now that byte size may have been determined try reading the first 512ish bytes to read an
    // encoding declaration
    let mut buf = [0u8; 512];
    loop {
        match reader.read(&mut buf) {
            Ok(0) => return Ok(Vec::new()), // eof
            Ok(_n) => break,
            Err(ref err) if err.kind() == io::ErrorKind::Interrupted => {} // retry
            Err(err) => return Err(err),
        };
    }

    let mut candidates = Vec::with_capacity(3);

    // Look for encoding="", charset="?"?
    search("encoding=", &buf, possible_encoding.as_ref())
        .or_else(|| search("charset=", &buf, possible_encoding.as_ref()))
        .map(normalise)
        .map(|encoding| {
            push_if_not_contains(
                &mut candidates,
                endianify(&encoding, possible_encoding.as_ref()),
            )
        });

    // Consider hint
    hint.map(normalise).map(|encoding| {
        push_if_not_contains(
            &mut candidates,
            endianify(&encoding, possible_encoding.as_ref()),
        )
    });

    // Include info from BOM detection
    match possible_encoding {
        Some(UCS_4_LE) => Some("ucs-4le"),
        Some(UCS_4_BE) => Some("ucs-4be"),
        Some(UTF_16_LE) => Some("utf-16le"),
        Some(UTF_16_BE) => Some("utf-16be"),
        Some(Descriptor(Flavour::UTF, Width::EightBit, _)) => Some("utf-8"),
        Some(EBCDIC) => Some("ebcdic"),
        _ => None,
    }
    .map(|encoding| push_if_not_contains(&mut candidates, encoding.to_string()));

    // Otherwise test if UTF-8
    if candidates.is_empty() && std::str::from_utf8(&buf).is_ok() {
        candidates.push("utf-8".to_string());
    }

    Ok(candidates)
}

fn detect_byte_order_mark(bom: &Bom) -> Option<Descriptor> {
    // Can do below without the Bom type if slice pattern syntax becomes non-experimental
    // let possible = match first_four_bytes {
    //     // With Byte Order Mark
    //     [0x00, 0x00, 0xFE, 0xFF]             => Some("UCS-4BE"),
    //     [0xFF, 0xFE, 0x00, 0x00]             => Some("UCS-4LE"),
    //     [0x00, 0x00, 0xFF, 0xFE]             => Some("UCS-4OE"),
    //     [0xFE, 0xFF, 0x00, 0x00]             => Some("UCS-4EO"),
    //     [0xFE, 0xFF, c, d] if c > 0 && d > 0 => Some("UTF-16BE"),
    //     [0xFF, 0xFE, c, d] if c > 0 && d > 0 => Some("UCS-16LE"),
    //     [0xEF, 0xBB, 0xBF, _   ]             => Some("UTF-8"),

    //     //  Without Byte Order Mark
    //     [0x00, 0x00, 0x00, 0x3C] |
    //     [0x3C, 0x00, 0x00, 0x00] |
    //     [0x00, 0x00, 0x3C, 0x00] |
    //     [0x00, 0x3C, 0x00, 0x00]             => Some("32-bit"),
    //     [0x00, 0x3C, 0x00, 0x3F]             => Some("16-bit Big Endian"),
    //     [0x3C, 0x00, 0x3F, 0x00]             => Some("16-bit Little Endian"),
    //     [0x3C, 0x3F, 0x78, 0x6D]             => Some("8-bit"),
    //     [0x4C, 0x6F, 0xA7, 0x94]             => Some("EBCDIC"),
    //     // This may be UTF-8 without an encoding declaration as this is not required
    //     // for UTF-8
    //     _                                    => Some("Other"),
    // };

    // http://www.w3.org/TR/2004/REC-xml-20040204/#sec-guessing-no-ext-info
    match *bom {
        // With Byte Order Mark
        Bom(0x00, 0x00, 0xFE, 0xFF) => Some(UCS_4_BE),
        Bom(0xFF, 0xFE, 0x00, 0x00) => Some(UCS_4_LE),
        Bom(0x00, 0x00, 0xFF, 0xFE) => Some(UCS_4_2143),
        Bom(0xFE, 0xFF, 0x00, 0x00) => Some(UCS_4_3412),
        Bom(0xFE, 0xFF, c, d) if c > 0 || d > 0 => Some(UTF_16_BE),
        Bom(0xFF, 0xFE, c, d) if c > 0 || d > 0 => Some(UTF_16_LE),
        Bom(0xEF, 0xBB, 0xBF, _) => Some(UTF_8),

        //  Without Byte Order Mark
        Bom(0x00, 0x00, 0x00, 0x3C) => Some(ASCII_32BIT_BE),
        Bom(0x3C, 0x00, 0x00, 0x00) => Some(ASCII_32BIT_LE),
        Bom(0x00, 0x00, 0x3C, 0x00) => Some(Descriptor(
            Flavour::Unknown,
            Width::ThirtyTwoBit,
            ByteOrder::Unusual2143,
        )),
        Bom(0x00, 0x3C, 0x00, 0x00) => Some(Descriptor(
            Flavour::Unknown,
            Width::ThirtyTwoBit,
            ByteOrder::Unusual3412,
        )),
        Bom(0x00, 0x3C, 0x00, 0x3F) => Some(ASCII_16BIT_BE),
        Bom(0x3C, 0x00, 0x3F, 0x00) => Some(ASCII_16BIT_LE),
        Bom(0x3C, 0x3F, 0x78, 0x6D) => Some(ASCII_8BIT),
        Bom(0x4C, 0x6F, 0xA7, 0x94) => Some(EBCDIC),
        // This may be UTF-8 without an encoding declaration as this is not required
        // for UTF-8
        _ => None,
    }
}

fn normalise<S: AsRef<str>>(encoding: S) -> String {
    encoding
        .as_ref()
        .to_lowercase()
        .replace("us-ascii", "ascii")
        .replace("utf8", "utf-8")
        .replace("shift-jis", "shift_jis")
}

fn push_if_not_contains<T: PartialEq>(vec: &mut Vec<T>, item: T) {
    if !vec.contains(&item) {
        vec.push(item);
    }
}

fn endianify(encoding: &str, descriptor: Option<&Descriptor>) -> String {
    let ascii = ASCII_8BIT;
    let &Descriptor(_, _, ref order) = descriptor.unwrap_or(&ascii);

    match encoding {
        "utf-16" => match *order {
            ByteOrder::LittleEndian => "utf-16le".to_string(),
            ByteOrder::BigEndian => "utf-16be".to_string(),
            _ => encoding.to_string(),
        },
        _ => encoding.to_string(),
    }
}

fn search(needle: &str, haystack: &[u8], descriptor: Option<&Descriptor>) -> Option<String> {
    let ascii = ASCII_8BIT;
    let &Descriptor(_, ref width, ref order) = descriptor.unwrap_or(&ascii);
    let chunk_size = (*width as usize) / 8;

    let mut index = match *order {
        ByteOrder::NotApplicable | ByteOrder::LittleEndian => 0,
        ByteOrder::BigEndian => chunk_size - 1,
        ByteOrder::Unusual2143 => 2,
        ByteOrder::Unusual3412 => 1,
    };

    let mut ascii_bytes = Vec::with_capacity(haystack.len() / chunk_size);
    while index < haystack.len() {
        ascii_bytes.push(haystack[index]);
        index += chunk_size;
    }

    let ascii_haystack = String::from_utf8_lossy(&ascii_bytes);

    ascii_haystack.find(needle).map(|pos| {
        // Skip to the matching byte + length of the needle
        ascii_haystack[pos + needle.len()..]
            .chars()
            .skip_while(|char| *char == '"' || *char == '\'')
            .take_while(|char| *char != '"' && *char != '\'')
            .collect()
    })
}

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

    #[test]
    fn test_detect_empty() {
        let mut text_cursor = Cursor::new("");
        let detected_charsets = detect(&mut text_cursor, None);
        assert!(detected_charsets.is_err()); // UnexpectedEof
    }

    #[test]
    fn test_detect_4_bytes() {
        let mut text_cursor = Cursor::new("1234");
        let detected_charsets = detect(&mut text_cursor, None).unwrap();
        assert!(detected_charsets.is_empty());
    }
}
<|endoftext|>