<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Access to the Streaming API.

use std::{self, io};

use chrono;
use futures::{Future, Stream, Poll, Async};
use hyper::Body;
use hyper::client::{Request, FutureResponse};
use rustc_serialize::json;

use auth::{self, Token};
use direct::DirectMessage;
use error;
use links;
use tweet::Tweet;
use user::TwitterUser;

use common::*;

/// Represents the kinds of messages that can be sent over Twitter's Streaming API.
#[derive(Debug)]
pub enum StreamMessage {
    /// A blank line, sent periodically to keep the connection alive.
    Ping,
    /// A list of accounts the authenticated user follows, sent at the beginning of the session for
    /// user streams.
    FriendList(Vec<u64>),
    /// A new tweet.
    ///
    /// Note that the `entities` inside the `user` field will be empty for tweets received via the
    /// Streaming API.
    Tweet(Tweet),
    /// A direct message.
    DirectMessage(DirectMessage),
    /// Notification that the given user has liked the given tweet.
    ///
    /// Note that this message can be sent for both "other user liked the authenticated user's
    /// tweet" and "authenticated user liked someone else's tweet".
    Like(chrono::DateTime<chrono::Utc>, TwitterUser, Tweet),
    /// Notification that the given user has cleared their like of the given tweet.
    ///
    /// As with `StreamMessage::Like`, this can be sent for actions taken either by the
    /// authenticated user or upon one of the authenticated user's tweets.
    Unlike(chrono::DateTime<chrono::Utc>, TwitterUser, Tweet),
    /// The authenticated user has blocked the given account.
    Block(chrono::DateTime<chrono::Utc>, TwitterUser),
    /// The authenticated user has unblocked the given account.
    Unblock(chrono::DateTime<chrono::Utc>, TwitterUser),
    /// The `source` user has followed the `target` user.
    ///
    /// This is sent both when the authenticated user follows an account, and when another account
    /// follows the authenticated user.
    Follow {
        /// The timestamp when the event occurred.
        at: chrono::DateTime<chrono::Utc>,
        /// The user who initiated the follow.
        source: TwitterUser,
        /// The user who was followed.
        target: TwitterUser,
    },
    /// The authenticated user has unfollowed the given account.
    Unfollow(chrono::DateTime<chrono::Utc>, TwitterUser),
    /// The given user has quote-tweeted one of the authenticated user's statuses.
    Quoted(chrono::DateTime<chrono::Utc>, TwitterUser, Tweet),
    /// The authenticated user has updated their profile information.
    ProfileUpdate(chrono::DateTime<chrono::Utc>, TwitterUser),
    /// Notice given when a user deletes a post.
    ///
    /// Clients are expected to comply with these notices by removing the status "from memory and
    /// any storage or archive, even in the rare case where a deletion message arrives earlier in
    /// the stream than the Tweet it references."
    Delete {
        /// The status that was deleted.
        status_id: u64,
        /// The user that deleted the status.
        user_id: u64
    },
    /// Notice given when a user removes geolocation information from their profile.
    ///
    /// Clients are expected to comply by deleting cached geolocation information from tweets by
    /// the given user, for any tweets up to and including the given status ID. According to
    /// Twitter's documentation, "These messages may also arrive before a Tweet which falls into
    /// the specified range, although this is rare."
    ScrubGeo {
        /// The user whose geolocation information needs to be scrubbed.
        user_id: u64,
        /// The last status ID to scrub information from.
        up_to_status_id: u64,
    },
    /// Placeholder message used to indicate that a specific tweet has been withheld in certain
    /// countries.
    StatusWithheld {
        /// The status that was withheld.
        status_id: u64,
        /// The user that posted the status.
        user_id: u64,
        /// A list of uppercase two-character country codes listing the countries where the tweet
        /// was withheld.
        withheld_in_countries: Vec<String>,
    },
    /// Placeholder message used to indicate that a specific user's content has been withheld in
    /// certain countries.
    UserWithheld {
        /// The user whose content was withheld.
        user_id: u64,
        /// A list of uppercase two-character country codes listing the countries where the content
        /// was withheld.
        withheld_in_countries: Vec<String>,
    },
    /// An error message that may be delivered immediately prior to Twitter disconnecting the
    /// stream.
    ///
    /// Note that if the stream is disconnected due to network issues or the client reading
    /// messages too slowly, it's possible that this message may not be received.
    ///
    /// The enclosed values are an error code and error description. A non-exhaustive list of error
    /// codes and their associated reasons are available on [Twitter's stream
    /// docmentation][stream-doc], under "Disconnect messages (disconnect)".
    ///
    /// [stream-doc]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/streaming-message-types
    Disconnect(u64, String),
    /// An unhandled message payload.
    ///
    /// Twitter can add new streaming messages to the API, and egg-mode includes them here so that
    /// they can be used before egg-mode has a chance to handle them.
    Unknown(json::Json),
    //TODO: stall warnings? "follows over limit" warnings? (other warnings?)
}

impl FromJson for StreamMessage {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if let Some(event) = input.find("event").and_then(|ev| ev.as_string()) {
            //TODO: all the event types -_-
            match event {
                "favorite" => {
                    Ok(StreamMessage::Like(
                        try!(field(input, "created_at")),
                        try!(field(input, "source")),
                        try!(field(input, "target_object"))))
                }
                "unfavorite" => {
                    Ok(StreamMessage::Unlike(
                        try!(field(input, "created_at")),
                        try!(field(input, "source")),
                        try!(field(input, "target_object"))))
                }
                "block" => {
                    Ok(StreamMessage::Block(
                        try!(field(input, "created_at")),
                        try!(field(input, "target"))))
                }
                "unblock" => {
                    Ok(StreamMessage::Unblock(
                        try!(field(input, "created_at")),
                        try!(field(input, "target"))))
                }
                "follow" => {
                    Ok(StreamMessage::Follow {
                        at: try!(field(input, "created_at")),
                        source: try!(field(input, "source")),
                        target: try!(field(input, "target")),
                    })
                }
                "unfollow" => {
                    Ok(StreamMessage::Unfollow(
                        try!(field(input, "created_at")),
                        try!(field(input, "target"))))
                }
                "quoted_tweet" => {
                    Ok(StreamMessage::Quoted(
                        try!(field(input, "created_at")),
                        try!(field(input, "source")),
                        try!(field(input, "target_object"))))
                }
                "user_update" => {
                    Ok(StreamMessage::ProfileUpdate(
                        try!(field(input, "created_at")),
                        try!(field(input, "source"))))
                }
                _ => {
                    Ok(StreamMessage::Unknown(input.clone()))
                }
            }
        } else if let Some(del) = input.find_path(&["delete", "status"]) {
            Ok(StreamMessage::Delete {
                status_id: try!(field(del, "id")),
                user_id: try!(field(del, "user_id")),
            })
        } else if let Some(scrub) = input.find("scrub_geo") {
            Ok(StreamMessage::ScrubGeo {
                user_id: try!(field(scrub, "user_id")),
                up_to_status_id: try!(field(scrub, "up_to_status_id")),
            })
        } else if let Some(tweet) = input.find("status_withheld") {
            Ok(StreamMessage::StatusWithheld {
                status_id: try!(field(tweet, "id")),
                user_id: try!(field(tweet, "user_id")),
                withheld_in_countries: try!(field(tweet, "withheld_in_countries")),
            })
        } else if let Some(user) = input.find("user_withheld") {
            Ok(StreamMessage::UserWithheld {
                user_id: try!(field(user, "id")),
                withheld_in_countries: try!(field(user, "withheld_in_countries")),
            })
        } else if let Some(err) = input.find("disconnect") {
            Ok(StreamMessage::Disconnect(try!(field(err, "code")), try!(field(err, "reason"))))
        } else if let Some(friends) = input.find("friends") {
            Ok(StreamMessage::FriendList(try!(Vec::<u64>::from_json(friends))))
        } else if let Some(dm) = input.find("direct_message") {
            Ok(StreamMessage::DirectMessage(try!(DirectMessage::from_json(dm))))
        } else if let Ok(tweet) = Tweet::from_json(input) {
            Ok(StreamMessage::Tweet(tweet))
        } else {
            Ok(StreamMessage::Unknown(input.clone()))
        }
    }

    fn from_str(input: &str) -> Result<Self, error::Error> {
        if input.trim().is_empty() {
            Ok(StreamMessage::Ping)
        } else {
            let json = try!(json::Json::from_str(input.trim()));

            StreamMessage::from_json(&json)
        }
    }
}

/// A `Stream` that represents a connection to the Twitter Streaming API.
pub struct TwitterStream {
    buf: Vec<u8>,
    handle: Handle,
    request: Option<Request>,
    response: Option<FutureResponse>,
    body: Option<Body>,
}

impl TwitterStream {
    fn new(handle: &Handle, request: Request) -> TwitterStream {
        TwitterStream {
            buf: vec![],
            handle: handle.clone(),
            request: Some(request),
            response: None,
            body: None,
        }
    }
}

impl Stream for TwitterStream {
    type Item = StreamMessage;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if let Some(req) = self.request.take() {
            self.response = Some(try!(get_response(&self.handle, req)));
        }

        if let Some(mut resp) = self.response.take() {
            match resp.poll() {
                Err(e) => return Err(e.into()),
                Ok(Async::NotReady) => {
                    self.response = Some(resp);
                    return Ok(Async::NotReady);
                },
                Ok(Async::Ready(resp)) => {
                    let status = resp.status();
                    if !status.is_success() {
                        //TODO: should i try to pull the response regardless?
                        return Err(error::Error::BadStatus(status));
                    }

                    self.body = Some(resp.body());
                },
            }
        }

        if let Some(mut body) = self.body.take() {
            loop {
                match body.poll() {
                    Err(e) => {
                        self.body = Some(body);
                        return Err(e.into());
                    },
                    Ok(Async::NotReady) => {
                        self.body = Some(body);
                        return Ok(Async::NotReady);
                    },
                    Ok(Async::Ready(None)) => {
                        //TODO: introduce a new error for this?
                        return Err(error::Error::FutureAlreadyCompleted);
                    },
                    Ok(Async::Ready(Some(chunk))) => {
                        self.buf.extend(&*chunk);

                        if let Some(pos) = self.buf.windows(2).position(|w| w == b"\r\n") {
                            self.body = Some(body);
                            let pos = pos + 2;
                            let resp = if let Ok(msg_str) = std::str::from_utf8(&self.buf[..pos]) {
                                StreamMessage::from_str(msg_str)
                            } else {
                                Err(io::Error::new(io::ErrorKind::InvalidData,
                                                   "stream did not contain valid UTF-8").into())
                            };

                            self.buf.drain(..pos);
                            return Ok(Async::Ready(Some(try!(resp))));
                        }
                    },
                }
            }
        } else {
            Err(error::Error::FutureAlreadyCompleted)
        }
    }
}

