<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 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 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),
    /// 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),
}

impl FromJson for StreamMessage {
    fn from_json(input: &json::Json) -> Result<Self, error::Error> {
        if let Some(_event) = input.find("event") {
            //TODO: all the event types -_-
            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>half the event types<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 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)
}
<|endoftext|>


<commit_before>use std::fmt::{self, Display, Debug};
use std::error::Error;
use std::any::Any;
use std::cell::Cell;
use std::io::Read;
use std::io;
use nom;

#[allow(dead_code)]
pub const DEFAULT_BUF_SIZE: usize = 256 * 1024;

pub trait StreamBuf<O> {
    fn fill(&mut self, min_count: usize) -> Result<(), FillError>;
    //TODO: rename to relocate
    fn recycle(&mut self);
    fn consume(&mut self, count: usize);
    fn data<'b>(&'b self) -> &'b[O];
    fn needed(&self) -> Option<nom::Needed>;
    fn apply<'b, C, CO, E>(&'b self, combinator: C) -> Result<Option<CO>, nom::Err<&'b [O], E>> where
        O: 'b, C: Fn(&'b [O]) -> nom::IResult<&'b [O], CO, E>;

    fn fill_apply<'b, C, CO, E>(&'b mut self, combinator: C) -> Result<Option<CO>, FillApplyError<&'b [O], E>> where
        C: Fn(&'b [O]) -> nom::IResult<&'b [O], CO, E> {

        match self.needed() {
            Some(nom::Needed::Size(bytes)) => try!(self.fill(bytes)),
            Some(nom::Needed::Unknown) => try!(self.fill(1)),
            None => ()
        }

        Ok(try!(self.apply(combinator)))
    }
}

#[derive(Debug)]
pub enum FillError {
    Io(io::Error),
    BufferOverflow(usize, usize),
}

impl From<io::Error> for FillError {
    fn from(e: io::Error) -> FillError {
        FillError::Io(e)
    }
}

impl Display for FillError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &FillError::Io(ref e) => write!(f, "Failed to read data: {}", e),
            &FillError::BufferOverflow(needed, capacity) => write!(f, "Cannot fill buffer of size {} bytes with {} bytes of data", capacity, needed),
        }
    }
}

impl Error for FillError {
    fn description(&self) -> &str {
        match self {
            &FillError::Io(_) => "I/O error",
            &FillError::BufferOverflow(_, _) => "buffer overflow",
        }
    }
    fn cause(&self) -> Option<&Error> {
        match self {
            &FillError::Io(ref e) => Some(e),
            &FillError::BufferOverflow(_, _) => None,
        }
    }
}

#[derive(Debug)]
pub enum FillApplyError<I, E> {
    Parser(nom::Err<I, E>),
    FillError(FillError),
}

impl<I, E> From<nom::Err<I, E>> for FillApplyError<I, E> {
    fn from(e: nom::Err<I, E>) -> FillApplyError<I, E> {
        FillApplyError::Parser(e)
    }
}

impl<I, E> From<FillError> for FillApplyError<I, E> {
    fn from(e: FillError) -> FillApplyError<I, E> {
        FillApplyError::FillError(e)
    }
}

impl<I, E> Display for FillApplyError<I, E> where I: Debug, E: Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &FillApplyError::Parser(ref e) => write!(f, "Failed to parse data: {}", e),
            &FillApplyError::FillError(ref e) => write!(f, "Failed to fill buffer with amout of data requested by parser: {}", e),
        }
    }
}

impl<I, E> Error for FillApplyError<I, E> where I: Debug + Display + Any, E: Error {
    fn description(&self) -> &str {
        match self {
            &FillApplyError::Parser(_) => "parsing faield",
            &FillApplyError::FillError(_) => "buffer fill error",
        }
    }
    fn cause(&self) -> Option<&Error> {
        match self {
            &FillApplyError::Parser(_) => None, // e contains reference to data
            &FillApplyError::FillError(ref e) => Some(e),
        }
    }
}

pub struct ReadStreamBuf<R: Read> {
    reader: R,
    //TODO: alignment?
    buf: Vec<u8>,
    cap: usize,
    needed: Cell<Option<nom::Needed>>,
    offset: Cell<usize>,
}

impl<R: Read> ReadStreamBuf<R> {
    #[allow(dead_code)]
    pub fn new(reader: R) -> ReadStreamBuf<R> {
        ReadStreamBuf::with_capacity(reader, DEFAULT_BUF_SIZE)
    }

    pub fn with_capacity(reader: R, cap: usize) -> ReadStreamBuf<R> {
        ReadStreamBuf {
            reader: reader,
            buf: Vec::with_capacity(cap),
            cap: cap,
            needed: Cell::new(Some(nom::Needed::Unknown)),
            offset: Cell::new(0),
        }
    }

    #[allow(dead_code)]
    pub fn into_inner(self) -> R {
        self.reader
    }
}

impl<R: Read> StreamBuf<u8> for ReadStreamBuf<R> {
    fn fill(&mut self, min_bytes: usize) -> Result<(), FillError> {
        let len = self.buf.len();
        let have = len - self.offset.get();

        if have >= min_bytes {
            return Ok(())
        }

        let needed = min_bytes - have;

        if min_bytes > self.cap {
            return Err(FillError::BufferOverflow(min_bytes, self.cap))
        }
        if len + needed > self.cap {
            self.recycle();
            assert_eq!(self.offset.get(), 0);
            assert_eq!(self.buf.len(), have);
            return self.fill(min_bytes)
        }

        self.buf.resize(self.cap, 0);
        trace!("reading exactly {} bytes into buf blocking: {}..{} ({}); have: {} will have: {}", needed, len, len + needed, self.buf[len..len + needed].len(), have, have + needed);
        if let Err(err) = self.reader.read_exact(&mut self.buf[len..len + needed]) {
            self.buf.resize(len, 0);
            return Err(From::from(err));
        }

        // Try to read to the end of the buffer if we can
        trace!("reading up to {} extra bytes into buf non blocking", self.cap - (len + needed));
        match self.reader.read(&mut self.buf[len + needed..self.cap]) {
            Err(err) => {
                self.buf.resize(len + needed, 0);
                return Err(From::from(err));
            },
            Ok(bytes_read) => {
                trace!("got extra {} bytes", bytes_read);
                self.buf.resize(len + needed + bytes_read, 0);
            }
        }

        //trace!("buf has: {:?}", self.data());
        trace!("buf has {} bytes", self.data().len());
        Ok(())
    }

    fn recycle(&mut self) {
        if self.offset.get() == 0 {
            return
        }
        self.buf = self.buf.split_off(self.offset.get());
        self.offset.set(0);
        //TODO: expand buffer OR use memcpy - bench test it
    }

    fn consume(&mut self, bytes: usize) {
        let len = self.buf.len();

        let consume = if self.offset.get() + bytes > len {
            len - self.offset.get()
        } else {
            bytes
        };
        self.offset.set(self.offset.get() + consume);
    }

    fn data<'b>(&'b self) -> &'b[u8] {
        &self.buf[self.offset.get()..self.buf.len()]
    }

    fn needed(&self) -> Option<nom::Needed> {
        self.needed.get().clone()
    }

    fn apply<'b, C, CO, E>(&'b self, combinator: C) -> Result<Option<CO>, nom::Err<&'b [u8], E>> where
        C: Fn(&'b [u8]) -> nom::IResult<&'b [u8], CO, E> {
        let data = self.data();
        match combinator(data) {
            nom::IResult::Done(left, out) => {
                let consumed = data.len() - left.len();
                trace!("done: consumed: {}", consumed);

                // Move the offset
                self.offset.set(self.offset.get() + consumed);
                // We don't know how much we will need now
                self.needed.set(None);
                Ok(Some(out))
            },
            nom::IResult::Error(err) => Err(err),
            nom::IResult::Incomplete(needed) => {
                trace!("incomplete: needed: {:?}", needed);
                self.needed.set(Some(needed));
                Ok(None)
            }
        }
    }
}

#[cfg(test)]
mod resd_stream_buf_tests {
    use super::StreamBuf;
    use super::ReadStreamBuf;
    use super::FillError;
    use nom::{IResult, Needed};
    use std::io::Cursor;

    fn subject(data: Vec<u8>) -> ReadStreamBuf<Cursor<Vec<u8>>> {
        ReadStreamBuf::new(Cursor::new(data))
    }