/// Opens a `TwitterStream` to the authenticated user's home stream.
pub fn user(handle: &Handle, token: &Token) -> TwitterStream {
    let req = auth::get(links::stream::USER, token, None);

    TwitterStream::new(handle, req)
}
<commit_msg>the list-based events<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Access to the Streaming API.

use std::{self, io};

use chrono;
use futures::{Future, Stream, Poll, Async};
use hyper::Body;
use hyper::client::{Request, FutureResponse};
use rustc_serialize::json;

use auth::{self, Token};
use direct::DirectMessage;
use error;
use links;
use list::List;
use tweet::Tweet;
use user::TwitterUser;

use common::*;

/// Represents the kinds of messages that can be sent over Twitter's Streaming API.
#[derive(Debug)]
pub enum StreamMessage {
    /// A blank line, sent periodically to keep the connection alive.
    Ping,
    /// A list of accounts the authenticated user follows, sent at the beginning of the session for
    /// user streams.
    FriendList(Vec<u64>),
    /// A new tweet.
    ///
    /// Note that the `entities` inside the `user` field will be empty for tweets received via the
    /// Streaming API.
    Tweet(Tweet),
    /// A direct message.
    DirectMessage(DirectMessage),
    /// Notification that the given user has liked the given tweet.
    ///
    /// Note that this message can be sent for both "other user liked the authenticated user's
    /// tweet" and "authenticated user liked someone else's tweet".
    Like(chrono::DateTime<chrono::Utc>, TwitterUser, Tweet),
    /// Notification that the given user has cleared their like of the given tweet.
    ///
    /// As with `StreamMessage::Like`, this can be sent for actions taken either by the
    /// authenticated user or upon one of the authenticated user's tweets.
    Unlike(chrono::DateTime<chrono::Utc>, TwitterUser, Tweet),
    /// The authenticated user has blocked the given account.
    Block(chrono::DateTime<chrono::Utc>, TwitterUser),
    /// The authenticated user has unblocked the given account.
    Unblock(chrono::DateTime<chrono::Utc>, TwitterUser),
    /// The `source` user has followed the `target` user.
    ///
    /// This is sent both when the authenticated user follows an account, and when another account
    /// follows the authenticated user.
    Follow {
        /// The timestamp when the event occurred.
        at: chrono::DateTime<chrono::Utc>,
        /// The user who initiated the follow.
        source: TwitterUser,
        /// The user who was followed.
        target: TwitterUser,
    },
    /// The authenticated user has unfollowed the given account.
    Unfollow(chrono::DateTime<chrono::Utc>, TwitterUser),
    /// The given user has quote-tweeted one of the authenticated user's statuses.
    Quoted(chrono::DateTime<chrono::Utc>, TwitterUser, Tweet),
    /// The authenticated user has updated their profile information.
    ProfileUpdate(chrono::DateTime<chrono::Utc>, TwitterUser),
    /// The given user was added to the given list.
    ///
    /// Note that this is sent both for the authenticated user adding members to their own lists,
    /// and other accounts adding the authenticated user to their own lists.
    AddListMember(chrono::DateTime<chrono::Utc>, TwitterUser, List),
    /// The given user was removed from the given list.
    ///
    /// As with `AddListMember`, this is sent both when the authenticated user removes an account
    /// from their own list, or when another account has removed the authenticated user from their
    /// own list.
    RemoveListMember(chrono::DateTime<chrono::Utc>, TwitterUser, List),
    /// The given user has subscried to the given list.
    ///
    /// This is sent for both the authenticated user subscribing to someone else's list, and for
    /// another user subscribing to one of the authenticated user's lists.
    ListSubscribe(chrono::DateTime<chrono::Utc>, TwitterUser, List),
    /// The given user has unsubscribed from the given list.
    ///
    /// As with `ListSubscribe`, this is sent both when the authenticated user unsubscribes from
    /// someone else's list, and when another user subscribes to one of the authenticated user's
    /// lists.
    ListUnsubscribe(chrono::DateTime<chrono::Utc>, TwitterUser, List),
    /// Notice given when a user deletes a post.
    ///
    /// Clients are expected to comply with these notices by removing the status "from memory and
    /// any storage or archive, even in the rare case where a deletion message arrives earlier in
    /// the stream than the Tweet it references."
    Delete {
        /// The status that was deleted.
        status_id: u64,
        /// The user that deleted the status.
        user_id: u64
    },
    /// Notice given when a user removes geolocation information from their profile.
    ///
    /// Clients are expected to comply by deleting cached geolocation information from tweets by
    /// the given user, for any tweets up to and including the given status ID. According to
    /// Twitter's documentation, "These messages may also arrive before a Tweet which falls into
    /// the specified range, although this is rare."
    ScrubGeo {
        /// The user whose geolocation information needs to be scrubbed.
        user_id: u64,
        /// The last status ID to scrub information from.
        up_to_status_id: u64,
    },
    /// Placeholder message used to indicate that a specific tweet has been withheld in certain
    /// countries.
    StatusWithheld {
        /// The status that was withheld.
        status_id: u64,
        /// The user that posted the status.
        user_id: u64,
        /// A list of uppercase two-character country codes listing the countries where the tweet
        /// was withheld.
        withheld_in_countries: Vec<String>,
    },
    /// Placeholder message used to indicate that a specific user's content has been withheld in
    /// certain countries.
    UserWithheld {
        /// The user whose content was withheld.
        user_id: u64,
        /// A list of uppercase two-character country codes listing the countries where the content
        /// was withheld.
        withheld_in_countries: Vec<String>,
    },
    /// An error message that may be delivered immediately prior to Twitter disconnecting the
    /// stream.
    ///
    /// Note that if the stream is disconnected due to network issues or the client reading
    /// messages too slowly, it's possible that this message may not be received.
    ///
    /// The enclosed values are an error code and error description. A non-exhaustive list of error
    /// codes and their associated reasons are available on [Twitter's stream
    /// docmentation][stream-doc], under "Disconnect messages (disconnect)".
    ///
    /// [stream-doc]: https://developer.twitter.com/en/docs/tweets/filter-realtime/guides/streaming-message-types
    Disconnect(u64, String),
    /// An unhandled message payload.
    ///
    /// Twitter can add new streaming messages to the API, and egg-mode includes them here so that
    /// they can be used before egg-mode has a chance to handle them.
    Unknown(json::Json),
    //TODO: stall warnings? "follows over limit" warnings? (other warnings?)
}

impl FromJson for StreamMessage {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if let Some(event) = input.find("event").and_then(|ev| ev.as_string()) {
            //TODO: if i ever support site streams, add "access_revoked" here
            match event {
                "favorite" => {
                    Ok(StreamMessage::Like(
                        try!(field(input, "created_at")),
                        try!(field(input, "source")),
                        try!(field(input, "target_object"))))
                }
                "unfavorite" => {
                    Ok(StreamMessage::Unlike(
                        try!(field(input, "created_at")),
                        try!(field(input, "source")),
                        try!(field(input, "target_object"))))
                }
                "block" => {
                    Ok(StreamMessage::Block(
                        try!(field(input, "created_at")),
                        try!(field(input, "target"))))
                }
                "unblock" => {
                    Ok(StreamMessage::Unblock(
                        try!(field(input, "created_at")),
                        try!(field(input, "target"))))
                }
                "follow" => {
                    Ok(StreamMessage::Follow {
                        at: try!(field(input, "created_at")),
                        source: try!(field(input, "source")),
                        target: try!(field(input, "target")),
                    })
                }
                "unfollow" => {
                    Ok(StreamMessage::Unfollow(
                        try!(field(input, "created_at")),
                        try!(field(input, "target"))))
                }
                "quoted_tweet" => {
                    Ok(StreamMessage::Quoted(
                        try!(field(input, "created_at")),
                        try!(field(input, "source")),
                        try!(field(input, "target_object"))))
                }
                "user_update" => {
                    Ok(StreamMessage::ProfileUpdate(
                        try!(field(input, "created_at")),
                        try!(field(input, "source"))))
                }
                "list_member_added" => {
                    Ok(StreamMessage::AddListMember(
                        try!(field(input, "created_at")),
                        try!(field(input, "target")),
                        try!(field(input, "target_object"))))
                }
                "list_member_removed" => {
                    Ok(StreamMessage::RemoveListMember(
                        try!(field(input, "created_at")),
                        try!(field(input, "target")),
                        try!(field(input, "target_object"))))
                }
                "list_user_subscribed" => {
                    Ok(StreamMessage::ListSubscribe(
                        try!(field(input, "created_at")),
                        try!(field(input, "source")),
                        try!(field(input, "target_object"))))
                }
                "list_user_unsubscribed" => {
                    Ok(StreamMessage::ListUnsubscribe(
                        try!(field(input, "created_at")),
                        try!(field(input, "source")),
                        try!(field(input, "target_object"))))
                }
                _ => {
                    Ok(StreamMessage::Unknown(input.clone()))
                }
            }
        } else if let Some(del) = input.find_path(&["delete", "status"]) {
            Ok(StreamMessage::Delete {
                status_id: try!(field(del, "id")),
                user_id: try!(field(del, "user_id")),
            })
        } else if let Some(scrub) = input.find("scrub_geo") {
            Ok(StreamMessage::ScrubGeo {
                user_id: try!(field(scrub, "user_id")),
                up_to_status_id: try!(field(scrub, "up_to_status_id")),
            })
        } else if let Some(tweet) = input.find("status_withheld") {
            Ok(StreamMessage::StatusWithheld {
                status_id: try!(field(tweet, "id")),
                user_id: try!(field(tweet, "user_id")),
                withheld_in_countries: try!(field(tweet, "withheld_in_countries")),
            })
        } else if let Some(user) = input.find("user_withheld") {
            Ok(StreamMessage::UserWithheld {
                user_id: try!(field(user, "id")),
                withheld_in_countries: try!(field(user, "withheld_in_countries")),
            })
        } else if let Some(err) = input.find("disconnect") {
            Ok(StreamMessage::Disconnect(try!(field(err, "code")), try!(field(err, "reason"))))
        } else if let Some(friends) = input.find("friends") {
            Ok(StreamMessage::FriendList(try!(Vec::<u64>::from_json(friends))))
        } else if let Some(dm) = input.find("direct_message") {
            Ok(StreamMessage::DirectMessage(try!(DirectMessage::from_json(dm))))
        } else if let Ok(tweet) = Tweet::from_json(input) {
            Ok(StreamMessage::Tweet(tweet))
        } else {
            Ok(StreamMessage::Unknown(input.clone()))
        }
    }

    fn from_str(input: &str) -> Result<Self, error::Error> {
        if input.trim().is_empty() {
            Ok(StreamMessage::Ping)
        } else {
            let json = try!(json::Json::from_str(input.trim()));

            StreamMessage::from_json(&json)
        }
    }
}

/// A `Stream` that represents a connection to the Twitter Streaming API.
pub struct TwitterStream {
    buf: Vec<u8>,
    handle: Handle,
    request: Option<Request>,
    response: Option<FutureResponse>,
    body: Option<Body>,
}

impl TwitterStream {
    fn new(handle: &Handle, request: Request) -> TwitterStream {
        TwitterStream {
            buf: vec![],
            handle: handle.clone(),
            request: Some(request),
            response: None,
            body: None,
        }
    }
}

impl Stream for TwitterStream {
    type Item = StreamMessage;
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if let Some(req) = self.request.take() {
            self.response = Some(try!(get_response(&self.handle, req)));
        }

        if let Some(mut resp) = self.response.take() {
            match resp.poll() {
                Err(e) => return Err(e.into()),
                Ok(Async::NotReady) => {
                    self.response = Some(resp);
                    return Ok(Async::NotReady);
                },
                Ok(Async::Ready(resp)) => {
                    let status = resp.status();
                    if !status.is_success() {
                        //TODO: should i try to pull the response regardless?
                        return Err(error::Error::BadStatus(status));
                    }

                    self.body = Some(resp.body());
                },
            }
        }

        if let Some(mut body) = self.body.take() {
            loop {
                match body.poll() {
                    Err(e) => {
                        self.body = Some(body);
                        return Err(e.into());
                    },
                    Ok(Async::NotReady) => {
                        self.body = Some(body);
                        return Ok(Async::NotReady);
                    },
                    Ok(Async::Ready(None)) => {
                        //TODO: introduce a new error for this?
                        return Err(error::Error::FutureAlreadyCompleted);
                    },
                    Ok(Async::Ready(Some(chunk))) => {
                        self.buf.extend(&*chunk);

                        if let Some(pos) = self.buf.windows(2).position(|w| w == b"\r\n") {
                            self.body = Some(body);
                            let pos = pos + 2;
                            let resp = if let Ok(msg_str) = std::str::from_utf8(&self.buf[..pos]) {
                                StreamMessage::from_str(msg_str)
                            } else {
                                Err(io::Error::new(io::ErrorKind::InvalidData,
                                                   "stream did not contain valid UTF-8").into())
                            };

                            self.buf.drain(..pos);
                            return Ok(Async::Ready(Some(try!(resp))));
                        }
                    },
                }
            }
        } else {
            Err(error::Error::FutureAlreadyCompleted)
        }
    }
}

/// Opens a `TwitterStream` to the authenticated user's home stream.
pub fn user(handle: &Handle, token: &Token) -> TwitterStream {
    let req = auth::get(links::stream::USER, token, None);

    TwitterStream::new(handle, req)
}
<|endoftext|>


<commit_before>#[cfg(feature = "model")]
use hyper::Client as HyperClient;
#[cfg(feature = "model")]
use std::io::Read;
#[cfg(feature = "model")]
use internal::prelude::*;

/// A file uploaded with a message. Not to be confused with [`Embed`]s.
///
/// [`Embed`]: struct.Embed.html
#[derive(Clone, Debug, Deserialize)]
pub struct Attachment {
    /// The unique ID given to this attachment.
    pub id: String,
    /// The filename of the file that was uploaded. This is equivalent to what
    /// the uploader had their file named.
    pub filename: String,
    /// If the attachment is an image, then the height of the image is provided.
    pub height: Option<u64>,
    /// The proxy URL.
    pub proxy_url: String,
    /// The size of the file in bytes.
    pub size: u64,
    /// The URL of the uploaded attachment.
    pub url: String,
    /// If the attachment is an image, then the widfth of the image is provided.
    pub width: Option<u64>,
}

#[cfg(feature = "model")]
impl Attachment {
    /// If this attachment is an image, then a tuple of the width and height
    /// in pixels is returned.
    pub fn dimensions(&self) -> Option<(u64, u64)> {
        self.width
            .and_then(|width| self.height.map(|height| (width, height)))
    }

    /// Downloads the attachment, returning back a vector of bytes.
    ///
    /// # Examples
    ///
    /// Download all of the attachments associated with a [`Message`]:
    ///
    /// ```rust,no_run
    /// use serenity::prelude::*;
    /// use serenity::model::*;
    /// use std::env;
    /// use std::fs::File;
    /// use std::io::Write;
    /// use std::path::Path;
    ///
    /// struct Handler;
    ///
    ///
    /// impl EventHandler for Handler {
    ///     fn on_message(&self, _: Context, message: Message) {
    ///         for attachment in message.attachments {
    ///             let content = match attachment.download() {
    ///                 Ok(content) => content,
    ///                 Err(why) => {
    ///                     println!("Error downloading attachment: {:?}", why);
    ///                     let _ = message.channel_id.say("Error downloading attachment");
    ///
    ///                     return;
    ///                 },
    ///             };
    ///
    ///             let mut file = match File::create(&attachment.filename) {
    ///                 Ok(file) => file,
    ///                 Err(why) => {
    ///                     println!("Error creating file: {:?}", why);
    ///                     let _ = message.channel_id.say("Error creating file");
    ///
    ///                     return;
    ///                 },
    ///             };
    ///
    ///             if let Err(why) = file.write(&content) {
    ///                 println!("Error writing to file: {:?}", why);
    ///
    ///                 return;
    ///             }
    ///
    ///             let _ = message.channel_id.say(&format!("Saved {:?}", attachment.filename));
    ///         }
    ///     }
    ///
    ///     fn on_ready(&self, _: Context, ready: Ready) {
    ///         println!("{} is connected!", ready.user.name);
    ///     }
    /// }
    /// let token = env::var("DISCORD_TOKEN").expect("token in environment");
    /// let mut client = Client::new(&token, Handler); client.start().unwrap();
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an [`Error::Io`] when there is a problem reading the contents
    /// of the HTTP response.
    ///
    /// Returns an [`Error::Hyper`] when there is a problem retrieving the
    /// attachment.
    ///
    /// [`Error::Hyper`]: ../enum.Error.html#variant.Hyper
    /// [`Error::Io`]: ../enum.Error.html#variant.Io
    /// [`Message`]: struct.Message.html
    pub fn download(&self) -> Result<Vec<u8>> {
        let hyper = request_client!();
        let mut response = hyper.get(&self.url).send()?;

        let mut bytes = vec![];
        response.read_to_end(&mut bytes)?;

        Ok(bytes)
    }
}
<commit_msg>Fix a typo<commit_after>#[cfg(feature = "model")]
use hyper::Client as HyperClient;
#[cfg(feature = "model")]
use std::io::Read;
#[cfg(feature = "model")]
use internal::prelude::*;

/// A file uploaded with a message. Not to be confused with [`Embed`]s.
///
/// [`Embed`]: struct.Embed.html
#[derive(Clone, Debug, Deserialize)]
pub struct Attachment {
    /// The unique ID given to this attachment.
    pub id: String,
    /// The filename of the file that was uploaded. This is equivalent to what
    /// the uploader had their file named.
    pub filename: String,
    /// If the attachment is an image, then the height of the image is provided.
    pub height: Option<u64>,
    /// The proxy URL.
    pub proxy_url: String,
    /// The size of the file in bytes.
    pub size: u64,
    /// The URL of the uploaded attachment.
    pub url: String,
    /// If the attachment is an image, then the width of the image is provided.
    pub width: Option<u64>,
}

#[cfg(feature = "model")]
impl Attachment {
    /// If this attachment is an image, then a tuple of the width and height
    /// in pixels is returned.
    pub fn dimensions(&self) -> Option<(u64, u64)> {
        self.width
            .and_then(|width| self.height.map(|height| (width, height)))
    }

    /// Downloads the attachment, returning back a vector of bytes.
    ///
    /// # Examples
    ///
    /// Download all of the attachments associated with a [`Message`]:
    ///
    /// ```rust,no_run
    /// use serenity::prelude::*;
    /// use serenity::model::*;
    /// use std::env;
    /// use std::fs::File;
    /// use std::io::Write;
    /// use std::path::Path;
    ///
    /// struct Handler;
    ///
    ///
    /// impl EventHandler for Handler {
    ///     fn on_message(&self, _: Context, message: Message) {
    ///         for attachment in message.attachments {
    ///             let content = match attachment.download() {
    ///                 Ok(content) => content,
    ///                 Err(why) => {
    ///                     println!("Error downloading attachment: {:?}", why);
    ///                     let _ = message.channel_id.say("Error downloading attachment");
    ///
    ///                     return;
    ///                 },
    ///             };
    ///
    ///             let mut file = match File::create(&attachment.filename) {
    ///                 Ok(file) => file,
    ///                 Err(why) => {
    ///                     println!("Error creating file: {:?}", why);
    ///                     let _ = message.channel_id.say("Error creating file");
    ///
    ///                     return;
    ///                 },
    ///             };
    ///
    ///             if let Err(why) = file.write(&content) {
    ///                 println!("Error writing to file: {:?}", why);
    ///
    ///                 return;
    ///             }
    ///
    ///             let _ = message.channel_id.say(&format!("Saved {:?}", attachment.filename));
    ///         }
    ///     }
    ///
    ///     fn on_ready(&self, _: Context, ready: Ready) {
    ///         println!("{} is connected!", ready.user.name);
    ///     }
    /// }
    /// let token = env::var("DISCORD_TOKEN").expect("token in environment");
    /// let mut client = Client::new(&token, Handler); client.start().unwrap();
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an [`Error::Io`] when there is a problem reading the contents
    /// of the HTTP response.
    ///
    /// Returns an [`Error::Hyper`] when there is a problem retrieving the
    /// attachment.
    ///
    /// [`Error::Hyper`]: ../enum.Error.html#variant.Hyper
    /// [`Error::Io`]: ../enum.Error.html#variant.Io
    /// [`Message`]: struct.Message.html
    pub fn download(&self) -> Result<Vec<u8>> {
        let hyper = request_client!();
        let mut response = hyper.get(&self.url).send()?;

        let mut bytes = vec![];
        response.read_to_end(&mut bytes)?;

        Ok(bytes)
    }
}
<|endoftext|>