    fn subject_with_default_data() -> ReadStreamBuf<Cursor<Vec<u8>>> {
        subject(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    }

    #[test]
    fn reading() {
        let mut rsb = subject_with_default_data();
        assert!(rsb.data().is_empty());

        // fill reads as much as it can non-blocking
        // TODO: how to test blocking streaming?
        rsb.fill(3).unwrap();
        assert_eq!(rsb.data(), [0, 1, 2, 3, 4, 5, 6, 7, 8 ,9].as_ref());
    }

    #[test]
    fn recycle() {
        let mut rsb = subject_with_default_data();

        //TODO: this test does nothing...
        rsb.fill(10).unwrap();
        rsb.recycle();
        assert_eq!(rsb.data(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_ref());
    }

    #[test]
    fn consume() {
        let mut rsb = subject_with_default_data();

        rsb.fill(10).unwrap();
        rsb.consume(8);
        assert_eq!(rsb.data(), [8, 9].as_ref());
    }

    #[test]
    fn consume_more_than_we_have() {
        let mut rsb = subject_with_default_data();

        rsb.consume(3);
        rsb.fill(10).unwrap();
        assert_eq!(rsb.data(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_ref());
    }

    #[test]
    fn apply_function() {
        use nom::be_u8;
        let mut rsb = subject_with_default_data();

        rsb.fill(1).unwrap();
        assert_eq!(rsb.apply(be_u8).unwrap(), Some(0));
    }

    #[test]
    fn apply_should_consume() {
        use nom::be_u8;
        let mut rsb = subject_with_default_data();

        rsb.fill(1).unwrap();
        assert_eq!(rsb.apply(be_u8).unwrap(), Some(0));
        rsb.fill(1).unwrap();
        assert_eq!(rsb.apply(be_u8).unwrap(), Some(1));
    }

    #[test]
    fn apply_converted_macro() {
        let mut rsb = subject_with_default_data();

        rsb.fill(2).unwrap();
        assert_eq!(rsb.apply(closure!(tag!([0, 1]))).unwrap(), Some([0, 1].as_ref()));
    }

    #[test]
    fn apply_closure() {
        let mut rsb = subject_with_default_data();

        rsb.fill(2).unwrap();
        assert_eq!(rsb.apply(|i| tag!(i, [0, 1])).unwrap(), Some([0, 1].as_ref()));
    }

    #[test]
    fn apply_custom_fuction_with_refs() {
        let mut rsb = subject_with_default_data();
        fn comb(input: &[u8]) -> IResult<&[u8], &[u8]> {
            tag!(input, [0, 1, 2])
        }

        rsb.fill(3).unwrap();
        assert_eq!(rsb.apply(comb).unwrap(), Some([0, 1, 2].as_ref()));
    }

    #[test]
    fn needed_with_apply() {
        let mut rsb = subject_with_default_data();

        fn comb<'a>(input: &'a[u8]) -> IResult<&'a[u8], &'a[u8]> {
            tag!(input, [0, 1, 2])
        }

        assert!(rsb.apply(comb).unwrap().is_none());

        let needed = rsb.needed().unwrap();
        assert_eq!(needed, Needed::Size(3));

        if let Needed::Size(bytes) = needed {
            rsb.fill(bytes).unwrap();
            assert_eq!(rsb.apply(comb).unwrap(), Some([0, 1, 2].as_ref()));
        }
    }

    #[test]
    fn fill_apply() {
        use nom::be_u8;
        let mut rsb = subject_with_default_data();

        fn comb(input: &[u8]) -> IResult<&[u8], &[u8]> {
            tag!(input, [0, 1, 2])
        }

        //TODO: test None scenario
        rsb.fill(10).unwrap();
        assert_eq!(rsb.fill_apply(comb).unwrap(), Some([0, 1, 2].as_ref()));
        assert_eq!(rsb.fill_apply(be_u8).unwrap(), Some(3));
    }

    #[test]
    fn fill_over_buf() {
        let mut rsb = subject_with_default_data();
        let error = rsb.fill(super::DEFAULT_BUF_SIZE + 1).unwrap_err();
        if let FillError::BufferOverflow(needed, capacity) = error {
            assert_eq!(needed, super::DEFAULT_BUF_SIZE + 1);
            assert_eq!(capacity, super::DEFAULT_BUF_SIZE);
        } else {
            panic!("was expecing BufferOverflow error");
        }
    }
}
<commit_msg>avoiding mem alloc on buffer recycle<commit_after>use std::fmt::{self, Display, Debug};
use std::error::Error;
use std::any::Any;
use std::cell::Cell;
use std::io::Read;
use std::io;
use std::ptr::copy;
use nom;

#[allow(dead_code)]
pub const DEFAULT_BUF_SIZE: usize = 256 * 1024;

pub trait StreamBuf<O> {
    fn fill(&mut self, min_count: usize) -> Result<(), FillError>;
    //TODO: rename to relocate
    fn recycle(&mut self);
    fn consume(&mut self, count: usize);
    fn data<'b>(&'b self) -> &'b[O];
    fn needed(&self) -> Option<nom::Needed>;
    fn apply<'b, C, CO, E>(&'b self, combinator: C) -> Result<Option<CO>, nom::Err<&'b [O], E>> where
        O: 'b, C: Fn(&'b [O]) -> nom::IResult<&'b [O], CO, E>;

    fn fill_apply<'b, C, CO, E>(&'b mut self, combinator: C) -> Result<Option<CO>, FillApplyError<&'b [O], E>> where
        C: Fn(&'b [O]) -> nom::IResult<&'b [O], CO, E> {

        match self.needed() {
            Some(nom::Needed::Size(bytes)) => try!(self.fill(bytes)),
            Some(nom::Needed::Unknown) => try!(self.fill(1)),
            None => ()
        }

        Ok(try!(self.apply(combinator)))
    }
}

#[derive(Debug)]
pub enum FillError {
    Io(io::Error),
    BufferOverflow(usize, usize),
}

impl From<io::Error> for FillError {
    fn from(e: io::Error) -> FillError {
        FillError::Io(e)
    }
}

impl Display for FillError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &FillError::Io(ref e) => write!(f, "Failed to read data: {}", e),
            &FillError::BufferOverflow(needed, capacity) => write!(f, "Cannot fill buffer of size {} bytes with {} bytes of data", capacity, needed),
        }
    }
}

impl Error for FillError {
    fn description(&self) -> &str {
        match self {
            &FillError::Io(_) => "I/O error",
            &FillError::BufferOverflow(_, _) => "buffer overflow",
        }
    }
    fn cause(&self) -> Option<&Error> {
        match self {
            &FillError::Io(ref e) => Some(e),
            &FillError::BufferOverflow(_, _) => None,
        }
    }
}

#[derive(Debug)]
pub enum FillApplyError<I, E> {
    Parser(nom::Err<I, E>),
    FillError(FillError),
}

impl<I, E> From<nom::Err<I, E>> for FillApplyError<I, E> {
    fn from(e: nom::Err<I, E>) -> FillApplyError<I, E> {
        FillApplyError::Parser(e)
    }
}

impl<I, E> From<FillError> for FillApplyError<I, E> {
    fn from(e: FillError) -> FillApplyError<I, E> {
        FillApplyError::FillError(e)
    }
}

impl<I, E> Display for FillApplyError<I, E> where I: Debug, E: Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &FillApplyError::Parser(ref e) => write!(f, "Failed to parse data: {}", e),
            &FillApplyError::FillError(ref e) => write!(f, "Failed to fill buffer with amout of data requested by parser: {}", e),
        }
    }
}

impl<I, E> Error for FillApplyError<I, E> where I: Debug + Display + Any, E: Error {
    fn description(&self) -> &str {
        match self {
            &FillApplyError::Parser(_) => "parsing faield",
            &FillApplyError::FillError(_) => "buffer fill error",
        }
    }
    fn cause(&self) -> Option<&Error> {
        match self {
            &FillApplyError::Parser(_) => None, // e contains reference to data
            &FillApplyError::FillError(ref e) => Some(e),
        }
    }
}

pub struct ReadStreamBuf<R: Read> {
    reader: R,
    //TODO: alignment?
    buf: Vec<u8>,
    cap: usize,
    needed: Cell<Option<nom::Needed>>,
    offset: Cell<usize>,
}