<commit_before>//! Symbol- and name-storage facilities.
//!
//! This is based on [Servo's string-cache
//! library](https://github.com/servo/string-cache); at the moment it's very
//! lifetime-unsafe for `Pooled` instances.
use std;
use std::fmt;
use std::slice::bytes;
use std::hash::{hash, Hash, Hasher, SipHasher};
use std::collections::BTreeMap;
use std::borrow::ToOwned;

#[macro_use]
pub mod nameable;
pub use self::nameable::Nameable;

/// Methods required of `unpacked` types.
pub trait PackFormat {
    /// Pack an unpacked symbol in the implementing format into a generic
    /// Symbol object.
    fn pack(&self) -> Symbol;

    /// Unpack a generic Symbol into the implementing format.  This function is
    /// marked as `unsafe` because the caller must verify that the symbol is
    /// indeed packed by the receiver's implementation.
    unsafe fn unpack(sym: &Symbol) -> Self;

    /// Fetch a `&str` slice from a symbol packed in the implementing format.
    /// This function is marked as `unsafe` because the caller must verify that
    /// the symbol is indeed packed by the receiver's implementation.
    unsafe fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str;
}

/* ****************************************************************
 * Pool
 */
/// A collection of symbols.
#[derive(Debug)]
pub struct Pool {
    map:  BTreeMap<u64,String>
}

impl Pool {
    /// Create a new symbol pool.
    pub fn new() -> Pool {
        Pool{map: BTreeMap::new()}
    }
    /// Fetch a symbol corresponding to the given string.  If if
    pub fn symbol(&mut self, name: &str) -> Symbol {
        if name.len() <= INLINE_SYMBOL_MAX_LEN
        { Inline::new(name).pack() }
        else { let hsh = hash::<_, SipHasher>(&name) << 1;
               if ! self.map.contains_key(&hsh) {
                   self.map.insert(hsh, name.to_owned());
               }

               Pooled::new(hsh, self).pack()
        }
    }
}

/* ****************************************************************
 * Symbol
 */
/// An atomic `Copy`able string.  Symbol either encodes a short string
/// directly, or stores it in an external Pool.
#[derive(Debug)]
pub struct Symbol {
    value: [u64; 2]
}
impl Copy for Symbol {}

impl Symbol {
    /// Convert the symbol to its unpacked representation.
    #[inline(always)]
    pub fn unpack(&self) -> Unpacked
    { unsafe { <Unpacked as PackFormat>::unpack(self) } }

    /// Get symbol's pack format.
    #[inline(always)]
    pub fn type_of(&self) -> Type {
        match (self.value[0] & 0x01) as u8 {
            INLINE => Type::INLINE,
            POOLED => Type::POOLED,
            _ => unreachable!()
        }
    }

}

impl std::str::Str for Symbol {
    fn as_slice<'t>(&'t self) -> &'t str { unsafe { <Unpacked as PackFormat>::as_slice_from(self) } }
}

impl Nameable for Symbol {
    fn name(&self) -> Symbol { *self }
}

impl Hash for Symbol {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_slice().hash(state);
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_slice())
    }
}

impl Eq for Symbol {}
impl PartialEq<Symbol> for Symbol
{
    fn eq(&self, other: &Symbol) -> bool
    {
        self.value == other.value
    }
}

/* ****************************************************************
 * Inline
 */
const INLINE_SYMBOL_MAX_LEN: usize = 15;//std::mem::size_of::<Symbol>() - 1;

/// Data format for symbols packed entirely into a `Symbol` instance.
#[derive(Debug)]
pub struct Inline{len: u8, data: [u8; INLINE_SYMBOL_MAX_LEN]}

impl Inline {
    /// Create a new Inline-format symbol using the given string.
    pub fn new(name: &str) -> Self {
        panic_unless!(name.len() <= INLINE_SYMBOL_MAX_LEN,
                      "Argument to Inline::new (length {}) exceeds maximum length ({}) for an inlined symbol",
                      name.len(), INLINE_SYMBOL_MAX_LEN);
        let mut buf: [u8; INLINE_SYMBOL_MAX_LEN] = [0; INLINE_SYMBOL_MAX_LEN];

        bytes::copy_memory(buf.as_mut_slice(), name.as_bytes());
        Inline{len: name.len() as u8, data: buf}
    }
}

impl std::str::Str for Inline {
    fn as_slice<'t>(&'t self) -> &'t str {
        let src: &[u8] = self.data.as_slice();
        std::str::from_utf8(&src[..(self.len as usize)]).unwrap()
    }
}

impl PackFormat for Inline {
    // Convert the Inline symbol into a generic packed representation.
    fn pack(&self) -> Symbol {
        unsafe {
            let mut out = Symbol{ value: [0; 2] };
            let mut dest: &mut [u8; 16] = std::mem::transmute(&mut out.value);
            dest[0] = (self.len << 1) | INLINE;
            bytes::copy_memory(&mut dest[1..], &self.data);
            out
        }
    }
    fn unpack(sym: &Symbol) -> Self {
        let mut out = Inline{len: 0, data: [0; INLINE_SYMBOL_MAX_LEN]};
        let src: &[u8;16] =
            unsafe { std::mem::transmute(&sym.value) };

        panic_unless!(src[0] & TAG_MASK == INLINE, "Invalid tag bit for inlined symbol!");
        out.len = src[0] >> 1;
        panic_unless!(out.len <= INLINE_SYMBOL_MAX_LEN as u8,
                      "Symbol length ({}) exceeds maximum ({}) for inlined symbol",
                      out.len, INLINE_SYMBOL_MAX_LEN);
        bytes::copy_memory(out.data.as_mut_slice(), &src[1..(out.len as usize + 1)]);

        out
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        let src: &[u8; 16] = unsafe { std::mem::transmute(&sym.value) };
        panic_unless!(src[0] & TAG_MASK == INLINE, "Invalid tag bit for inlined symbol!");
        let len: usize = (src[0] >> 1) as usize;
        panic_unless!(len <= INLINE_SYMBOL_MAX_LEN,
                      "Symbol length ({}) exceeds maximum ({}) for inlined symbol",
                      len, INLINE_SYMBOL_MAX_LEN);

        std::str::from_utf8(&src[1..(len + 1)]).unwrap()
    }
}

impl std::cmp::Eq for Inline {}
impl std::cmp::PartialEq for Inline {
    fn eq(&self, other: &Self) -> bool {
        self.len == other.len && self.data == other.data
    }
}

/* ****************************************************************
 * Pooled
 */
/// Data format for symbols stored as references to a symbol pool.
#[derive(Debug)]
pub struct Pooled{key: u64, pool: *const Pool}

impl Pooled {
    #[inline(always)]
    pub fn new(key: u64, pool: *const Pool) -> Self {
        Pooled{key: key, pool: pool}
    }

}

impl std::str::Str for Pooled {
    fn as_slice<'u>(&'u self) -> &'u str {
        unsafe { (*self.pool).map[self.key].as_slice() }
    }
}

impl PackFormat for Pooled {
    fn pack(&self) -> Symbol {
        let mut val: [u64; 2] = [0; 2];
        val[0] = self.key  | POOLED as u64;
        val[1] = self.pool as u64;
        Symbol{value: val}
    }
    fn unpack(sym: &Symbol) -> Self {
        Pooled{key: sym.value[0],
               pool: unsafe { std::mem::transmute(sym.value[1] as *const Pool) }}
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        panic_unless!(sym.value[0] & TAG_MASK as u64 == POOLED as u64,
                      "Invalid flag bit for pooled symbol");
        unsafe { std::mem::transmute::<_,&'t Pool>(sym.value[1] as *const Pool) }.
            map[sym.value[0]].as_slice()
    }
}

impl std::cmp::Eq for Pooled {}
impl std::cmp::PartialEq for Pooled {
    fn eq(&self, other: &Self) -> bool {
        self.key == other.key && self.pool as u64 == other.pool as u64
    }
}

/* ****************************************************************
 * Types
 */
const TAG_MASK: u8 = 0x01;
const POOLED: u8 = 0;
const INLINE: u8 = 1;

pub enum Type { INLINE = 0, POOLED = 1 }

impl std::cmp::Eq for Type {}
impl std::cmp::PartialEq for Type {
    fn eq(&self, other: &Self) -> bool {
        match *self { Type::INLINE => match *other { Type::INLINE => true, _ => false },
                      Type::POOLED => match *other { Type::POOLED => true, _ => false } }
    }
}

impl std::fmt::Debug for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self { Type::INLINE => write!(f, "inline"),
                      Type::POOLED => write!(f, "pooled") }
    }
}

/* ****************************************************************
 * Unpacked
 */

/// A differentiated `Symbol`.
#[derive(Debug)]
pub enum Unpacked {
    /// Symbol stored inline in `Symbol`'s data fields.
    Inline(Inline),

    /// Symbol stored in a Pool
    Pooled(Pooled)
}

impl PackFormat for Unpacked {
    fn pack(&self) -> Symbol {
        match *self {
            Unpacked::Inline(ref x) => x.pack(),
            Unpacked::Pooled(ref x) => x.pack()
        }
    }

    fn unpack(sym: &Symbol) -> Unpacked {
        match sym.type_of() {
            Type::POOLED => Unpacked::Pooled(unsafe { <Pooled as PackFormat>::unpack(sym) }),
            Type::INLINE => Unpacked::Inline(unsafe { <Inline as PackFormat>::unpack(sym) }),
        }
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        match sym.type_of() {
            Type::INLINE => unsafe { <Inline as PackFormat>::as_slice_from(sym) },
            Type::POOLED => unsafe { <Pooled as PackFormat>::as_slice_from(sym) }
        }
    }
}

impl std::str::Str for Unpacked {
    fn as_slice<'t>(&'t self) -> &'t str {
        match *self {
            Unpacked::Inline(ref x) => x.as_slice(),
            Unpacked::Pooled(ref x) => x.as_slice()
        }
    }
}

impl std::cmp::Eq for Unpacked {}
impl std::cmp::PartialEq for Unpacked {
    fn eq(&self, other: &Self) -> bool {
        match *self {
            Unpacked::Inline(ref si) =>
                match *other { Unpacked::Inline(ref oi) => si == oi, _ => false },
            Unpacked::Pooled(ref sp) =>
                match *other { Unpacked::Pooled(ref op) => sp == op, _ => false }
        }
    }
}

impl Unpacked {
}
<commit_msg>symbol: change std::str::Str => AsRef<str><commit_after>//! Symbol- and name-storage facilities.
//!
//! This is based on [Servo's string-cache
//! library](https://github.com/servo/string-cache); at the moment it's very
//! lifetime-unsafe for `Pooled` instances.
use std;
use std::fmt;
use std::convert::AsRef;
use std::slice::bytes;
use std::hash::{hash, Hash, Hasher, SipHasher};
use std::collections::BTreeMap;
use std::borrow::ToOwned;

#[macro_use]
pub mod nameable;
pub use self::nameable::Nameable;

/// Methods required of `unpacked` types.
pub trait PackFormat {
    /// Pack an unpacked symbol in the implementing format into a generic
    /// Symbol object.
    fn pack(&self) -> Symbol;

    /// Unpack a generic Symbol into the implementing format.  This function is
    /// marked as `unsafe` because the caller must verify that the symbol is
    /// indeed packed by the receiver's implementation.
    unsafe fn unpack(sym: &Symbol) -> Self;

    /// Fetch a `&str` slice from a symbol packed in the implementing format.
    /// This function is marked as `unsafe` because the caller must verify that
    /// the symbol is indeed packed by the receiver's implementation.
    unsafe fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str;
}

/* ****************************************************************
 * Pool
 */
/// A collection of symbols.
#[derive(Debug)]
pub struct Pool {
    map:  BTreeMap<u64,String>
}

impl Pool {
    /// Create a new symbol pool.
    pub fn new() -> Pool {
        Pool{map: BTreeMap::new()}
    }
    /// Fetch a symbol corresponding to the given string.  If if
    pub fn symbol(&mut self, name: &str) -> Symbol {
        if name.len() <= INLINE_SYMBOL_MAX_LEN
        { Inline::new(name).pack() }
        else { let hsh = hash::<_, SipHasher>(&name) << 1;
               if ! self.map.contains_key(&hsh) {
                   self.map.insert(hsh, name.to_owned());
               }

               Pooled::new(hsh, self).pack()
        }
    }
}

/* ****************************************************************
 * Symbol
 */
/// An atomic `Copy`able string.  Symbol either encodes a short string
/// directly, or stores it in an external Pool.
#[derive(Debug)]
pub struct Symbol {
    value: [u64; 2]
}
impl Copy for Symbol {}

impl Symbol {
    /// Convert the symbol to its unpacked representation.
    #[inline(always)]
    pub fn unpack(&self) -> Unpacked
    { unsafe { <Unpacked as PackFormat>::unpack(self) } }

    /// Get symbol's pack format.
    #[inline(always)]
    pub fn type_of(&self) -> Type {
        match (self.value[0] & 0x01) as u8 {
            INLINE => Type::INLINE,
            POOLED => Type::POOLED,
            _ => unreachable!()
        }
    }

}

impl AsRef<str> for Symbol {
    fn as_ref<'t>(&'t self) -> &'t str { unsafe { <Unpacked as PackFormat>::as_slice_from(self) } }
}

impl Nameable for Symbol {
    fn name(&self) -> Symbol { *self }
}

impl Hash for Symbol {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_ref().hash(state);
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_ref())
    }
}

impl Eq for Symbol {}
impl PartialEq<Symbol> for Symbol
{
    fn eq(&self, other: &Symbol) -> bool
    {
        self.value == other.value
    }
}

/* ****************************************************************
 * Inline
 */
const INLINE_SYMBOL_MAX_LEN: usize = 15;//std::mem::size_of::<Symbol>() - 1;

/// Data format for symbols packed entirely into a `Symbol` instance.
#[derive(Debug)]
pub struct Inline{len: u8, data: [u8; INLINE_SYMBOL_MAX_LEN]}

impl Inline {
    /// Create a new Inline-format symbol using the given string.
    pub fn new(name: &str) -> Self {
        panic_unless!(name.len() <= INLINE_SYMBOL_MAX_LEN,
                      "Argument to Inline::new (length {}) exceeds maximum length ({}) for an inlined symbol",
                      name.len(), INLINE_SYMBOL_MAX_LEN);
        let mut buf: [u8; INLINE_SYMBOL_MAX_LEN] = [0; INLINE_SYMBOL_MAX_LEN];

        bytes::copy_memory(buf.as_mut_slice(), name.as_bytes());
        Inline{len: name.len() as u8, data: buf}
    }
}

impl AsRef<str> for Inline {
    fn as_ref<'t>(&'t self) -> &'t str {
        let src: &[u8] = self.data.as_ref();
        std::str::from_utf8(&src[..(self.len as usize)]).unwrap()
    }
}

impl PackFormat for Inline {
    // Convert the Inline symbol into a generic packed representation.
    fn pack(&self) -> Symbol {
        unsafe {
            let mut out = Symbol{ value: [0; 2] };
            let mut dest: &mut [u8; 16] = std::mem::transmute(&mut out.value);
            dest[0] = (self.len << 1) | INLINE;
            bytes::copy_memory(&mut dest[1..], &self.data);
            out
        }
    }
    fn unpack(sym: &Symbol) -> Self {
        let mut out = Inline{len: 0, data: [0; INLINE_SYMBOL_MAX_LEN]};
        let src: &[u8;16] =
            unsafe { std::mem::transmute(&sym.value) };

        panic_unless!(src[0] & TAG_MASK == INLINE, "Invalid tag bit for inlined symbol!");
        out.len = src[0] >> 1;
        panic_unless!(out.len <= INLINE_SYMBOL_MAX_LEN as u8,
                      "Symbol length ({}) exceeds maximum ({}) for inlined symbol",
                      out.len, INLINE_SYMBOL_MAX_LEN);
        bytes::copy_memory(out.data.as_mut_slice(), &src[1..(out.len as usize + 1)]);

        out
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        let src: &[u8; 16] = unsafe { std::mem::transmute(&sym.value) };
        panic_unless!(src[0] & TAG_MASK == INLINE, "Invalid tag bit for inlined symbol!");
        let len: usize = (src[0] >> 1) as usize;
        panic_unless!(len <= INLINE_SYMBOL_MAX_LEN,
                      "Symbol length ({}) exceeds maximum ({}) for inlined symbol",
                      len, INLINE_SYMBOL_MAX_LEN);

        std::str::from_utf8(&src[1..(len + 1)]).unwrap()
    }
}

impl std::cmp::Eq for Inline {}
impl std::cmp::PartialEq for Inline {
    fn eq(&self, other: &Self) -> bool {
        self.len == other.len && self.data == other.data
    }
}

/* ****************************************************************
 * Pooled
 */
/// Data format for symbols stored as references to a symbol pool.
#[derive(Debug)]
pub struct Pooled{key: u64, pool: *const Pool}

impl Pooled {
    #[inline(always)]
    pub fn new(key: u64, pool: *const Pool) -> Self {
        Pooled{key: key, pool: pool}
    }

}

impl AsRef<str> for Pooled {
    fn as_ref<'u>(&'u self) -> &'u str {
        unsafe { (*self.pool).map[&self.key].as_ref() }
    }
}

impl PackFormat for Pooled {
    fn pack(&self) -> Symbol {
        let mut val: [u64; 2] = [0; 2];
        val[0] = self.key  | POOLED as u64;
        val[1] = self.pool as u64;
        Symbol{value: val}
    }
    fn unpack(sym: &Symbol) -> Self {
        Pooled{key: sym.value[0],
               pool: unsafe { std::mem::transmute(sym.value[1] as *const Pool) }}
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        panic_unless!(sym.value[0] & TAG_MASK as u64 == POOLED as u64,
                      "Invalid flag bit for pooled symbol");
        unsafe { std::mem::transmute::<_,&'t Pool>(sym.value[1] as *const Pool) }.
            map[sym.value[0]].as_slice()
    }
}

impl std::cmp::Eq for Pooled {}
impl std::cmp::PartialEq for Pooled {
    fn eq(&self, other: &Self) -> bool {
        self.key == other.key && self.pool as u64 == other.pool as u64
    }
}

/* ****************************************************************
 * Types
 */
const TAG_MASK: u8 = 0x01;
const POOLED: u8 = 0;
const INLINE: u8 = 1;

pub enum Type { INLINE = 0, POOLED = 1 }

impl std::cmp::Eq for Type {}
impl std::cmp::PartialEq for Type {
    fn eq(&self, other: &Self) -> bool {
        match *self { Type::INLINE => match *other { Type::INLINE => true, _ => false },
                      Type::POOLED => match *other { Type::POOLED => true, _ => false } }
    }
}

impl std::fmt::Debug for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self { Type::INLINE => write!(f, "inline"),
                      Type::POOLED => write!(f, "pooled") }
    }
}

/* ****************************************************************
 * Unpacked
 */

/// A differentiated `Symbol`.
#[derive(Debug)]
pub enum Unpacked {
    /// Symbol stored inline in `Symbol`'s data fields.
    Inline(Inline),

    /// Symbol stored in a Pool
    Pooled(Pooled)
}

impl PackFormat for Unpacked {
    fn pack(&self) -> Symbol {
        match *self {
            Unpacked::Inline(ref x) => x.pack(),
            Unpacked::Pooled(ref x) => x.pack()
        }
    }

    fn unpack(sym: &Symbol) -> Unpacked {
        match sym.type_of() {
            Type::POOLED => Unpacked::Pooled(unsafe { <Pooled as PackFormat>::unpack(sym) }),
            Type::INLINE => Unpacked::Inline(unsafe { <Inline as PackFormat>::unpack(sym) }),
        }
    }

    fn as_slice_from<'t>(sym: &'t Symbol) -> &'t str {
        match sym.type_of() {
            Type::INLINE => unsafe { <Inline as PackFormat>::as_slice_from(sym) },
            Type::POOLED => unsafe { <Pooled as PackFormat>::as_slice_from(sym) }
        }
    }
}

impl AsRef<str> for Unpacked {
    fn as_ref<'t>(&'t self) -> &'t str {
        match *self {
            Unpacked::Inline(ref x) => x.as_ref(),
            Unpacked::Pooled(ref x) => x.as_ref()
        }
    }
}

impl std::cmp::Eq for Unpacked {}
impl std::cmp::PartialEq for Unpacked {
    fn eq(&self, other: &Self) -> bool {
        match *self {
            Unpacked::Inline(ref si) =>
                match *other { Unpacked::Inline(ref oi) => si == oi, _ => false },
            Unpacked::Pooled(ref sp) =>
                match *other { Unpacked::Pooled(ref op) => sp == op, _ => false }
        }
    }
}

impl Unpacked {
}
<|endoftext|>


<commit_before>use {Errno, Error, Result};
use libc::c_int;
use void::Void;
use std::mem::drop;

#[allow(overflowing_literals)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RebootMode {
    Halt = 0xcdef0123,
    kexec = 0x45584543,
    PowerOff = 0x4321fedc,
    Restart = 0x1234567,
    // we do not support Restart2,
    Suspend = 0xd000fce1,
}

pub fn reboot(how: RebootMode) -> Result<Void> {
    unsafe {
        ext::reboot(how as c_int)
    };
    Err(Error::Sys(Errno::last()))
}