impl<R: Read> ReadStreamBuf<R> {
    #[allow(dead_code)]
    pub fn new(reader: R) -> ReadStreamBuf<R> {
        ReadStreamBuf::with_capacity(reader, DEFAULT_BUF_SIZE)
    }

    pub fn with_capacity(reader: R, cap: usize) -> ReadStreamBuf<R> {
        ReadStreamBuf {
            reader: reader,
            buf: Vec::with_capacity(cap),
            cap: cap,
            needed: Cell::new(Some(nom::Needed::Unknown)),
            offset: Cell::new(0),
        }
    }

    #[allow(dead_code)]
    pub fn into_inner(self) -> R {
        self.reader
    }
}

impl<R: Read> StreamBuf<u8> for ReadStreamBuf<R> {
    fn fill(&mut self, min_bytes: usize) -> Result<(), FillError> {
        let len = self.buf.len();
        let have = len - self.offset.get();

        if have >= min_bytes {
            return Ok(())
        }

        let needed = min_bytes - have;

        if min_bytes > self.cap {
            return Err(FillError::BufferOverflow(min_bytes, self.cap))
        }
        if len + needed > self.cap {
            self.recycle();
            assert_eq!(self.offset.get(), 0);
            assert_eq!(self.buf.len(), have);
            return self.fill(min_bytes)
        }

        self.buf.resize(self.cap, 0);
        trace!("reading exactly {} bytes into buf blocking: {}..{} ({}); have: {} will have: {}", needed, len, len + needed, self.buf[len..len + needed].len(), have, have + needed);
        if let Err(err) = self.reader.read_exact(&mut self.buf[len..len + needed]) {
            self.buf.resize(len, 0);
            return Err(From::from(err));
        }

        // Try to read to the end of the buffer if we can
        trace!("reading up to {} extra bytes into buf non blocking", self.cap - (len + needed));
        match self.reader.read(&mut self.buf[len + needed..self.cap]) {
            Err(err) => {
                self.buf.resize(len + needed, 0);
                return Err(From::from(err));
            },
            Ok(bytes_read) => {
                trace!("got extra {} bytes", bytes_read);
                self.buf.resize(len + needed + bytes_read, 0);
            }
        }

        //trace!("buf has: {:?}", self.data());
        trace!("buf has {} bytes", self.data().len());
        Ok(())
    }

    fn recycle(&mut self) {
        let offset = self.offset.get();
        if offset == 0 {
            return
        }

        let len = self.buf.len();
        let have = len - offset;

        // This does same thing as:
        // self.buf = self.buf.split_off(self.offset.get());
        // but avoids alloation by moving the memory instead of slicing + freeing + allocating
        unsafe {
            copy(self.buf[offset..len].as_ptr(), self.buf.as_mut_ptr(), have);
        }

        self.buf.resize(have, 0);
        self.offset.set(0);
    }

    fn consume(&mut self, bytes: usize) {
        let len = self.buf.len();

        let consume = if self.offset.get() + bytes > len {
            len - self.offset.get()
        } else {
            bytes
        };
        self.offset.set(self.offset.get() + consume);
    }

    fn data<'b>(&'b self) -> &'b[u8] {
        &self.buf[self.offset.get()..self.buf.len()]
    }

    fn needed(&self) -> Option<nom::Needed> {
        self.needed.get().clone()
    }

    fn apply<'b, C, CO, E>(&'b self, combinator: C) -> Result<Option<CO>, nom::Err<&'b [u8], E>> where
        C: Fn(&'b [u8]) -> nom::IResult<&'b [u8], CO, E> {
        let data = self.data();
        match combinator(data) {
            nom::IResult::Done(left, out) => {
                let consumed = data.len() - left.len();
                trace!("done: consumed: {}", consumed);

                // Move the offset
                self.offset.set(self.offset.get() + consumed);
                // We don't know how much we will need now
                self.needed.set(None);
                Ok(Some(out))
            },
            nom::IResult::Error(err) => Err(err),
            nom::IResult::Incomplete(needed) => {
                trace!("incomplete: needed: {:?}", needed);
                self.needed.set(Some(needed));
                Ok(None)
            }
        }
    }
}

#[cfg(test)]
mod resd_stream_buf_tests {
    use super::StreamBuf;
    use super::ReadStreamBuf;
    use super::FillError;
    use nom::{IResult, Needed};
    use std::io::Cursor;

    fn subject(data: Vec<u8>) -> ReadStreamBuf<Cursor<Vec<u8>>> {
        ReadStreamBuf::new(Cursor::new(data))
    }

    fn subject_with_default_data() -> ReadStreamBuf<Cursor<Vec<u8>>> {
        subject(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    }

    #[test]
    fn reading() {
        let mut rsb = subject_with_default_data();
        assert!(rsb.data().is_empty());

        // fill reads as much as it can non-blocking
        // TODO: how to test blocking streaming?
        rsb.fill(3).unwrap();
        assert_eq!(rsb.data(), [0, 1, 2, 3, 4, 5, 6, 7, 8 ,9].as_ref());
    }

    #[test]
    fn recycle() {
        let mut rsb = subject_with_default_data();

        //TODO: this test does nothing...
        rsb.fill(10).unwrap();
        rsb.recycle();
        assert_eq!(rsb.data(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_ref());
    }

    #[test]
    fn consume() {
        let mut rsb = subject_with_default_data();

        rsb.fill(10).unwrap();
        rsb.consume(8);
        assert_eq!(rsb.data(), [8, 9].as_ref());
    }

    #[test]
    fn consume_more_than_we_have() {
        let mut rsb = subject_with_default_data();

        rsb.consume(3);
        rsb.fill(10).unwrap();
        assert_eq!(rsb.data(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_ref());
    }

    #[test]
    fn apply_function() {
        use nom::be_u8;
        let mut rsb = subject_with_default_data();

        rsb.fill(1).unwrap();
        assert_eq!(rsb.apply(be_u8).unwrap(), Some(0));
    }

    #[test]
    fn apply_should_consume() {
        use nom::be_u8;
        let mut rsb = subject_with_default_data();

        rsb.fill(1).unwrap();
        assert_eq!(rsb.apply(be_u8).unwrap(), Some(0));
        rsb.fill(1).unwrap();
        assert_eq!(rsb.apply(be_u8).unwrap(), Some(1));
    }

    #[test]
    fn apply_converted_macro() {
        let mut rsb = subject_with_default_data();

        rsb.fill(2).unwrap();
        assert_eq!(rsb.apply(closure!(tag!([0, 1]))).unwrap(), Some([0, 1].as_ref()));
    }

    #[test]
    fn apply_closure() {
        let mut rsb = subject_with_default_data();

        rsb.fill(2).unwrap();
        assert_eq!(rsb.apply(|i| tag!(i, [0, 1])).unwrap(), Some([0, 1].as_ref()));
    }

    #[test]
    fn apply_custom_fuction_with_refs() {
        let mut rsb = subject_with_default_data();
        fn comb(input: &[u8]) -> IResult<&[u8], &[u8]> {
            tag!(input, [0, 1, 2])
        }

        rsb.fill(3).unwrap();
        assert_eq!(rsb.apply(comb).unwrap(), Some([0, 1, 2].as_ref()));
    }

    #[test]
    fn needed_with_apply() {
        let mut rsb = subject_with_default_data();

        fn comb<'a>(input: &'a[u8]) -> IResult<&'a[u8], &'a[u8]> {
            tag!(input, [0, 1, 2])
        }

        assert!(rsb.apply(comb).unwrap().is_none());

        let needed = rsb.needed().unwrap();
        assert_eq!(needed, Needed::Size(3));

        if let Needed::Size(bytes) = needed {
            rsb.fill(bytes).unwrap();
            assert_eq!(rsb.apply(comb).unwrap(), Some([0, 1, 2].as_ref()));
        }
    }

    #[test]
    fn fill_apply() {
        use nom::be_u8;
        let mut rsb = subject_with_default_data();

        fn comb(input: &[u8]) -> IResult<&[u8], &[u8]> {
            tag!(input, [0, 1, 2])
        }

        //TODO: test None scenario
        rsb.fill(10).unwrap();
        assert_eq!(rsb.fill_apply(comb).unwrap(), Some([0, 1, 2].as_ref()));
        assert_eq!(rsb.fill_apply(be_u8).unwrap(), Some(3));
    }

    #[test]
    fn fill_over_buf() {
        let mut rsb = subject_with_default_data();
        let error = rsb.fill(super::DEFAULT_BUF_SIZE + 1).unwrap_err();
        if let FillError::BufferOverflow(needed, capacity) = error {
            assert_eq!(needed, super::DEFAULT_BUF_SIZE + 1);
            assert_eq!(capacity, super::DEFAULT_BUF_SIZE);
        } else {
            panic!("was expecing BufferOverflow error");
        }
    }
}
<|endoftext|>


<commit_before>use crate::utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_then, type_is_unsafe_function};
use if_chain::if_chain;
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;

pub struct EtaPass;

/// **What it does:** Checks for closures which just call another function where
/// the function can be called directly. `unsafe` functions or calls where types
/// get adjusted are ignored.
///
/// **Why is this bad?** Needlessly creating a closure adds code for no benefit
/// and gives the optimizer more work.
///
/// **Known problems:** If creating the closure inside the closure has a side-
/// effect then moving the closure creation out will change when that side-
/// effect runs.
/// See https://github.com/rust-lang/rust-clippy/issues/1439 for more
/// details.
///
/// **Example:**
/// ```rust
/// xs.map(|x| foo(x))
/// ```
/// where `foo(_)` is a plain function that takes the exact argument type of
/// `x`.
declare_clippy_lint! {
    pub REDUNDANT_CLOSURE,
    style,
    "redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`)"
}

impl LintPass for EtaPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(REDUNDANT_CLOSURE)
    }

    fn name(&self) -> &'static str {
        "EtaReduction"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaPass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        match expr.node {
            ExprKind::Call(_, ref args) | ExprKind::MethodCall(_, _, ref args) => {
                for arg in args {
                    check_closure(cx, arg)
                }
            },
            _ => (),
        }
    }
}