/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
///
/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
#[allow(overflowing_literals)]
pub fn set_cad_enabled(enable: bool) -> Result<()> {
    let res = unsafe {
        ext::reboot(if enable { 0x89abcdef } else { 0 })
    };
    Errno::result(res).map(drop)
}

mod ext {
    use libc::c_int;
    extern {
        pub fn reboot(cmd: c_int) -> c_int;
    }
}
<commit_msg>Use libc's declarations<commit_after>use {Errno, Error, Result};
use libc;
use void::Void;
use std::mem::drop;

#[repr(i32)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RebootMode {
    Halt = libc::RB_HALT_SYSTEM,
    kexec = libc::RB_KEXEC,
    PowerOff = libc::RB_POWER_OFF,
    Restart = libc::RB_AUTOBOOT,
    // we do not support Restart2,
    Suspend = libc::RB_SW_SUSPEND,
}

pub fn reboot(how: RebootMode) -> Result<Void> {
    unsafe {
        libc::reboot(how as libc::c_int)
    };
    Err(Error::Sys(Errno::last()))
}

/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
///
/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
pub fn set_cad_enabled(enable: bool) -> Result<()> {
    let cmd = if enable {
        libc::RB_ENABLE_CAD
    } else {
        libc::RB_DISABLE_CAD
    };
    let res = unsafe {
        libc::reboot(cmd)
    };
    Errno::result(res).map(drop)
}
<|endoftext|>


<commit_before>use super::StockPrice;
use net::http::tls_client;

use hyper::header::Connection;
use select::document::Document;
use select::node::Node;
use select::predicate::{Attr, Class, Element, Name, Predicate, Text};
use serde_urlencoded;
use std::io::Read;

static BASE_URL: &'static str = "https://www.comdirect.de/inf/search/all.html";

error_chain! {
    errors {
        ComdirectScrapingFailed(info: String) {
            description("Did not find information in HTML document")
            display("Did not find {} in HTML document", info)
        }
        ComdirectSearchResultNotUnique {
            description("The search did not returned an unique result")
            display("The search did not returned an unique result")
        }
    }
}

pub fn scrape_stock_price<T: FnMut(String) -> ()>(query: &str, mut progress: Option<T>) -> Result<StockPrice> {
    let parameters = &[("SEARCH_VALUE", query.to_owned())];
    let parameters_enc = serde_urlencoded::to_string(&parameters)
        .chain_err(|| "Could not encode URL parameters")?;
    let url = format!("{}?{}", BASE_URL, parameters_enc);

    if let Some(p) = progress.as_mut() {
        p("Sending search request ...".to_owned());
    }

    let body = get_stock_page(&url).unwrap();

    if let Some(p) = progress.as_mut() {
        p(format!("Received {} bytes", body.len()));
    }

    let stock_price = parse_stock_price(&body)?;

    Ok(stock_price)
}

fn get_stock_page(url: &str) -> Result<Vec<u8>> {
    let client = tls_client().chain_err(|| "Could not create TLS client")?;
    let mut response = client.get(url).header(Connection::close()).send()
        .chain_err(|| "Could not send request")?;

    let mut body = Vec::new();
    let _ = response.read_to_end(&mut body)
        .chain_err(|| "Could not read response body")?;

    Ok(body)
}

// /html/body/div[4]/div/div[1]/div[2]/div[1]/div/div/div[1]/div/span[2]
// body > div.cif-scope-content-wrapper.siteFrame > div > div.key-focus > div:nth-child(2) > div.col-3.col--sm-4 > div > div > div:nth-child(1) > div > span.realtime-indicator--value.text-size--xxlarge.text-weight--medium
fn parse_stock_price(body: &[u8]) -> Result<StockPrice> {
    let document = Document::from_read(body).chain_err(|| "Could not parse HTML in response body")?;

    let no_exact_match = document.find(Class("Informer")).nth(0).is_some();
    if no_exact_match {
        bail!(ErrorKind::ComdirectSearchResultNotUnique);
    }

    let name = document.find(Name("h1")).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("name".to_string()))?.text();

    let price_str = document.find(Class("key-focus__quote").descendant(Class("realtime-indicator").descendant(Text))).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price".to_string()))?
        .text()
        .trim()
        .replace(".", "") // thousand separation
        .replace(",", ".") // decimal separation
    ;
    eprintln!("price_str = {:?}", price_str);
    eprintln!("Mööp");
    let price = price_str
        .parse()
        .chain_err(|| ErrorKind::ComdirectScrapingFailed("parsing stock price".to_string()))?;

    let currency = document.find(Class("realtime-indicator").descendant(Text)).nth(1)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock currency".to_string()))?.text();

    let date_fn = |node: &Node| node.text() == "Stand";
    let date_text = document.find(Class("key-focus").descendant(date_fn)).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (1)".to_string()))?
        .parent()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (2)".to_string()))?
        .find(Element).nth(1)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (3)".to_string()))?
        .first_child()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (4)".to_string()))?
        .text();
    let date = date_text
        .split('-')
        .nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (5)".to_string()))?
        .trim();

    let wkn = document.find(Class("key-focus__info")).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock WKN (1)".to_string()))?
        .last_child()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock WKN (2)".to_string()))?
        .text();

    let stock_price = StockPrice {
        name: name.trim().to_string(),
        wkn: wkn.trim().to_string(),
        date: date.trim().to_string(),
        price,
        currency: currency.trim().to_string(),
    };

    Ok(stock_price)
}

#[cfg(test)]
mod test {
    use super::*;
    use std::io::Read;
    use std::fs::File;

    fn get_stock_page(file: &str) -> Result<Vec<u8>> {
        let mut f = File::open(file).unwrap();
        let mut buffer = Vec::new();
        f.read_to_end(&mut buffer).unwrap();

        Ok(buffer)
    }