fn check_closure(cx: &LateContext<'_, '_>, expr: &Expr) {
    if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.node {
        let body = cx.tcx.hir().body(eid);
        let ex = &body.value;

        if_chain!(
            if let ExprKind::Call(ref caller, ref args) = ex.node;

            // Not the same number of arguments, there is no way the closure is the same as the function return;
            if args.len() == decl.inputs.len();

            // Are the expression or the arguments type-adjusted? Then we need the closure
            if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)));

            let fn_ty = cx.tables.expr_ty(caller);
            if !type_is_unsafe_function(cx, fn_ty);

            if compare_inputs(&mut iter_input_pats(decl, body), &mut args.into_iter());

            then {
                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |db| {
                    if let Some(snippet) = snippet_opt(cx, caller.span) {
                        db.span_suggestion(
                            expr.span,
                            "remove closure as shown",
                            snippet,
                            Applicability::MachineApplicable,
                        );
                    }
                });
            }
        );

        if_chain!(
            if let ExprKind::MethodCall(ref path, _, ref args) = ex.node;

            // Not the same number of arguments, there is no way the closure is the same as the function return;
            if args.len() == decl.inputs.len();

            // Are the expression or the arguments type-adjusted? Then we need the closure
            if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg)));

            let method_def_id = cx.tables.type_dependent_defs()[ex.hir_id].def_id();
            if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id));

            if compare_inputs(&mut iter_input_pats(decl, body), &mut args.into_iter());

            if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]);

            then {
                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |db| {
                    db.span_suggestion(
                        expr.span,
                        "remove closure as shown",
                        format!("{}::{}", name, path.ident.name),
                        Applicability::MachineApplicable,
                    );
                });
            }
        );
    }
}

/// Tries to determine the type for universal function call to be used instead of the closure
fn get_ufcs_type_name(
    cx: &LateContext<'_, '_>,
    method_def_id: def_id::DefId,
    self_arg: &Expr,
) -> std::option::Option<String> {
    let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0].sty;
    let actual_type_of_self = &cx.tables.node_type(self_arg.hir_id).sty;

    if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
        //if the method expectes &self, ufcs requires explicit borrowing so closure can't be removed
        return match (expected_type_of_self, actual_type_of_self) {
            (ty::Ref(_, _, _), ty::Ref(_, _, _)) => Some(cx.tcx.item_path_str(trait_id)),
            (l, r) => match (l, r) {
                (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => None,
                (_, _) => Some(cx.tcx.item_path_str(trait_id)),
            },
        };
    }

    cx.tcx.impl_of_method(method_def_id).and_then(|_| {
        //a type may implicitly implement other types methods (e.g. Deref)
        if match_types(expected_type_of_self, actual_type_of_self) {
            return Some(get_type_name(cx, &actual_type_of_self));
        }
        None
    })
}

fn match_types(lhs: &ty::TyKind<'_>, rhs: &ty::TyKind<'_>) -> bool {
    match (lhs, rhs) {
        (ty::Bool, ty::Bool)
        | (ty::Char, ty::Char)
        | (ty::Int(_), ty::Int(_))
        | (ty::Uint(_), ty::Uint(_))
        | (ty::Str, ty::Str) => true,
        (ty::Ref(_, t1, _), ty::Ref(_, t2, _))
        | (ty::Array(t1, _), ty::Array(t2, _))
        | (ty::Slice(t1), ty::Slice(t2)) => match_types(&t1.sty, &t2.sty),
        (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2,
        (_, _) => false,
    }
}

fn get_type_name(cx: &LateContext<'_, '_>, kind: &ty::TyKind<'_>) -> String {
    match kind {
        ty::Adt(t, _) => cx.tcx.item_path_str(t.did),
        ty::Ref(_, r, _) => get_type_name(cx, &r.sty),
        _ => kind.to_string(),
    }
}

fn compare_inputs(closure_inputs: &mut dyn Iterator<Item = &Arg>, call_args: &mut dyn Iterator<Item = &Expr>) -> bool {
    for (closure_input, function_arg) in closure_inputs.zip(call_args) {
        if let PatKind::Binding(_, _, _, ident, _) = closure_input.pat.node {
            // XXXManishearth Should I be checking the binding mode here?
            if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.node {
                if p.segments.len() != 1 {
                    // If it's a proper path, it can't be a local variable
                    return false;
                }
                if p.segments[0].ident.name != ident.name {
                    // The two idents should be the same
                    return false;
                }
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    true
}
<commit_msg>Auto merge of #3816 - g-bartoszek:redundant_closure_for_macros, r=Manishearth<commit_after>use crate::utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_then, type_is_unsafe_function};
use if_chain::if_chain;
use rustc::hir::*;
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_errors::Applicability;

pub struct EtaPass;

/// **What it does:** Checks for closures which just call another function where
/// the function can be called directly. `unsafe` functions or calls where types
/// get adjusted are ignored.
///
/// **Why is this bad?** Needlessly creating a closure adds code for no benefit
/// and gives the optimizer more work.
///
/// **Known problems:** If creating the closure inside the closure has a side-
/// effect then moving the closure creation out will change when that side-
/// effect runs.
/// See https://github.com/rust-lang/rust-clippy/issues/1439 for more
/// details.
///
/// **Example:**
/// ```rust
/// xs.map(|x| foo(x))
/// ```
/// where `foo(_)` is a plain function that takes the exact argument type of
/// `x`.
declare_clippy_lint! {
    pub REDUNDANT_CLOSURE,
    style,
    "redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`)"
}

impl LintPass for EtaPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(REDUNDANT_CLOSURE)
    }

    fn name(&self) -> &'static str {
        "EtaReduction"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaPass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if in_external_macro(cx.sess(), expr.span) {
            return;
        }

        match expr.node {
            ExprKind::Call(_, ref args) | ExprKind::MethodCall(_, _, ref args) => {
                for arg in args {
                    check_closure(cx, arg)
                }
            },
            _ => (),
        }
    }
}

fn check_closure(cx: &LateContext<'_, '_>, expr: &Expr) {
    if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.node {
        let body = cx.tcx.hir().body(eid);
        let ex = &body.value;

        if_chain!(
            if let ExprKind::Call(ref caller, ref args) = ex.node;

            // Not the same number of arguments, there is no way the closure is the same as the function return;
            if args.len() == decl.inputs.len();

            // Are the expression or the arguments type-adjusted? Then we need the closure
            if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)));

            let fn_ty = cx.tables.expr_ty(caller);
            if !type_is_unsafe_function(cx, fn_ty);

            if compare_inputs(&mut iter_input_pats(decl, body), &mut args.into_iter());

            then {
                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |db| {
                    if let Some(snippet) = snippet_opt(cx, caller.span) {
                        db.span_suggestion(
                            expr.span,
                            "remove closure as shown",
                            snippet,
                            Applicability::MachineApplicable,
                        );
                    }
                });
            }
        );

        if_chain!(
            if let ExprKind::MethodCall(ref path, _, ref args) = ex.node;

            // Not the same number of arguments, there is no way the closure is the same as the function return;
            if args.len() == decl.inputs.len();

            // Are the expression or the arguments type-adjusted? Then we need the closure
            if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg)));

            let method_def_id = cx.tables.type_dependent_defs()[ex.hir_id].def_id();
            if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id));

            if compare_inputs(&mut iter_input_pats(decl, body), &mut args.into_iter());

            if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]);

            then {
                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |db| {
                    db.span_suggestion(
                        expr.span,
                        "remove closure as shown",
                        format!("{}::{}", name, path.ident.name),
                        Applicability::MachineApplicable,
                    );
                });
            }
        );
    }
}