    #[test]
    fn test_parse_file_ok() {
        let body = get_stock_page("test/data/stocks/deutsche_bank.html").unwrap();
        let db = parse_stock_price(&body).unwrap();

        assert_eq!(db.name, "DEUTSCHE BANK");
        assert_eq!(db.wkn, "514000");
        assert_eq!(db.date, "27.10.17");
        assert_eq!(db.price, 14.16f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_file_with_thousand_separator() {
        let body = get_stock_page("test/data/stocks/amunid_thousand_period.html").unwrap();
        let db = parse_stock_price(&body).unwrap();

        assert_eq!(db.name, "AMUNDI ETF LEVERAGED MSCI USA DAILY UCITS ETF - EUR ACC");
        assert_eq!(db.wkn, "A0X8ZS");
        assert_eq!(db.date, "27.10.17");
        assert_eq!(db.price, 1396.47f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_file_no_exact_match() {
        let body = get_stock_page("test/data/stocks/no_exact_match.html").unwrap();
        let db = parse_stock_price(&body);

        assert!(db.is_err());
    }

    #[test]
    fn test_parse_online_ok() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let db = scrape_stock_price("Deutsche Bank", None::<fn(_)>).unwrap();

        assert_eq!(db.name, "DEUTSCHE BANK");
        assert_eq!(db.wkn, "514000");
        assert!(db.price > 0.00f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_online_with_thousand_separator() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let db = scrape_stock_price("A0X8ZS", None::<fn(_)>).unwrap();

        assert_eq!(db.name, "AMUNDI ETF LEVERAGED MSCI USA DAILY UCITS ETF - EUR ACC");
        assert_eq!(db.wkn, "A0X8ZS");
        assert!(db.price > 0.00_f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_online_no_exact_match() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let result = scrape_stock_price("Deutsche", None::<fn(_)>);

        // TODO: This needs to be nicer.
        let result_is_not_unique = match result.unwrap_err() {
            Error(ErrorKind::ComdirectSearchResultNotUnique, _) => true,
            _ => false
        };
        assert!(result_is_not_unique);
    }
}
<commit_msg>Remove one unused import<commit_after>use super::StockPrice;
use net::http::tls_client;

use hyper::header::Connection;
use select::document::Document;
use select::node::Node;
use select::predicate::{Class, Element, Name, Predicate, Text};
use serde_urlencoded;
use std::io::Read;

static BASE_URL: &'static str = "https://www.comdirect.de/inf/search/all.html";

error_chain! {
    errors {
        ComdirectScrapingFailed(info: String) {
            description("Did not find information in HTML document")
            display("Did not find {} in HTML document", info)
        }
        ComdirectSearchResultNotUnique {
            description("The search did not returned an unique result")
            display("The search did not returned an unique result")
        }
    }
}

pub fn scrape_stock_price<T: FnMut(String) -> ()>(query: &str, mut progress: Option<T>) -> Result<StockPrice> {
    let parameters = &[("SEARCH_VALUE", query.to_owned())];
    let parameters_enc = serde_urlencoded::to_string(&parameters)
        .chain_err(|| "Could not encode URL parameters")?;
    let url = format!("{}?{}", BASE_URL, parameters_enc);

    if let Some(p) = progress.as_mut() {
        p("Sending search request ...".to_owned());
    }

    let body = get_stock_page(&url).unwrap();

    if let Some(p) = progress.as_mut() {
        p(format!("Received {} bytes", body.len()));
    }

    let stock_price = parse_stock_price(&body)?;

    Ok(stock_price)
}

fn get_stock_page(url: &str) -> Result<Vec<u8>> {
    let client = tls_client().chain_err(|| "Could not create TLS client")?;
    let mut response = client.get(url).header(Connection::close()).send()
        .chain_err(|| "Could not send request")?;

    let mut body = Vec::new();
    let _ = response.read_to_end(&mut body)
        .chain_err(|| "Could not read response body")?;

    Ok(body)
}

// /html/body/div[4]/div/div[1]/div[2]/div[1]/div/div/div[1]/div/span[2]
// body > div.cif-scope-content-wrapper.siteFrame > div > div.key-focus > div:nth-child(2) > div.col-3.col--sm-4 > div > div > div:nth-child(1) > div > span.realtime-indicator--value.text-size--xxlarge.text-weight--medium
fn parse_stock_price(body: &[u8]) -> Result<StockPrice> {
    let document = Document::from_read(body).chain_err(|| "Could not parse HTML in response body")?;

    let no_exact_match = document.find(Class("Informer")).nth(0).is_some();
    if no_exact_match {
        bail!(ErrorKind::ComdirectSearchResultNotUnique);
    }

    let name = document.find(Name("h1")).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("name".to_string()))?.text();

    let price_str = document.find(Class("key-focus__quote").descendant(Class("realtime-indicator").descendant(Text))).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price".to_string()))?
        .text()
        .trim()
        .replace(".", "") // thousand separation
        .replace(",", ".") // decimal separation
    ;
    eprintln!("price_str = {:?}", price_str);
    eprintln!("Mööp");
    let price = price_str
        .parse()
        .chain_err(|| ErrorKind::ComdirectScrapingFailed("parsing stock price".to_string()))?;

    let currency = document.find(Class("realtime-indicator").descendant(Text)).nth(1)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock currency".to_string()))?.text();

    let date_fn = |node: &Node| node.text() == "Stand";
    let date_text = document.find(Class("key-focus").descendant(date_fn)).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (1)".to_string()))?
        .parent()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (2)".to_string()))?
        .find(Element).nth(1)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (3)".to_string()))?
        .first_child()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (4)".to_string()))?
        .text();
    let date = date_text
        .split('-')
        .nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (5)".to_string()))?
        .trim();

    let wkn = document.find(Class("key-focus__info")).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock WKN (1)".to_string()))?
        .last_child()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock WKN (2)".to_string()))?
        .text();

    let stock_price = StockPrice {
        name: name.trim().to_string(),
        wkn: wkn.trim().to_string(),
        date: date.trim().to_string(),
        price,
        currency: currency.trim().to_string(),
    };

    Ok(stock_price)
}

#[cfg(test)]
mod test {
    use super::*;
    use std::io::Read;
    use std::fs::File;

    fn get_stock_page(file: &str) -> Result<Vec<u8>> {
        let mut f = File::open(file).unwrap();
        let mut buffer = Vec::new();
        f.read_to_end(&mut buffer).unwrap();

        Ok(buffer)
    }

    #[test]
    fn test_parse_file_ok() {
        let body = get_stock_page("test/data/stocks/deutsche_bank.html").unwrap();
        let db = parse_stock_price(&body).unwrap();

        assert_eq!(db.name, "DEUTSCHE BANK");
        assert_eq!(db.wkn, "514000");
        assert_eq!(db.date, "27.10.17");
        assert_eq!(db.price, 14.16f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_file_with_thousand_separator() {
        let body = get_stock_page("test/data/stocks/amunid_thousand_period.html").unwrap();
        let db = parse_stock_price(&body).unwrap();

        assert_eq!(db.name, "AMUNDI ETF LEVERAGED MSCI USA DAILY UCITS ETF - EUR ACC");
        assert_eq!(db.wkn, "A0X8ZS");
        assert_eq!(db.date, "27.10.17");
        assert_eq!(db.price, 1396.47f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_file_no_exact_match() {
        let body = get_stock_page("test/data/stocks/no_exact_match.html").unwrap();
        let db = parse_stock_price(&body);

        assert!(db.is_err());
    }

    #[test]
    fn test_parse_online_ok() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let db = scrape_stock_price("Deutsche Bank", None::<fn(_)>).unwrap();

        assert_eq!(db.name, "DEUTSCHE BANK");
        assert_eq!(db.wkn, "514000");
        assert!(db.price > 0.00f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_online_with_thousand_separator() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let db = scrape_stock_price("A0X8ZS", None::<fn(_)>).unwrap();

        assert_eq!(db.name, "AMUNDI ETF LEVERAGED MSCI USA DAILY UCITS ETF - EUR ACC");
        assert_eq!(db.wkn, "A0X8ZS");
        assert!(db.price > 0.00_f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_online_no_exact_match() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let result = scrape_stock_price("Deutsche", None::<fn(_)>);

        // TODO: This needs to be nicer.
        let result_is_not_unique = match result.unwrap_err() {
            Error(ErrorKind::ComdirectSearchResultNotUnique, _) => true,
            _ => false
        };
        assert!(result_is_not_unique);
    }
}
<|endoftext|>


<commit_before>// Kept private because MemoryBlocks cannot be created outside of this static allocator
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Id(usize);

/// Size of a memory block in cells
/// One cell is typically equivalent to a byte, however that depends on the implementation of
/// the brainfuck interpreter being used. This definition of size is interpreter-agnostic.
pub type Size = usize;

/// Index of a position within a MemoryBlocks, must be within the allocated size of the MemoryBlock
/// to prevent buffer overrun
/// Indexes start at zero
pub type Index = usize;

/// MemoryBlock of the first cell in a memory block of the given size
/// Use CellPositions to represent locations within a memory block pointed to by an MemoryBlock
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct MemoryBlock {
    id: Id,
    size: Size,
}

impl MemoryBlock {
    /// Returns the size of this memory block
    pub fn size(&self) -> Size {
        self.size
    }

    /// Returns the cell position of the first cell within this MemoryBlock
    pub fn position(&self) -> CellPosition {
        CellPosition(self.id, 0)
    }

    /// Returns the position of the cell at the given index within this MemoryBlock
    ///
    /// # Panics
    /// If the index is out of bounds, this method will panic. This is meant to catch compiler
    /// errors early. If the usage of this method is well designed, the panic should never happen
    /// Only panics when compiled in debug mode
    pub fn position_at(&self, index: Index) -> CellPosition {
        debug_assert!(index < self.size,
            "Attempt to access a position outside of the memory allocated for a MemoryBlock");

        CellPosition(self.id, index)
    }
}

/// The position of a cell within an MemoryBlock
/// Index = 0 indicates
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct CellPosition(Id, Index);

pub struct StaticAllocator {
    next_id: usize,
}

impl StaticAllocator {
    pub fn new() -> StaticAllocator {
        StaticAllocator {
            next_id: 0,
        }
    }

    /// Allocates a memory block of the given size and gives it a unique ID
    /// so that this memory block can be referred to uniquely
    pub fn allocate(&mut self, size: Size) -> MemoryBlock {
        let blk = MemoryBlock {
            id: Id(self.next_id),
            size: size,
        };

        self.next_id += 1;
        blk
    }
}
<commit_msg>Completing comment that was left unfinished<commit_after>// Kept private because MemoryBlocks cannot be created outside of this static allocator
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Id(usize);

/// Size of a memory block in cells
/// One cell is typically equivalent to a byte, however that depends on the implementation of
/// the brainfuck interpreter being used. This definition of size is interpreter-agnostic.
pub type Size = usize;

/// Index of a position within a MemoryBlocks, must be within the allocated size of the MemoryBlock
/// to prevent buffer overrun
/// Indexes start at zero
pub type Index = usize;

/// MemoryBlock of the first cell in a memory block of the given size
/// Use CellPositions to represent locations within a memory block pointed to by an MemoryBlock
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct MemoryBlock {
    id: Id,
    size: Size,
}

impl MemoryBlock {
    /// Returns the size of this memory block
    pub fn size(&self) -> Size {
        self.size
    }

    /// Returns the cell position of the first cell within this MemoryBlock
    pub fn position(&self) -> CellPosition {
        CellPosition(self.id, 0)
    }

    /// Returns the position of the cell at the given index within this MemoryBlock
    ///
    /// # Panics
    /// If the index is out of bounds, this method will panic. This is meant to catch compiler
    /// errors early. If the usage of this method is well designed, the panic should never happen
    /// Only panics when compiled in debug mode
    pub fn position_at(&self, index: Index) -> CellPosition {
        debug_assert!(index < self.size,
            "Attempt to access a position outside of the memory allocated for a MemoryBlock");

        CellPosition(self.id, index)
    }
}

/// The position of a cell within an MemoryBlock
/// Index = 0 indicates the start of the memory block
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct CellPosition(Id, Index);

pub struct StaticAllocator {
    next_id: usize,
}

impl StaticAllocator {
    pub fn new() -> StaticAllocator {
        StaticAllocator {
            next_id: 0,
        }
    }

    /// Allocates a memory block of the given size and gives it a unique ID
    /// so that this memory block can be referred to uniquely
    pub fn allocate(&mut self, size: Size) -> MemoryBlock {
        let blk = MemoryBlock {
            id: Id(self.next_id),
            size: size,
        };

        self.next_id += 1;
        blk
    }
}
<|endoftext|>


<commit_before>use conduit::{Handler, Method};

use cargo_registry::Model;
use cargo_registry::db::RequestTransaction;
use cargo_registry::krate::EncodableCrate;
use cargo_registry::user::{User, NewUser, EncodableUser};
use cargo_registry::version::EncodableVersion;

#[derive(RustcDecodable)]
struct AuthResponse { url: String, state: String }
#[derive(RustcDecodable)]
struct MeResponse { user: EncodableUser, api_token: String }
#[derive(RustcDecodable)]
struct UserShowResponse { user: EncodableUser }

#[test]
fn auth_gives_a_token() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/authorize_url");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: AuthResponse = ::json(&mut response);
    assert!(json.url.contains(&json.state));
}

#[test]
fn access_token_needs_data() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/authorize");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: ::Bad = ::json(&mut response);
    assert!(json.errors[0].detail.contains("invalid state"));
}

#[test]
fn user_insert() {
    let (_b, app, _middle) = ::app();
    let conn = t!(app.database.get());
    let tx = t!(conn.transaction());

    let user = t!(User::find_or_insert(&tx, 1, "foo", None, None, None, "bar"));
    assert_eq!(t!(User::find_by_api_token(&tx, &user.api_token)), user);
    assert_eq!(t!(User::find(&tx, user.id)), user);

    assert_eq!(t!(User::find_or_insert(&tx, 1, "foo", None, None, None,
                                       "bar")), user);
    let user2 = t!(User::find_or_insert(&tx, 1, "foo", None, None, None,
                                        "baz"));
    assert!(user != user2);
    assert_eq!(user.id, user2.id);
    assert_eq!(user2.gh_access_token, "baz");

    let user3 = t!(User::find_or_insert(&tx, 1, "bar", None, None, None,
                                        "baz"));
    assert!(user != user3);
    assert_eq!(user.id, user3.id);
    assert_eq!(user3.gh_login, "bar");
}

#[test]
fn me() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/me");
    let response = t_resp!(middle.call(&mut req));
    assert_eq!(response.status.0, 403);

    let user = ::mock_user(&mut req, ::user("foo"));
    let mut response = ok_resp!(middle.call(&mut req));
    let json: MeResponse = ::json(&mut response);
    assert_eq!(json.user.email, user.email);
    assert_eq!(json.api_token, user.api_token);
}

#[test]
fn show() {
    let (_b, app, middle) = ::app();
    {
        let conn = t!(app.diesel_database.get());

        t!(NewUser::new(1, "foo", Some("foo@bar.com"), None, None, "bar").create_or_update(&conn));
        t!(NewUser::new(2, "bar", Some("bar@baz.com"), None, None, "bar").create_or_update(&conn));
    }

    let mut req = ::req(app.clone(), Method::Get, "/api/v1/users/foo");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: UserShowResponse = ::json(&mut response);
    assert_eq!(Some("foo@bar.com".into()), json.user.email);
    assert_eq!("foo", json.user.login);

    let mut response = ok_resp!(middle.call(req.with_path("/api/v1/users/bar")));
    let json: UserShowResponse = ::json(&mut response);
    assert_eq!(Some("bar@baz.com".into()), json.user.email);
    assert_eq!("bar", json.user.login);
}

#[test]
fn reset_token() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Put, "/me/reset_token");
    let user = User::find_or_insert(req.tx().unwrap(), 1, "foo", None,
                                    None, None, "bar").unwrap();
    ::sign_in_as(&mut req, &user);
    ok_resp!(middle.call(&mut req));

    let u2 = User::find(req.tx().unwrap(), user.id).unwrap();
    assert!(u2.api_token != user.api_token);
}

#[test]
fn crates_by_user_id() {
    let (_b, app, middle) = ::app();
    let u;
    {
        let conn = app.diesel_database.get().unwrap();
        u = ::new_user("foo")
            .create_or_update(&conn)
            .unwrap();
        ::CrateBuilder::new("foo_my_packages", u.id)
            .expect_build(&conn);
    }

    let mut req = ::req(app, Method::Get, "/api/v1/crates");
    req.with_query(&format!("user_id={}", u.id));
    let mut response = ok_resp!(middle.call(&mut req));

    #[derive(RustcDecodable)]
    struct Response { crates: Vec<EncodableCrate> }
    let response: Response = ::json(&mut response);
    assert_eq!(response.crates.len(), 1);
}

#[test]
fn following() {
    #[derive(RustcDecodable)]
    struct R {
        versions: Vec<EncodableVersion>,
        meta: Meta,
    }
    #[derive(RustcDecodable)] struct Meta { more: bool }

    let (_b, app, middle) = ::app();
    let mut req = ::req(app.clone(), Method::Get, "/");
    {
        let conn = app.diesel_database.get().unwrap();
        let user = ::new_user("foo").create_or_update(&conn).unwrap();
        ::sign_in_as(&mut req, &user);

        ::CrateBuilder::new("foo_fighters", user.id)
            .version("1.0.0")
            .expect_build(&conn);

        ::CrateBuilder::new("bar_fighters", user.id)
            .version("1.0.0")
            .expect_build(&conn);
    }

    let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
                                               .with_method(Method::Get)));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 0);
    assert_eq!(r.meta.more, false);

    ok_resp!(middle.call(req.with_path("/api/v1/crates/foo_fighters/follow")
                            .with_method(Method::Put)));
    ok_resp!(middle.call(req.with_path("/api/v1/crates/bar_fighters/follow")
                            .with_method(Method::Put)));

    let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
                                               .with_method(Method::Get)));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 2);
    assert_eq!(r.meta.more, false);

    let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
                                               .with_method(Method::Get)
                                               .with_query("per_page=1")));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 1);
    assert_eq!(r.meta.more, true);

    ok_resp!(middle.call(req.with_path("/api/v1/crates/bar_fighters/follow")
                            .with_method(Method::Delete)));
    let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
                                               .with_method(Method::Get)
                                               .with_query("page=2&per_page=1")));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 0);
    assert_eq!(r.meta.more, false);

    bad_resp!(middle.call(req.with_query("page=0")));
}
<commit_msg>Add a test for github URL always being present on user show<commit_after>use conduit::{Handler, Method};