/// Tries to determine the type for universal function call to be used instead of the closure
fn get_ufcs_type_name(
    cx: &LateContext<'_, '_>,
    method_def_id: def_id::DefId,
    self_arg: &Expr,
) -> std::option::Option<String> {
    let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0].sty;
    let actual_type_of_self = &cx.tables.node_type(self_arg.hir_id).sty;

    if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
        //if the method expectes &self, ufcs requires explicit borrowing so closure can't be removed
        return match (expected_type_of_self, actual_type_of_self) {
            (ty::Ref(_, _, _), ty::Ref(_, _, _)) => Some(cx.tcx.item_path_str(trait_id)),
            (l, r) => match (l, r) {
                (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => None,
                (_, _) => Some(cx.tcx.item_path_str(trait_id)),
            },
        };
    }

    cx.tcx.impl_of_method(method_def_id).and_then(|_| {
        //a type may implicitly implement other types methods (e.g. Deref)
        if match_types(expected_type_of_self, actual_type_of_self) {
            return Some(get_type_name(cx, &actual_type_of_self));
        }
        None
    })
}

fn match_types(lhs: &ty::TyKind<'_>, rhs: &ty::TyKind<'_>) -> bool {
    match (lhs, rhs) {
        (ty::Bool, ty::Bool)
        | (ty::Char, ty::Char)
        | (ty::Int(_), ty::Int(_))
        | (ty::Uint(_), ty::Uint(_))
        | (ty::Str, ty::Str) => true,
        (ty::Ref(_, t1, _), ty::Ref(_, t2, _))
        | (ty::Array(t1, _), ty::Array(t2, _))
        | (ty::Slice(t1), ty::Slice(t2)) => match_types(&t1.sty, &t2.sty),
        (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2,
        (_, _) => false,
    }
}

fn get_type_name(cx: &LateContext<'_, '_>, kind: &ty::TyKind<'_>) -> String {
    match kind {
        ty::Adt(t, _) => cx.tcx.item_path_str(t.did),
        ty::Ref(_, r, _) => get_type_name(cx, &r.sty),
        _ => kind.to_string(),
    }
}

fn compare_inputs(closure_inputs: &mut dyn Iterator<Item = &Arg>, call_args: &mut dyn Iterator<Item = &Expr>) -> bool {
    for (closure_input, function_arg) in closure_inputs.zip(call_args) {
        if let PatKind::Binding(_, _, _, ident, _) = closure_input.pat.node {
            // XXXManishearth Should I be checking the binding mode here?
            if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.node {
                if p.segments.len() != 1 {
                    // If it's a proper path, it can't be a local variable
                    return false;
                }
                if p.segments[0].ident.name != ident.name {
                    // The two idents should be the same
                    return false;
                }
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    true
}
<|endoftext|>


<commit_before>use std::{mem, ptr};
use libc::{c_int, socklen_t};
use fcntl::{Fd, fcntl, F_SETFL, F_SETFD, FD_CLOEXEC, O_NONBLOCK};
use errno::{SysResult, SysError, from_ffi};
use features;

pub use libc::{in_addr, sockaddr_in, sockaddr_in6, sockaddr_un, sa_family_t};

pub use self::consts::*;

mod ffi {
    use libc::{c_int, c_void, socklen_t};
    pub use libc::{socket, listen, bind, accept, connect, setsockopt};

    extern {
        pub fn getsockopt(
            sockfd: c_int,
            level: c_int,
            optname: c_int,
            optval: *mut c_void,
            optlen: *mut socklen_t) -> c_int;
    }
}

// Extra flags - Supported by Linux 2.6.27, normalized on other platforms
bitflags!(
    flags SockFlag: c_int {
        const SOCK_NONBLOCK = 0o0004000,
        const SOCK_CLOEXEC  = 0o2000000
    }
)

pub enum SockAddr {
    SockIpV4(sockaddr_in),
    SockIpV6(sockaddr_in6),
    SockUnix(sockaddr_un)
}

#[cfg(target_os = "linux")]
mod consts {
    use libc::{c_int};

    pub type AddressFamily = c_int;

    pub const AF_UNIX: AddressFamily  = 1;
    pub const AF_LOCAL: AddressFamily = AF_UNIX;
    pub const AF_INET: AddressFamily  = 2;
    pub const AF_INET6: AddressFamily = 10;

    pub type SockType = c_int;

    pub const SOCK_STREAM: SockType = 1;
    pub const SOCK_DGRAM: SockType = 2;
    pub const SOCK_SEQPACKET: SockType = 5;
    pub const SOCK_RAW: SockType = 3;
    pub const SOCK_RDM: SockType = 4;

    pub type SockLevel = c_int;

    pub const SOL_IP: SockLevel     = 0;
    pub const SOL_SOCKET: SockLevel = 1;
    pub const SOL_TCP: SockLevel    = 6;
    pub static IPPROTO_TCP: SockLevel = SOL_TCP;
    pub const SOL_UDP: SockLevel    = 17;
    pub const SOL_IPV6: SockLevel   = 41;

    pub type SockOpt = c_int;

    pub const SO_ACCEPTCONN: SockOpt = 30;
    pub const SO_BINDTODEVICE: SockOpt = 25;
    pub const SO_BROADCAST: SockOpt = 6;
    pub const SO_BSDCOMPAT: SockOpt = 14;
    pub const SO_DEBUG: SockOpt = 1;
    pub const SO_DOMAIN: SockOpt = 39;
    pub const SO_ERROR: SockOpt = 4;
    pub const SO_DONTROUTE: SockOpt = 5;
    pub const SO_KEEPALIVE: SockOpt = 9;
    pub const SO_LINGER: SockOpt = 13;
    pub const SO_MARK: SockOpt = 36;
    pub const SO_OOBINLINE: SockOpt = 10;
    pub const SO_PASSCRED: SockOpt = 16;
    pub const SO_PEEK_OFF: SockOpt = 42;
    pub const SO_PEERCRED: SockOpt = 17;
    pub const SO_PRIORITY: SockOpt = 12;
    pub const SO_PROTOCOL: SockOpt = 38;
    pub const SO_RCVBUF: SockOpt = 8;
    pub const SO_RCVBUFFORCE: SockOpt = 33;
    pub const SO_RCVLOWAT: SockOpt = 18;
    pub const SO_SNDLOWAT: SockOpt = 19;
    pub const SO_RCVTIMEO: SockOpt = 20;
    pub const SO_SNDTIMEO: SockOpt = 21;
    pub const SO_REUSEADDR: SockOpt = 2;
    pub const SO_REUSEPORT: SockOpt = 15;
    pub const SO_RXQ_OVFL: SockOpt = 40;
    pub const SO_SNDBUF: SockOpt = 7;
    pub const SO_SNDBUFFORCE: SockOpt = 32;
    pub const SO_TIMESTAMP: SockOpt = 29;
    pub const SO_TYPE: SockOpt = 3;
    pub const SO_BUSY_POLL: SockOpt = 46;

    // Socket options for TCP sockets
    pub const TCP_NODELAY: SockOpt = 1;
    pub const TCP_MAXSEG: SockOpt = 2;
    pub const TCP_CORK: SockOpt = 3;
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
mod consts {
    use libc::{c_int};

    pub type AddressFamily = c_int;

    pub const AF_UNIX: AddressFamily  = 1;
    pub const AF_LOCAL: AddressFamily = AF_UNIX;
    pub const AF_INET: AddressFamily  = 2;
    pub const AF_INET6: AddressFamily = 30;

    pub type SockType = c_int;

    pub const SOCK_STREAM: SockType = 1;
    pub const SOCK_DGRAM: SockType = 2;
    pub const SOCK_SEQPACKET: SockType = 5;
    pub const SOCK_RAW: SockType = 3;
    pub const SOCK_RDM: SockType = 4;

    pub type SockLevel = c_int;

    pub const SOL_SOCKET: SockLevel = 0xffff;

    pub type SockOpt = c_int;

    pub const SO_ACCEPTCONN: SockOpt          = 0x0002;
    pub const SO_BROADCAST: SockOpt           = 0x0020;
    pub const SO_DEBUG: SockOpt               = 0x0001;
    pub const SO_DONTTRUNC: SockOpt           = 0x2000;
    pub const SO_ERROR: SockOpt               = 0x1007;
    pub const SO_DONTROUTE: SockOpt           = 0x0010;
    pub const SO_KEEPALIVE: SockOpt           = 0x0008;
    pub const SO_LABEL: SockOpt               = 0x1010;
    pub const SO_LINGER: SockOpt              = 0x0080;
    pub const SO_NREAD: SockOpt               = 0x1020;
    pub const SO_NKE: SockOpt                 = 0x1021;
    pub const SO_NOSIGPIPE: SockOpt           = 0x1022;
    pub const SO_NOADDRERR: SockOpt           = 0x1023;
    pub const SO_NOTIFYCONFLICT: SockOpt      = 0x1026;
    pub const SO_NP_EXTENSIONS: SockOpt       = 0x1083;
    pub const SO_NWRITE: SockOpt              = 0x1024;
    pub const SO_OOBINLINE: SockOpt           = 0x0100;
    pub const SO_PEERLABEL: SockOpt           = 0x1011;
    pub const SO_RCVBUF: SockOpt              = 0x1002;
    pub const SO_RCVLOWAT: SockOpt            = 0x1004;
    pub const SO_SNDLOWAT: SockOpt            = 0x1003;
    pub const SO_RCVTIMEO: SockOpt            = 0x1006;
    pub const SO_SNDTIMEO: SockOpt            = 0x1005;
    pub const SO_RANDOMPORT: SockOpt          = 0x1082;
    pub const SO_RESTRICTIONS: SockOpt        = 0x1081;
    pub const SO_RESTRICT_DENYIN: SockOpt     = 0x00000001;
    pub const SO_RESTRICT_DENYOUT: SockOpt    = 0x00000002;
    pub const SO_REUSEADDR: SockOpt           = 0x0004;
    pub const SO_REUSESHAREUID: SockOpt       = 0x1025;
    pub const SO_SNDBUF: SockOpt              = 0x1001;
    pub const SO_TIMESTAMP: SockOpt           = 0x0400;
    pub const SO_TIMESTAMP_MONOTONIC: SockOpt = 0x0800;
    pub const SO_TYPE: SockOpt                = 0x1008;
    pub const SO_WANTMORE: SockOpt            = 0x4000;
    pub const SO_WANTOOBFLAG: SockOpt         = 0x8000;
    #[allow(type_overflow)]
    pub const SO_RESTRICT_DENYSET: SockOpt    = 0x80000000;
}

pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> SysResult<Fd> {
    let feat_atomic = features::socket_atomic_cloexec();

    if feat_atomic {
        ty = ty | flags.bits();
    }

    // TODO: Check the kernel version
    let res = unsafe { ffi::socket(domain, ty, 0) };

    if res < 0 {
        return Err(SysError::last());
    }

    if !feat_atomic {
        if flags.contains(SOCK_CLOEXEC) {
            try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
        }

        if flags.contains(SOCK_NONBLOCK) {
            try!(fcntl(res, F_SETFL(O_NONBLOCK)));
        }
    }

    Ok(res)
}

pub fn listen(sockfd: Fd, backlog: uint) -> SysResult<()> {
    let res = unsafe { ffi::listen(sockfd, backlog as c_int) };
    from_ffi(res)
}

pub fn bind(sockfd: Fd, addr: &SockAddr) -> SysResult<()> {
    let res = unsafe {
        match *addr {
            SockIpV4(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in>() as socklen_t),
            SockIpV6(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in6>() as socklen_t),
            SockUnix(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_un>() as socklen_t)
        }
    };

    from_ffi(res)
}

pub fn accept(sockfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    use libc::sockaddr;

    type F = unsafe extern "C" fn(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int;

    extern {
        #[linkage = "extern_weak"]
        static accept4: *const ();
    }

    if !accept4.is_null() {
        let res = unsafe {
            mem::transmute::<*const (), F>(accept4)(
                sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits)
        };

        if res < 0 {
            return Err(SysError::last());
        }

        Ok(res)
    } else {
        accept4_polyfill(sockfd, flags)
    }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    accept4_polyfill(sockfd, flags)
}

#[inline]
fn accept4_polyfill(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    let res =  unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(SysError::last());
    }

    if flags.contains(SOCK_CLOEXEC) {
        try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(SOCK_NONBLOCK) {
        try!(fcntl(res, F_SETFL(O_NONBLOCK)));
    }

    Ok(res)
}

pub fn connect(sockfd: Fd, addr: &SockAddr) -> SysResult<()> {
    let res = unsafe {
        match *addr {
            SockIpV4(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in>() as socklen_t),
            SockIpV6(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in6>() as socklen_t),
            SockUnix(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_un>() as socklen_t)
        }
    };

    from_ffi(res)
}

#[repr(C)]
pub struct linger {
    pub l_onoff: c_int,
    pub l_linger: c_int
}

pub fn getsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &mut T) -> SysResult<uint> {
    let mut len = mem::size_of::<T>() as socklen_t;

    let res = unsafe {
        ffi::getsockopt(
            fd, level, opt,
            mem::transmute(val),
            &mut len as *mut socklen_t)
    };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(len as uint)
}

pub fn setsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &T) -> SysResult<()> {
    let len = mem::size_of::<T>() as socklen_t;

    let res = unsafe {
            ffi::setsockopt(
            fd, level, opt,
            mem::transmute(val),
            len)
    };

    from_ffi(res)
}
<commit_msg>Add defines for multicast membership management<commit_after>use std::{mem, ptr};
use libc::{c_int, socklen_t};
use fcntl::{Fd, fcntl, F_SETFL, F_SETFD, FD_CLOEXEC, O_NONBLOCK};
use errno::{SysResult, SysError, from_ffi};
use features;

pub use libc::{in_addr, sockaddr_in, sockaddr_in6, sockaddr_un, sa_family_t, ip_mreq};

pub use self::consts::*;

mod ffi {
    use libc::{c_int, c_void, socklen_t};
    pub use libc::{socket, listen, bind, accept, connect, setsockopt};

    extern {
        pub fn getsockopt(
            sockfd: c_int,
            level: c_int,
            optname: c_int,
            optval: *mut c_void,
            optlen: *mut socklen_t) -> c_int;
    }
}

// Extra flags - Supported by Linux 2.6.27, normalized on other platforms
bitflags!(
    flags SockFlag: c_int {
        const SOCK_NONBLOCK = 0o0004000,
        const SOCK_CLOEXEC  = 0o2000000
    }
)

pub enum SockAddr {
    SockIpV4(sockaddr_in),
    SockIpV6(sockaddr_in6),
    SockUnix(sockaddr_un)
}

#[cfg(target_os = "linux")]
mod consts {
    use libc::{c_int};

    pub type AddressFamily = c_int;

    pub const AF_UNIX: AddressFamily  = 1;
    pub const AF_LOCAL: AddressFamily = AF_UNIX;
    pub const AF_INET: AddressFamily  = 2;
    pub const AF_INET6: AddressFamily = 10;

    pub type SockType = c_int;

    pub const SOCK_STREAM: SockType = 1;
    pub const SOCK_DGRAM: SockType = 2;
    pub const SOCK_SEQPACKET: SockType = 5;
    pub const SOCK_RAW: SockType = 3;
    pub const SOCK_RDM: SockType = 4;

    pub type SockLevel = c_int;

    pub const SOL_IP: SockLevel     = 0;
    pub const SOL_SOCKET: SockLevel = 1;
    pub const SOL_TCP: SockLevel    = 6;
    pub static IPPROTO_TCP: SockLevel = SOL_TCP;
    pub const SOL_UDP: SockLevel    = 17;
    pub const SOL_IPV6: SockLevel   = 41;

    pub type SockOpt = c_int;

    pub const SO_ACCEPTCONN: SockOpt = 30;
    pub const SO_BINDTODEVICE: SockOpt = 25;
    pub const SO_BROADCAST: SockOpt = 6;
    pub const SO_BSDCOMPAT: SockOpt = 14;
    pub const SO_DEBUG: SockOpt = 1;
    pub const SO_DOMAIN: SockOpt = 39;
    pub const SO_ERROR: SockOpt = 4;
    pub const SO_DONTROUTE: SockOpt = 5;
    pub const SO_KEEPALIVE: SockOpt = 9;
    pub const SO_LINGER: SockOpt = 13;
    pub const SO_MARK: SockOpt = 36;
    pub const SO_OOBINLINE: SockOpt = 10;
    pub const SO_PASSCRED: SockOpt = 16;
    pub const SO_PEEK_OFF: SockOpt = 42;
    pub const SO_PEERCRED: SockOpt = 17;
    pub const SO_PRIORITY: SockOpt = 12;
    pub const SO_PROTOCOL: SockOpt = 38;
    pub const SO_RCVBUF: SockOpt = 8;
    pub const SO_RCVBUFFORCE: SockOpt = 33;
    pub const SO_RCVLOWAT: SockOpt = 18;
    pub const SO_SNDLOWAT: SockOpt = 19;
    pub const SO_RCVTIMEO: SockOpt = 20;
    pub const SO_SNDTIMEO: SockOpt = 21;
    pub const SO_REUSEADDR: SockOpt = 2;
    pub const SO_REUSEPORT: SockOpt = 15;
    pub const SO_RXQ_OVFL: SockOpt = 40;
    pub const SO_SNDBUF: SockOpt = 7;
    pub const SO_SNDBUFFORCE: SockOpt = 32;
    pub const SO_TIMESTAMP: SockOpt = 29;
    pub const SO_TYPE: SockOpt = 3;
    pub const SO_BUSY_POLL: SockOpt = 46;

    // Socket options for TCP sockets
    pub const TCP_NODELAY: SockOpt = 1;
    pub const TCP_MAXSEG: SockOpt = 2;
    pub const TCP_CORK: SockOpt = 3;

    // Socket options for the IP layer of the socket
    pub const IP_MULTICAST_IF: SockOpt = 32;
    pub const IP_MULTICAST_TTL: SockOpt = 33;
    pub const IP_MULTICAST_LOOP: SockOpt = 34;
    pub const IP_ADD_MEMBERSHIP: SockOpt = 35;
    pub const IP_DROP_MEMBERSHIP: SockOpt = 36;
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
mod consts {
    use libc::{c_int};

    pub type AddressFamily = c_int;

    pub const AF_UNIX: AddressFamily  = 1;
    pub const AF_LOCAL: AddressFamily = AF_UNIX;
    pub const AF_INET: AddressFamily  = 2;
    pub const AF_INET6: AddressFamily = 30;

    pub type SockType = c_int;

    pub const SOCK_STREAM: SockType = 1;
    pub const SOCK_DGRAM: SockType = 2;
    pub const SOCK_SEQPACKET: SockType = 5;
    pub const SOCK_RAW: SockType = 3;
    pub const SOCK_RDM: SockType = 4;

    pub type SockLevel = c_int;

    pub const SOL_SOCKET: SockLevel = 0xffff;

    pub type SockOpt = c_int;

    pub const SO_ACCEPTCONN: SockOpt          = 0x0002;
    pub const SO_BROADCAST: SockOpt           = 0x0020;
    pub const SO_DEBUG: SockOpt               = 0x0001;
    pub const SO_DONTTRUNC: SockOpt           = 0x2000;
    pub const SO_ERROR: SockOpt               = 0x1007;
    pub const SO_DONTROUTE: SockOpt           = 0x0010;
    pub const SO_KEEPALIVE: SockOpt           = 0x0008;
    pub const SO_LABEL: SockOpt               = 0x1010;
    pub const SO_LINGER: SockOpt              = 0x0080;
    pub const SO_NREAD: SockOpt               = 0x1020;
    pub const SO_NKE: SockOpt                 = 0x1021;
    pub const SO_NOSIGPIPE: SockOpt           = 0x1022;
    pub const SO_NOADDRERR: SockOpt           = 0x1023;
    pub const SO_NOTIFYCONFLICT: SockOpt      = 0x1026;
    pub const SO_NP_EXTENSIONS: SockOpt       = 0x1083;
    pub const SO_NWRITE: SockOpt              = 0x1024;
    pub const SO_OOBINLINE: SockOpt           = 0x0100;
    pub const SO_PEERLABEL: SockOpt           = 0x1011;
    pub const SO_RCVBUF: SockOpt              = 0x1002;
    pub const SO_RCVLOWAT: SockOpt            = 0x1004;
    pub const SO_SNDLOWAT: SockOpt            = 0x1003;
    pub const SO_RCVTIMEO: SockOpt            = 0x1006;
    pub const SO_SNDTIMEO: SockOpt            = 0x1005;
    pub const SO_RANDOMPORT: SockOpt          = 0x1082;
    pub const SO_RESTRICTIONS: SockOpt        = 0x1081;
    pub const SO_RESTRICT_DENYIN: SockOpt     = 0x00000001;
    pub const SO_RESTRICT_DENYOUT: SockOpt    = 0x00000002;
    pub const SO_REUSEADDR: SockOpt           = 0x0004;
    pub const SO_REUSESHAREUID: SockOpt       = 0x1025;
    pub const SO_SNDBUF: SockOpt              = 0x1001;
    pub const SO_TIMESTAMP: SockOpt           = 0x0400;
    pub const SO_TIMESTAMP_MONOTONIC: SockOpt = 0x0800;
    pub const SO_TYPE: SockOpt                = 0x1008;
    pub const SO_WANTMORE: SockOpt            = 0x4000;
    pub const SO_WANTOOBFLAG: SockOpt         = 0x8000;
    #[allow(type_overflow)]
    pub const SO_RESTRICT_DENYSET: SockOpt    = 0x80000000;
}

pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> SysResult<Fd> {
    let feat_atomic = features::socket_atomic_cloexec();

    if feat_atomic {
        ty = ty | flags.bits();
    }

    // TODO: Check the kernel version
    let res = unsafe { ffi::socket(domain, ty, 0) };

    if res < 0 {
        return Err(SysError::last());
    }

    if !feat_atomic {
        if flags.contains(SOCK_CLOEXEC) {
            try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
        }

        if flags.contains(SOCK_NONBLOCK) {
            try!(fcntl(res, F_SETFL(O_NONBLOCK)));
        }
    }

    Ok(res)
}

pub fn listen(sockfd: Fd, backlog: uint) -> SysResult<()> {
    let res = unsafe { ffi::listen(sockfd, backlog as c_int) };
    from_ffi(res)
}

pub fn bind(sockfd: Fd, addr: &SockAddr) -> SysResult<()> {
    let res = unsafe {
        match *addr {
            SockIpV4(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in>() as socklen_t),
            SockIpV6(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in6>() as socklen_t),
            SockUnix(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_un>() as socklen_t)
        }
    };

    from_ffi(res)
}

pub fn accept(sockfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    use libc::sockaddr;

    type F = unsafe extern "C" fn(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int;

    extern {
        #[linkage = "extern_weak"]
        static accept4: *const ();
    }

    if !accept4.is_null() {
        let res = unsafe {
            mem::transmute::<*const (), F>(accept4)(
                sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits)
        };

        if res < 0 {
            return Err(SysError::last());
        }

        Ok(res)
    } else {
        accept4_polyfill(sockfd, flags)
    }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    accept4_polyfill(sockfd, flags)
}

#[inline]
fn accept4_polyfill(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    let res =  unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(SysError::last());
    }

    if flags.contains(SOCK_CLOEXEC) {
        try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(SOCK_NONBLOCK) {
        try!(fcntl(res, F_SETFL(O_NONBLOCK)));
    }

    Ok(res)
}

pub fn connect(sockfd: Fd, addr: &SockAddr) -> SysResult<()> {
    let res = unsafe {
        match *addr {
            SockIpV4(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in>() as socklen_t),
            SockIpV6(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in6>() as socklen_t),
            SockUnix(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_un>() as socklen_t)
        }
    };

    from_ffi(res)
}

#[repr(C)]
pub struct linger {
    pub l_onoff: c_int,
    pub l_linger: c_int
}

pub fn getsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &mut T) -> SysResult<uint> {
    let mut len = mem::size_of::<T>() as socklen_t;

    let res = unsafe {
        ffi::getsockopt(
            fd, level, opt,
            mem::transmute(val),
            &mut len as *mut socklen_t)
    };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(len as uint)
}

pub fn setsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &T) -> SysResult<()> {
    let len = mem::size_of::<T>() as socklen_t;

    let res = unsafe {
            ffi::setsockopt(
            fd, level, opt,
            mem::transmute(val),
            len)
    };

    from_ffi(res)
}
<|endoftext|>


<commit_before>use std::io::stdio::{stdout, stderr};

use libc::pid_t;
use collections::treemap::TreeMap;
use argparse::{ArgumentParser, StoreFalse};

use super::super::env::{Environ, Container};
use super::super::options::env_options;
use super::super::run::internal_run;
use super::super::config::{Plain, Command};
use super::super::build::ensure_container;
use super::super::monitor::Monitor;

pub fn run_plain_command(env: &mut Environ, cmdname: &String,
    args: Vec<String>)
    -> Result<int, String>
{
    let has_arguments;
    let description;
    let mut command_workdir;
    {
        let command = env.config.commands.find(cmdname).unwrap();
        has_arguments = command.accepts_arguments;
        description = command.description.clone().unwrap_or("".to_string());
        command_workdir = command.work_dir.is_some();
    }
    let mut cmdargs;
    if has_arguments {
        //  All options forwarded to command (including --help and others)
        cmdargs = args;
        cmdargs.shift();  // Zeroth arg is a command
    } else {
        //  We can provide useful help in this case
        cmdargs = Vec::new();
        let mut ap = ArgumentParser::new();
        if description.len() > 0 {
            ap.set_description(description.as_slice());
        }
        if command_workdir {
            ap.refer(&mut command_workdir)
                .add_option(["--override-workdir"], box StoreFalse,
                    "Do not obey `work-dir` parameter in command definition.
                     Use current working directory instead.");
        }
        env_options(env, &mut ap);
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    let command = env.config.commands.find(cmdname).unwrap();
    let cname = match command.container {
        Some(ref name) => name.clone(),
        None => unimplemented!(),
    };
    let mut container = try!(env.get_container(&cname));
    try!(ensure_container(env, &mut container));

    let work_dir = if command_workdir {
        let ncwd = env.project_root.join(
            command.work_dir.as_ref().unwrap().as_slice());
        if !env.project_root.is_ancestor_of(&ncwd) {
            return Err(format!("Command's work-dir must be relative to \
                project root"));
        }
        ncwd
    } else {
        env.work_dir.clone()
    };

    let mut monitor = Monitor::new();
    let pid = try!(exec_plain_command_args(env, &work_dir,
        command, &container, cmdargs));
    monitor.add("child".to_string(), pid);
    monitor.wait_all();
    return Ok(monitor.get_status());
}

pub fn exec_plain_command(env: &Environ, work_dir: &Path,
    command: &Command, container: &Container)
    -> Result<pid_t, String>
{
    return exec_plain_command_args(env, work_dir,
        command, container, Vec::new());
}

pub fn exec_plain_command_args(env: &Environ, work_dir: &Path,
    command: &Command, container: &Container, cmdargs: Vec<String>)
    -> Result<pid_t, String>
{
    let mut runenv = TreeMap::new();
    for (k, v) in command.environ.iter() {
        runenv.insert(k.clone(), v.clone());
    }
    for k in command.inherit_environ.iter() {
        match getenv(k.as_slice()) {
            Some(ref val) => { runenv.insert(k.clone(), val.clone()); }
            None => {}
        }
    }
    let mut argprefix: Vec<String> = Vec::new();
    match container.command_wrapper {
        Some(ref wrapper) => {
            argprefix.extend(wrapper.clone().move_iter());
        }
        None => {}
    }
    match command.execute {
        Plain(ref cmd) => argprefix.push_all(cmd.as_slice()),
        _ => unreachable!(),
    }
    let cmd = argprefix.shift().unwrap();
    return internal_run(env, container,
        command.pid1mode, work_dir, cmd, (argprefix + cmdargs), runenv);
}
<commit_msg>Fix build problem<commit_after>use std::io::stdio::{stdout, stderr};
use std::os::getenv;

use libc::pid_t;
use collections::treemap::TreeMap;
use argparse::{ArgumentParser, StoreFalse};

use super::super::env::{Environ, Container};
use super::super::options::env_options;
use super::super::run::internal_run;
use super::super::config::{Plain, Command};
use super::super::build::ensure_container;
use super::super::monitor::Monitor;

pub fn run_plain_command(env: &mut Environ, cmdname: &String,
    args: Vec<String>)
    -> Result<int, String>
{
    let has_arguments;
    let description;
    let mut command_workdir;
    {
        let command = env.config.commands.find(cmdname).unwrap();
        has_arguments = command.accepts_arguments;
        description = command.description.clone().unwrap_or("".to_string());
        command_workdir = command.work_dir.is_some();
    }
    let mut cmdargs;
    if has_arguments {
        //  All options forwarded to command (including --help and others)
        cmdargs = args;
        cmdargs.shift();  // Zeroth arg is a command
    } else {
        //  We can provide useful help in this case
        cmdargs = Vec::new();
        let mut ap = ArgumentParser::new();
        if description.len() > 0 {
            ap.set_description(description.as_slice());
        }
        if command_workdir {
            ap.refer(&mut command_workdir)
                .add_option(["--override-workdir"], box StoreFalse,
                    "Do not obey `work-dir` parameter in command definition.
                     Use current working directory instead.");
        }
        env_options(env, &mut ap);
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    let command = env.config.commands.find(cmdname).unwrap();
    let cname = match command.container {
        Some(ref name) => name.clone(),
        None => unimplemented!(),
    };
    let mut container = try!(env.get_container(&cname));
    try!(ensure_container(env, &mut container));

    let work_dir = if command_workdir {
        let ncwd = env.project_root.join(
            command.work_dir.as_ref().unwrap().as_slice());
        if !env.project_root.is_ancestor_of(&ncwd) {
            return Err(format!("Command's work-dir must be relative to \
                project root"));
        }
        ncwd
    } else {
        env.work_dir.clone()
    };

    let mut monitor = Monitor::new();
    let pid = try!(exec_plain_command_args(env, &work_dir,
        command, &container, cmdargs));
    monitor.add("child".to_string(), pid);
    monitor.wait_all();
    return Ok(monitor.get_status());
}

pub fn exec_plain_command(env: &Environ, work_dir: &Path,
    command: &Command, container: &Container)
    -> Result<pid_t, String>
{
    return exec_plain_command_args(env, work_dir,
        command, container, Vec::new());
}

pub fn exec_plain_command_args(env: &Environ, work_dir: &Path,
    command: &Command, container: &Container, cmdargs: Vec<String>)
    -> Result<pid_t, String>
{
    let mut runenv = TreeMap::new();
    for (k, v) in command.environ.iter() {
        runenv.insert(k.clone(), v.clone());
    }
    for k in command.inherit_environ.iter() {
        match getenv(k.as_slice()) {
            Some(ref val) => { runenv.insert(k.clone(), val.clone()); }
            None => {}
        }
    }
    let mut argprefix: Vec<String> = Vec::new();
    match container.command_wrapper {
        Some(ref wrapper) => {
            argprefix.extend(wrapper.clone().move_iter());
        }
        None => {}
    }
    match command.execute {
        Plain(ref cmd) => argprefix.push_all(cmd.as_slice()),
        _ => unreachable!(),
    }
    let cmd = argprefix.shift().unwrap();
    return internal_run(env, container,
        command.pid1mode, work_dir, cmd, (argprefix + cmdargs), runenv);
}
<|endoftext|>