use cargo_registry::Model;
use cargo_registry::db::RequestTransaction;
use cargo_registry::krate::EncodableCrate;
use cargo_registry::user::{User, NewUser, EncodableUser};
use cargo_registry::version::EncodableVersion;

#[derive(RustcDecodable)]
struct AuthResponse { url: String, state: String }
#[derive(RustcDecodable)]
struct MeResponse { user: EncodableUser, api_token: String }
#[derive(RustcDecodable)]
struct UserShowResponse { user: EncodableUser }

#[test]
fn auth_gives_a_token() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/authorize_url");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: AuthResponse = ::json(&mut response);
    assert!(json.url.contains(&json.state));
}

#[test]
fn access_token_needs_data() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/authorize");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: ::Bad = ::json(&mut response);
    assert!(json.errors[0].detail.contains("invalid state"));
}

#[test]
fn user_insert() {
    let (_b, app, _middle) = ::app();
    let conn = t!(app.database.get());
    let tx = t!(conn.transaction());

    let user = t!(User::find_or_insert(&tx, 1, "foo", None, None, None, "bar"));
    assert_eq!(t!(User::find_by_api_token(&tx, &user.api_token)), user);
    assert_eq!(t!(User::find(&tx, user.id)), user);

    assert_eq!(t!(User::find_or_insert(&tx, 1, "foo", None, None, None,
                                       "bar")), user);
    let user2 = t!(User::find_or_insert(&tx, 1, "foo", None, None, None,
                                        "baz"));
    assert!(user != user2);
    assert_eq!(user.id, user2.id);
    assert_eq!(user2.gh_access_token, "baz");

    let user3 = t!(User::find_or_insert(&tx, 1, "bar", None, None, None,
                                        "baz"));
    assert!(user != user3);
    assert_eq!(user.id, user3.id);
    assert_eq!(user3.gh_login, "bar");
}

#[test]
fn me() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Get, "/me");
    let response = t_resp!(middle.call(&mut req));
    assert_eq!(response.status.0, 403);

    let user = ::mock_user(&mut req, ::user("foo"));
    let mut response = ok_resp!(middle.call(&mut req));
    let json: MeResponse = ::json(&mut response);
    assert_eq!(json.user.email, user.email);
    assert_eq!(json.api_token, user.api_token);
}

#[test]
fn show() {
    let (_b, app, middle) = ::app();
    {
        let conn = t!(app.diesel_database.get());

        t!(NewUser::new(1, "foo", Some("foo@bar.com"), None, None, "bar").create_or_update(&conn));
        t!(NewUser::new(2, "bar", Some("bar@baz.com"), None, None, "bar").create_or_update(&conn));
    }

    let mut req = ::req(app.clone(), Method::Get, "/api/v1/users/foo");
    let mut response = ok_resp!(middle.call(&mut req));
    let json: UserShowResponse = ::json(&mut response);
    assert_eq!(Some("foo@bar.com".into()), json.user.email);
    assert_eq!("foo", json.user.login);

    let mut response = ok_resp!(middle.call(req.with_path("/api/v1/users/bar")));
    let json: UserShowResponse = ::json(&mut response);
    assert_eq!(Some("bar@baz.com".into()), json.user.email);
    assert_eq!("bar", json.user.login);
    assert_eq!(Some("https://github.com/bar".into()), json.user.url);
}

#[test]
fn reset_token() {
    let (_b, app, middle) = ::app();
    let mut req = ::req(app, Method::Put, "/me/reset_token");
    let user = User::find_or_insert(req.tx().unwrap(), 1, "foo", None,
                                    None, None, "bar").unwrap();
    ::sign_in_as(&mut req, &user);
    ok_resp!(middle.call(&mut req));

    let u2 = User::find(req.tx().unwrap(), user.id).unwrap();
    assert!(u2.api_token != user.api_token);
}

#[test]
fn crates_by_user_id() {
    let (_b, app, middle) = ::app();
    let u;
    {
        let conn = app.diesel_database.get().unwrap();
        u = ::new_user("foo")
            .create_or_update(&conn)
            .unwrap();
        ::CrateBuilder::new("foo_my_packages", u.id)
            .expect_build(&conn);
    }

    let mut req = ::req(app, Method::Get, "/api/v1/crates");
    req.with_query(&format!("user_id={}", u.id));
    let mut response = ok_resp!(middle.call(&mut req));

    #[derive(RustcDecodable)]
    struct Response { crates: Vec<EncodableCrate> }
    let response: Response = ::json(&mut response);
    assert_eq!(response.crates.len(), 1);
}

#[test]
fn following() {
    #[derive(RustcDecodable)]
    struct R {
        versions: Vec<EncodableVersion>,
        meta: Meta,
    }
    #[derive(RustcDecodable)] struct Meta { more: bool }

    let (_b, app, middle) = ::app();
    let mut req = ::req(app.clone(), Method::Get, "/");
    {
        let conn = app.diesel_database.get().unwrap();
        let user = ::new_user("foo").create_or_update(&conn).unwrap();
        ::sign_in_as(&mut req, &user);

        ::CrateBuilder::new("foo_fighters", user.id)
            .version("1.0.0")
            .expect_build(&conn);

        ::CrateBuilder::new("bar_fighters", user.id)
            .version("1.0.0")
            .expect_build(&conn);
    }

    let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
                                               .with_method(Method::Get)));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 0);
    assert_eq!(r.meta.more, false);

    ok_resp!(middle.call(req.with_path("/api/v1/crates/foo_fighters/follow")
                            .with_method(Method::Put)));
    ok_resp!(middle.call(req.with_path("/api/v1/crates/bar_fighters/follow")
                            .with_method(Method::Put)));

    let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
                                               .with_method(Method::Get)));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 2);
    assert_eq!(r.meta.more, false);

    let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
                                               .with_method(Method::Get)
                                               .with_query("per_page=1")));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 1);
    assert_eq!(r.meta.more, true);

    ok_resp!(middle.call(req.with_path("/api/v1/crates/bar_fighters/follow")
                            .with_method(Method::Delete)));
    let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
                                               .with_method(Method::Get)
                                               .with_query("page=2&per_page=1")));
    let r = ::json::<R>(&mut response);
    assert_eq!(r.versions.len(), 0);
    assert_eq!(r.meta.more, false);

    bad_resp!(middle.call(req.with_query("page=0")));
}
<|endoftext|>


<commit_before><commit_msg>Uninstallation<commit_after>use std::error::Error;
use std::fs;
use std::path::Path;

use package::find;
use operation::selection;
use paths::{ self, PathAppend };

pub fn uninstall(input: &str, base_path: &Path) -> Result<bool, String> {
    let package_list_dir = get!(fs::read_dir(base_path.to_path_buf().append(paths::PACKAGE_LIST_DIR)), "");
    for list_path in package_list_dir {
        let result = try!(find::select_package(input, list_path.unwrap().path().as_path(), selection::select_package));
        let installation_path = base_path.to_path_buf().append(&(paths::PACKAGE_INSTALL_DIR.to_string() + &result.name + "/"));
        if installation_path.exists() {
            match fs::remove_dir_all(&installation_path) {
                Ok(x) => x,
                Err(why) => return Err(why.description().to_string())
            }
            return Ok(true)
        } else {
            continue;
        }
    }
    return Ok(false);
}
<|endoftext|>