<commit_before>//! Hubcaps provides a set of building blocks for interacting with the Github API
//!
//! # Examples
//!
//!  Typical use will require instantiation of a Github client. Which requires
//! a user agent string, a `hyper::Client`, and set of `hubcaps::Credentials`.
//!
//! The hyper client should be configured with tls.
//!
//! ```no_run
//! extern crate hubcaps;
//! extern crate hyper;
//! extern crate hyper_native_tls;
//!
//! use hubcaps::{Credentials, Github};
//! use hyper::Client;
//! use hyper::net::HttpsConnector;
//! use hyper_native_tls::NativeTlsClient;
//!
//! fn main() {
//!   let github = Github::new(
//!     String::from("user-agent-name"),
//!     Client::with_connector(
//!       HttpsConnector::new(
//!         NativeTlsClient::new().unwrap()
//!       )
//!     ),
//!     Credentials::Token(
//!       String::from("personal-access-token")
//!     )
//!   );
//! }
//! ```
//!
//! Github enterprise users will want to create a client with the
//! [Github#host](struct.Github.html#method.host) method
//!
//! Access to various services are provided via methods on instances of the `Github` type.
//!
//! The convention for executing operations typically looks like
//! `github.repo(.., ..).service().operation(OperationOptions)` where operation may be `create`,
//! `delete`, ect.
//!
//! A service interface will provide access to operations and operations may access options types
//! this define the various parameter options available for the operation. Most operation option
//! types expose `builder()` methods for a builder oriented style of constructing options.
//!
//! # Errors
//!
//! Operations typically result in a `hubcaps::Result` Type which is an alias for Rust's
//! built-in Result with the Err Type fixed to the
//! [hubcaps::Error](errors/enum.Error.html) enum type.
//!
#[macro_use]
extern crate log;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate url;

// all the modules!
use serde::de::Deserialize;
pub mod users;
pub mod comments;
pub mod review_comments;
pub mod pull_commits;
pub mod keys;
pub mod gists;
pub mod deployments;
pub mod errors;
pub mod hooks;
pub mod issues;
pub mod labels;
pub mod rep;
pub mod releases;
pub mod repositories;
pub mod statuses;
pub mod pulls;
pub mod search;
pub mod organizations;

// fixme: move reps to modules
pub use rep::*;

pub use errors::Error;
use gists::{Gists, UserGists};
use search::Search;
use hyper::Client;
use hyper::client::RequestBuilder;
use hyper::method::Method;
use hyper::header::{qitem, Accept, Authorization, ContentLength, UserAgent};
use hyper::mime::Mime;
use hyper::status::StatusCode;
use repositories::{Repository, Repositories, UserRepositories, OrganizationRepositories};
use organizations::{Organizations, UserOrganizations};
use std::fmt;
use std::io::Read;
use url::Url;
use std::collections::HashMap;

header! { (Link, "Link") => [String] }

const DEFAULT_HOST: &'static str = "https://api.github.com";

/// alias for Result that infers hubcaps::Error as Err
pub type Result<T> = std::result::Result<T, Error>;

enum MediaType {
    Json,
    Preview(&'static str),
}

impl Default for MediaType {
    fn default() -> MediaType {
        MediaType::Json
    }
}

impl From<MediaType> for Mime {
    fn from(media: MediaType) -> Mime {
        match media {
            MediaType::Json => "application/vnd.github.v3+json".parse().unwrap(),
            MediaType::Preview(codename) => {
                format!("application/vnd.github.{}-preview+json", codename).parse().unwrap()
            }
        }
    }
}

/// enum representation of Github list sorting options
#[derive(Clone, Debug, PartialEq)]
pub enum SortDirection {
    /// Sort in ascending order
    Asc,
    /// Sort in descending order
    Desc,
}

impl fmt::Display for SortDirection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   SortDirection::Asc => "asc",
                   SortDirection::Desc => "desc",
               })
    }
}

impl Default for SortDirection {
    fn default() -> SortDirection {
        SortDirection::Asc
    }
}

/// Various forms of authentication credentials supported by Github
#[derive(Debug, PartialEq)]
pub enum Credentials {
    /// No authentication
    None,
    /// Oauth token string
    /// https://developer.github.com/v3/#oauth2-token-sent-in-a-header
    Token(String),
    /// Oauth client id and secret
    /// https://developer.github.com/v3/#oauth2-keysecret
    Client(String, String),
}

impl Default for Credentials {
    fn default() -> Credentials {
        Credentials::None
    }
}

/// Entry point interface for interacting with Github API
pub struct Github {
    host: String,
    agent: String,
    client: Client,
    credentials: Credentials,
}

impl Github {
    /// Create a new Github instance
    pub fn new<A>(agent: A, client: Client, credentials: Credentials) -> Github
        where A: Into<String>
    {
        Github::host(DEFAULT_HOST, agent, client, credentials)
    }

    /// Create a new Github instance hosted at a custom location.
    /// Useful for github enterprise installations ( yourdomain.com/api/v3/ )
    pub fn host<H, A>(host: H, agent: A, client: Client, credentials: Credentials) -> Github
        where H: Into<String>,
              A: Into<String>
    {
        Github {
            host: host.into(),
            agent: agent.into(),
            client: client,
            credentials: credentials,
        }
    }

    /// Return a reference to a Github reposistory
    pub fn repo<O, R>(&self, owner: O, repo: R) -> Repository
        where O: Into<String>,
              R: Into<String>
    {
        Repository::new(self, owner, repo)
    }

    /// Return a reference to the collection of repositories owned by and
    /// associated with an owner
    pub fn user_repos<S>(&self, owner: S) -> UserRepositories
        where S: Into<String>
    {
        UserRepositories::new(self, owner)
    }

    /// Return a reference to the collection of repositories owned by the user
    /// associated with the current authentication credentials
    pub fn repos(&self) -> Repositories {
        Repositories::new(self)
    }

    /// Return a reference to the collection of organizations that the user
    /// associated with the current authentication credentials is in
    pub fn orgs(&self) -> Organizations {
        Organizations::new(self)
    }

    /// Return a reference to the collection of organizations a user
    /// is publicly associated with
    pub fn user_orgs<U>(&self, user: U) -> UserOrganizations
        where U: Into<String>
    {
        UserOrganizations::new(self, user)
    }

    /// Return a reference to an interface that provides access to a user's gists
    pub fn user_gists<O>(&self, owner: O) -> UserGists
        where O: Into<String>
    {
        UserGists::new(self, owner)
    }

    /// Return a reference to an interface that provides access to the
    /// gists belonging to the owner of the token used to configure this client
    pub fn gists(&self) -> Gists {
        Gists::new(self)
    }

    pub fn search(&self) -> Search {
        Search::new(self)
    }

    /// Return a reference to the collection of repositories owned by and
    /// associated with an organization
    pub fn org_repos<O>(&self, org: O) -> OrganizationRepositories
        where O: Into<String>
    {
        OrganizationRepositories::new(self, org)
    }

    fn authenticate(&self, method: Method, url: String) -> RequestBuilder {
        match self.credentials {
            Credentials::Token(ref token) => {
                self.client.request(method, &url).header(Authorization(format!("token {}", token)))
            }
            Credentials::Client(ref id, ref secret) => {

                let mut parsed = Url::parse(&url).unwrap();
                parsed.query_pairs_mut()
                    .append_pair("client_id", id)
                    .append_pair("client_secret", secret);
                self.client.request(method, parsed)
            }
            Credentials::None => self.client.request(method, &url),
        }
    }

    fn iter<'a, D, I>(&'a self, uri: String, into_items: fn(D) -> Vec<I>) -> Result<Iter<'a, D, I>>
        where D: Deserialize
    {
        Iter::new(self, self.host.clone() + &uri, into_items)
    }

    fn request<D>(&self,
                  method: Method,
                  uri: String,
                  body: Option<&[u8]>,
                  media_type: MediaType)
                  -> Result<(Option<Links>, D)>
        where D: Deserialize
    {
        let builder = self.authenticate(method, uri)
            .header(UserAgent(self.agent.to_owned()))
            .header(Accept(vec![qitem(From::from(media_type))]));

        let mut res = try!(match body {
            Some(ref bod) => builder.body(*bod).send(),
            _ => builder.send(),
        });

        let mut body = match res.headers.clone().get::<ContentLength>() {
            Some(&ContentLength(len)) => String::with_capacity(len as usize),
            _ => String::new(),
        };
        try!(res.read_to_string(&mut body));

        let links = res.headers.get::<Link>().map(|&Link(ref value)| Links::new(value.to_owned()));

        debug!("rec response {:#?} {:#?} {}", res.status, res.headers, body);
        match res.status {
            StatusCode::Conflict |
            StatusCode::BadRequest |
            StatusCode::UnprocessableEntity |
            StatusCode::Unauthorized |
            StatusCode::NotFound |
            StatusCode::Forbidden => {
                Err(Error::Fault {
                    code: res.status,
                    error: try!(serde_json::from_str::<errors::ClientError>(&body)),
                })
            }
            _ => Ok((links, try!(serde_json::from_str::<D>(&body)))),
        }
    }

    fn request_entity<D>(&self,
                         method: Method,
                         uri: String,
                         body: Option<&[u8]>,
                         media_type: MediaType)
                         -> Result<D>
        where D: Deserialize
    {
        self.request(method, uri, body, media_type).map(|(_, entity)| entity)
    }

    fn get<D>(&self, uri: &str) -> Result<D>
        where D: Deserialize
    {
        self.request_entity(Method::Get, self.host.clone() + uri, None, MediaType::Json)
    }

    fn delete(&self, uri: &str) -> Result<()> {
        match self.request_entity::<()>(Method::Delete,
                                        self.host.clone() + uri,
                                        None,
                                        MediaType::Json) {
            Err(Error::Codec(_)) => Ok(()),
            otherwise => otherwise,
        }
    }

    fn post<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request_entity(Method::Post,
                            self.host.clone() + uri,
                            Some(message),
                            MediaType::Json)
    }

    fn patch<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request_entity(Method::Patch,
                            self.host.clone() + uri,
                            Some(message),
                            MediaType::Json)
    }

    fn put<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request_entity(Method::Put,
                            self.host.clone() + uri,
                            Some(message),
                            MediaType::Json)
    }
}

pub struct Iter<'a, D, I> {
    github: &'a Github,
    next_link: Option<String>,
    into_items: fn(D) -> Vec<I>,
    items: Vec<I>,
}

impl<'a, D, I> Iter<'a, D, I>
    where D: Deserialize
{
    pub fn new(github: &'a Github,
               uri: String,
               into_items: fn(D) -> Vec<I>)
               -> Result<Iter<'a, D, I>> {
        let (links, payload) = try!(github.request::<D>(Method::Get, uri, None, MediaType::Json));
        Ok(Iter {
            github: github,
            next_link: links.and_then(|l| l.next()),
            into_items: into_items,
            items: into_items(payload),
        })
    }

    fn set_next(&mut self, next: Option<String>) {
        self.next_link = next;
    }
}

impl<'a, D, I> Iterator for Iter<'a, D, I>
    where D: Deserialize
{
    type Item = I;
    fn next(&mut self) -> Option<I> {
        self.items.pop().or_else(|| {
            self.next_link.clone().and_then(|ref next_link| {
                self.github
                    .request::<D>(Method::Get, next_link.to_owned(), None, MediaType::Json)
                    .ok()
                    .and_then(|(links, payload)| {
                        self.set_next(links.and_then(|l| l.next()));
                        self.items = (self.into_items)(payload);
                        self.next()
                    })
            })
        })
    }
}

#[derive(Debug)]
pub struct Links {
    values: HashMap<String, String>,
}

impl Links {
    pub fn new<V>(value: V) -> Links
        where V: Into<String>
    {
        let values = value.into()
            .split(",")
            .map(|link| {
                let parts = link.split(";").collect::<Vec<_>>();
                (parts[1].to_owned().replace(" rel=\"", "").replace("\"", ""),
                 parts[0].to_owned().replace("<", "").replace(">", "").replace(" ", ""))
            })
            .fold(HashMap::new(), |mut acc, (rel, link)| {
                acc.insert(rel, link);
                acc
            });
        Links { values: values }
    }

    pub fn next(&self) -> Option<String> {
        self.values.get("next").map(|s| s.to_owned())
    }

    pub fn prev(&self) -> Option<String> {
        self.values.get("prev").map(|s| s.to_owned())
    }

    pub fn last(&self) -> Option<String> {
        self.values.get("last").map(|s| s.to_owned())
    }
}

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

    #[test]
    fn test_parse_links() {
        let links = Links::new(r#"<linknext>; rel="next", <linklast>; rel="last""#);
        assert_eq!(links.next(), Some("linknext".to_owned()));
        assert_eq!(links.last(), Some("linklast".to_owned()));
    }

    #[test]
    fn default_sort_direction() {
        let default: SortDirection = Default::default();
        assert_eq!(default, SortDirection::Asc)
    }

    #[test]
    fn default_credentials() {
        let default: Credentials = Default::default();
        assert_eq!(default, Credentials::None)
    }
}
<commit_msg>update docs<commit_after>//! Hubcaps provides a set of building blocks for interacting with the Github API
//!
//! # Examples
//!
//!  Typical use will require instantiation of a Github client. Which requires
//! a user agent string, a `hyper::Client`, and set of `hubcaps::Credentials`.
//!
//! The hyper client should be configured with tls.
//!
//! ```no_run
//! extern crate hubcaps;
//! extern crate hyper;
//! extern crate hyper_native_tls;
//!
//! use hubcaps::{Credentials, Github};
//! use hyper::Client;
//! use hyper::net::HttpsConnector;
//! use hyper_native_tls::NativeTlsClient;
//!
//! fn main() {
//!   let github = Github::new(
//!     String::from("user-agent-name"),
//!     Client::with_connector(
//!       HttpsConnector::new(
//!         NativeTlsClient::new().unwrap()
//!       )
//!     ),
//!     Credentials::Token(
//!       String::from("personal-access-token")
//!     )
//!   );
//! }
//! ```
//!
//! Github enterprise users will want to create a client with the
//! [Github#host](struct.Github.html#method.host) method
//!
//! Access to various services are provided via methods on instances of the `Github` type.
//!
//! The convention for executing operations typically looks like
//! `github.repo(.., ..).service().operation(OperationOptions)` where operation may be `create`,
//! `delete`, ect.

//! Services and their types are packaged under their own module namespace.
//! A service interface will provide access to operations and operations may access options types
//! this define the various parameter options available for the operation. Most operation option
//! types expose `builder()` methods for a builder oriented style of constructing options.
//!
//! # Errors
//!
//! Operations typically result in a `hubcaps::Result` Type which is an alias for Rust's
//! built-in Result with the Err Type fixed to the
//! [hubcaps::Error](errors/enum.Error.html) enum type.
//!
#[macro_use]
extern crate log;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate url;

// all the modules!
use serde::de::Deserialize;
pub mod users;
pub mod comments;
pub mod review_comments;
pub mod pull_commits;
pub mod keys;
pub mod gists;
pub mod deployments;
pub mod errors;
pub mod hooks;
pub mod issues;
pub mod labels;
pub mod rep;
pub mod releases;
pub mod repositories;
pub mod statuses;
pub mod pulls;
pub mod search;
pub mod organizations;

// fixme: move reps to modules
pub use rep::*;

pub use errors::Error;
use gists::{Gists, UserGists};
use search::Search;
use hyper::Client;
use hyper::client::RequestBuilder;
use hyper::method::Method;
use hyper::header::{qitem, Accept, Authorization, ContentLength, UserAgent};
use hyper::mime::Mime;
use hyper::status::StatusCode;
use repositories::{Repository, Repositories, UserRepositories, OrganizationRepositories};
use organizations::{Organizations, UserOrganizations};
use std::fmt;
use std::io::Read;
use url::Url;
use std::collections::HashMap;

header! { (Link, "Link") => [String] }

const DEFAULT_HOST: &'static str = "https://api.github.com";

/// alias for Result that infers hubcaps::Error as Err
pub type Result<T> = std::result::Result<T, Error>;

enum MediaType {
    Json,
    Preview(&'static str),
}

impl Default for MediaType {
    fn default() -> MediaType {
        MediaType::Json
    }
}

impl From<MediaType> for Mime {
    fn from(media: MediaType) -> Mime {
        match media {
            MediaType::Json => "application/vnd.github.v3+json".parse().unwrap(),
            MediaType::Preview(codename) => {
                format!("application/vnd.github.{}-preview+json", codename).parse().unwrap()
            }
        }
    }
}

/// enum representation of Github list sorting options
#[derive(Clone, Debug, PartialEq)]
pub enum SortDirection {
    /// Sort in ascending order
    Asc,
    /// Sort in descending order
    Desc,
}

impl fmt::Display for SortDirection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{}",
               match *self {
                   SortDirection::Asc => "asc",
                   SortDirection::Desc => "desc",
               })
    }
}

impl Default for SortDirection {
    fn default() -> SortDirection {
        SortDirection::Asc
    }
}

/// Various forms of authentication credentials supported by Github
#[derive(Debug, PartialEq)]
pub enum Credentials {
    /// No authentication
    None,
    /// Oauth token string
    /// https://developer.github.com/v3/#oauth2-token-sent-in-a-header
    Token(String),
    /// Oauth client id and secret
    /// https://developer.github.com/v3/#oauth2-keysecret
    Client(String, String),
}

impl Default for Credentials {
    fn default() -> Credentials {
        Credentials::None
    }
}

/// Entry point interface for interacting with Github API
pub struct Github {
    host: String,
    agent: String,
    client: Client,
    credentials: Credentials,
}

impl Github {
    /// Create a new Github instance
    pub fn new<A>(agent: A, client: Client, credentials: Credentials) -> Github
        where A: Into<String>
    {
        Github::host(DEFAULT_HOST, agent, client, credentials)
    }

    /// Create a new Github instance hosted at a custom location.
    /// Useful for github enterprise installations ( yourdomain.com/api/v3/ )
    pub fn host<H, A>(host: H, agent: A, client: Client, credentials: Credentials) -> Github
        where H: Into<String>,
              A: Into<String>
    {
        Github {
            host: host.into(),
            agent: agent.into(),
            client: client,
            credentials: credentials,
        }
    }

    /// Return a reference to a Github reposistory
    pub fn repo<O, R>(&self, owner: O, repo: R) -> Repository
        where O: Into<String>,
              R: Into<String>
    {
        Repository::new(self, owner, repo)
    }

    /// Return a reference to the collection of repositories owned by and
    /// associated with an owner
    pub fn user_repos<S>(&self, owner: S) -> UserRepositories
        where S: Into<String>
    {
        UserRepositories::new(self, owner)
    }

    /// Return a reference to the collection of repositories owned by the user
    /// associated with the current authentication credentials
    pub fn repos(&self) -> Repositories {
        Repositories::new(self)
    }

    /// Return a reference to the collection of organizations that the user
    /// associated with the current authentication credentials is in
    pub fn orgs(&self) -> Organizations {
        Organizations::new(self)
    }

    /// Return a reference to the collection of organizations a user
    /// is publicly associated with
    pub fn user_orgs<U>(&self, user: U) -> UserOrganizations
        where U: Into<String>
    {
        UserOrganizations::new(self, user)
    }

    /// Return a reference to an interface that provides access to a user's gists
    pub fn user_gists<O>(&self, owner: O) -> UserGists
        where O: Into<String>
    {
        UserGists::new(self, owner)
    }

    /// Return a reference to an interface that provides access to the
    /// gists belonging to the owner of the token used to configure this client
    pub fn gists(&self) -> Gists {
        Gists::new(self)
    }

    pub fn search(&self) -> Search {
        Search::new(self)
    }

    /// Return a reference to the collection of repositories owned by and
    /// associated with an organization
    pub fn org_repos<O>(&self, org: O) -> OrganizationRepositories
        where O: Into<String>
    {
        OrganizationRepositories::new(self, org)
    }

    fn authenticate(&self, method: Method, url: String) -> RequestBuilder {
        match self.credentials {
            Credentials::Token(ref token) => {
                self.client.request(method, &url).header(Authorization(format!("token {}", token)))
            }
            Credentials::Client(ref id, ref secret) => {

                let mut parsed = Url::parse(&url).unwrap();
                parsed.query_pairs_mut()
                    .append_pair("client_id", id)
                    .append_pair("client_secret", secret);
                self.client.request(method, parsed)
            }
            Credentials::None => self.client.request(method, &url),
        }
    }

    fn iter<'a, D, I>(&'a self, uri: String, into_items: fn(D) -> Vec<I>) -> Result<Iter<'a, D, I>>
        where D: Deserialize
    {
        Iter::new(self, self.host.clone() + &uri, into_items)
    }

    fn request<D>(&self,
                  method: Method,
                  uri: String,
                  body: Option<&[u8]>,
                  media_type: MediaType)
                  -> Result<(Option<Links>, D)>
        where D: Deserialize
    {
        let builder = self.authenticate(method, uri)
            .header(UserAgent(self.agent.to_owned()))
            .header(Accept(vec![qitem(From::from(media_type))]));

        let mut res = try!(match body {
            Some(ref bod) => builder.body(*bod).send(),
            _ => builder.send(),
        });

        let mut body = match res.headers.clone().get::<ContentLength>() {
            Some(&ContentLength(len)) => String::with_capacity(len as usize),
            _ => String::new(),
        };
        try!(res.read_to_string(&mut body));

        let links = res.headers.get::<Link>().map(|&Link(ref value)| Links::new(value.to_owned()));

        debug!("rec response {:#?} {:#?} {}", res.status, res.headers, body);
        match res.status {
            StatusCode::Conflict |
            StatusCode::BadRequest |
            StatusCode::UnprocessableEntity |
            StatusCode::Unauthorized |
            StatusCode::NotFound |
            StatusCode::Forbidden => {
                Err(Error::Fault {
                    code: res.status,
                    error: try!(serde_json::from_str::<errors::ClientError>(&body)),
                })
            }
            _ => Ok((links, try!(serde_json::from_str::<D>(&body)))),
        }
    }

    fn request_entity<D>(&self,
                         method: Method,
                         uri: String,
                         body: Option<&[u8]>,
                         media_type: MediaType)
                         -> Result<D>
        where D: Deserialize
    {
        self.request(method, uri, body, media_type).map(|(_, entity)| entity)
    }

    fn get<D>(&self, uri: &str) -> Result<D>
        where D: Deserialize
    {
        self.request_entity(Method::Get, self.host.clone() + uri, None, MediaType::Json)
    }

    fn delete(&self, uri: &str) -> Result<()> {
        match self.request_entity::<()>(Method::Delete,
                                        self.host.clone() + uri,
                                        None,
                                        MediaType::Json) {
            Err(Error::Codec(_)) => Ok(()),
            otherwise => otherwise,
        }
    }

    fn post<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request_entity(Method::Post,
                            self.host.clone() + uri,
                            Some(message),
                            MediaType::Json)
    }

    fn patch<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request_entity(Method::Patch,
                            self.host.clone() + uri,
                            Some(message),
                            MediaType::Json)
    }

    fn put<D>(&self, uri: &str, message: &[u8]) -> Result<D>
        where D: Deserialize
    {
        self.request_entity(Method::Put,
                            self.host.clone() + uri,
                            Some(message),
                            MediaType::Json)
    }
}

pub struct Iter<'a, D, I> {
    github: &'a Github,
    next_link: Option<String>,
    into_items: fn(D) -> Vec<I>,
    items: Vec<I>,
}

impl<'a, D, I> Iter<'a, D, I>
    where D: Deserialize
{
    pub fn new(github: &'a Github,
               uri: String,
               into_items: fn(D) -> Vec<I>)
               -> Result<Iter<'a, D, I>> {
        let (links, payload) = try!(github.request::<D>(Method::Get, uri, None, MediaType::Json));
        Ok(Iter {
            github: github,
            next_link: links.and_then(|l| l.next()),
            into_items: into_items,
            items: into_items(payload),
        })
    }

    fn set_next(&mut self, next: Option<String>) {
        self.next_link = next;
    }
}

impl<'a, D, I> Iterator for Iter<'a, D, I>
    where D: Deserialize
{
    type Item = I;
    fn next(&mut self) -> Option<I> {
        self.items.pop().or_else(|| {
            self.next_link.clone().and_then(|ref next_link| {
                self.github
                    .request::<D>(Method::Get, next_link.to_owned(), None, MediaType::Json)
                    .ok()
                    .and_then(|(links, payload)| {
                        self.set_next(links.and_then(|l| l.next()));
                        self.items = (self.into_items)(payload);
                        self.next()
                    })
            })
        })
    }
}

#[derive(Debug)]
pub struct Links {
    values: HashMap<String, String>,
}

impl Links {
    pub fn new<V>(value: V) -> Links
        where V: Into<String>
    {
        let values = value.into()
            .split(",")
            .map(|link| {
                let parts = link.split(";").collect::<Vec<_>>();
                (parts[1].to_owned().replace(" rel=\"", "").replace("\"", ""),
                 parts[0].to_owned().replace("<", "").replace(">", "").replace(" ", ""))
            })
            .fold(HashMap::new(), |mut acc, (rel, link)| {
                acc.insert(rel, link);
                acc
            });
        Links { values: values }
    }

    pub fn next(&self) -> Option<String> {
        self.values.get("next").map(|s| s.to_owned())
    }

    pub fn prev(&self) -> Option<String> {
        self.values.get("prev").map(|s| s.to_owned())
    }

    pub fn last(&self) -> Option<String> {
        self.values.get("last").map(|s| s.to_owned())
    }
}

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

    #[test]
    fn test_parse_links() {
        let links = Links::new(r#"<linknext>; rel="next", <linklast>; rel="last""#);
        assert_eq!(links.next(), Some("linknext".to_owned()));
        assert_eq!(links.last(), Some("linklast".to_owned()));
    }

    #[test]
    fn default_sort_direction() {
        let default: SortDirection = Default::default();
        assert_eq!(default, SortDirection::Asc)
    }

    #[test]
    fn default_credentials() {
        let default: Credentials = Default::default();
        assert_eq!(default, Credentials::None)
    }
}
<|endoftext|>


<commit_before>#![crate_name = "urlencoded"]
#![license = "MIT"]

#![feature(default_type_params)]

//! URL Encoded Plugin for Iron.
//!
//! Parses "url encoded" data from client requests.
//! Capable of parsing both URL query strings and POST request bodies.

extern crate iron;
extern crate url;
extern crate serialize;

extern crate plugin;
extern crate typemap;

use iron::Request;

use url::form_urlencoded;
use std::collections::hashmap::{HashMap, Occupied, Vacant};
use std::str;

use plugin::{PluginFor, Phantom};
use typemap::Assoc;

/// Plugin for `Request` that extracts URL encoded data from the URL query string.
///
/// Use it like this: `req.get_ref::<UrlEncodedQuery>()`
pub struct UrlEncodedQuery;

/// Plugin for `Request` that extracts URL encoded data from the request body.
///
/// Use it like this: `req.get_ref::<UrlEncodedBody>()`
pub struct UrlEncodedBody;

/// Hashmap mapping strings to vectors of strings.
pub type QueryMap = HashMap<String, Vec<String>>;

impl Assoc<QueryMap> for UrlEncodedQuery {}
impl Assoc<QueryMap> for UrlEncodedBody {}

impl PluginFor<Request, QueryMap> for UrlEncodedQuery {
    fn eval(req: &mut Request, _: Phantom<UrlEncodedQuery>) -> Option<QueryMap> {
        match req.url.query {
            Some(ref query) => create_param_hashmap(query.as_slice()),
            None => None
        }
    }
}

impl PluginFor<Request, HashMap<String, Vec<String>>> for UrlEncodedBody {
    fn eval(req: &mut Request, _: Phantom<UrlEncodedBody>) -> Option<QueryMap> {
        str::from_utf8(req.body.as_slice()).and_then(create_param_hashmap)
    }
}

/// Parse a urlencoded string into an optional HashMap.
fn create_param_hashmap(data: &str) -> Option<HashMap<String, Vec<String>>> {
    match data {
        "" => None,
        _ => Some(combine_duplicates(form_urlencoded::parse_str(data)))
    }
}

/// Convert a list of (key, value) pairs into a hashmap with vector values.
fn combine_duplicates(q: Vec<(String, String)>) -> HashMap<String, Vec<String>> {

    let mut deduplicated: HashMap<String, Vec<String>> = HashMap::new();

    for (k, v) in q.into_iter() {
        match deduplicated.entry(k) {
            // Already a Vec here, push onto it
            Occupied(entry) => { entry.into_mut().push(v); },

            // No value, create a one-element Vec.
            Vacant(entry) => { entry.set(vec![v]); }
        };
    }

    deduplicated
}

#[test]
fn test_combine_duplicates() {
    let my_vec = vec!(("band".to_string(), "arctic monkeys".to_string()),
                      ("band".to_string(), "temper trap".to_string()),
                      ("color".to_string(),"green".to_string()));
    let answer = combine_duplicates(my_vec);
    let mut control = HashMap::new();
    control.insert("band".to_string(),
                   vec!("arctic monkeys".to_string(), "temper trap".to_string()));
    control.insert("color".to_string(), vec!("green".to_string()));
    assert_eq!(answer, control);
}
<commit_msg>(fix) Update to latest Iron and Rust.<commit_after>#![crate_name = "urlencoded"]
#![license = "MIT"]

#![feature(default_type_params)]

//! URL Encoded Plugin for Iron.
//!
//! Parses "url encoded" data from client requests.
//! Capable of parsing both URL query strings and POST request bodies.

extern crate iron;
extern crate url;
extern crate serialize;

extern crate plugin;
extern crate typemap;

use iron::Request;

use url::form_urlencoded;
use std::collections::hash_map::{HashMap, Occupied, Vacant};
use std::str;

use plugin::{PluginFor, Phantom};
use typemap::Assoc;

/// Plugin for `Request` that extracts URL encoded data from the URL query string.
///
/// Use it like this: `req.get_ref::<UrlEncodedQuery>()`
pub struct UrlEncodedQuery;

/// Plugin for `Request` that extracts URL encoded data from the request body.
///
/// Use it like this: `req.get_ref::<UrlEncodedBody>()`
pub struct UrlEncodedBody;

/// Hashmap mapping strings to vectors of strings.
pub type QueryMap = HashMap<String, Vec<String>>;

impl Assoc<QueryMap> for UrlEncodedQuery {}
impl Assoc<QueryMap> for UrlEncodedBody {}

impl PluginFor<Request, QueryMap> for UrlEncodedQuery {
    fn eval(req: &mut Request, _: Phantom<UrlEncodedQuery>) -> Option<QueryMap> {
        match req.url.query {
            Some(ref query) => create_param_hashmap(query.as_slice()),
            None => None
        }
    }
}

impl PluginFor<Request, HashMap<String, Vec<String>>> for UrlEncodedBody {
    fn eval(req: &mut Request, _: Phantom<UrlEncodedBody>) -> Option<QueryMap> {
        str::from_utf8(req.body.as_slice()).and_then(create_param_hashmap)
    }
}

/// Parse a urlencoded string into an optional HashMap.
fn create_param_hashmap(data: &str) -> Option<HashMap<String, Vec<String>>> {
    match data {
        "" => None,
        _ => Some(combine_duplicates(form_urlencoded::parse_str(data)))
    }
}

/// Convert a list of (key, value) pairs into a hashmap with vector values.
fn combine_duplicates(q: Vec<(String, String)>) -> HashMap<String, Vec<String>> {

    let mut deduplicated: HashMap<String, Vec<String>> = HashMap::new();

    for (k, v) in q.into_iter() {
        match deduplicated.entry(k) {
            // Already a Vec here, push onto it
            Occupied(entry) => { entry.into_mut().push(v); },

            // No value, create a one-element Vec.
            Vacant(entry) => { entry.set(vec![v]); }
        };
    }

    deduplicated
}

#[test]
fn test_combine_duplicates() {
    let my_vec = vec!(("band".to_string(), "arctic monkeys".to_string()),
                      ("band".to_string(), "temper trap".to_string()),
                      ("color".to_string(),"green".to_string()));
    let answer = combine_duplicates(my_vec);
    let mut control = HashMap::new();
    control.insert("band".to_string(),
                   vec!("arctic monkeys".to_string(), "temper trap".to_string()));
    control.insert("color".to_string(), vec!("green".to_string()));
    assert_eq!(answer, control);
}
<|endoftext|>


<commit_before>#![feature(core,str_char)]

extern crate core;

use core::iter::Iterator;

pub struct Splits<'l>{
	begin: &'l str,
	end  : &'l str
}

impl<'l> Splits<'l>{
	pub fn from_str(str: &'l str) -> Self{
		Splits{
			begin: unsafe{str.slice_unchecked(0,0)},
			end  : str
		}
	}
}

impl<'l> Iterator for Splits<'l>{
	type Item = (&'l str,&'l str);

	fn next(&mut self) -> Option<<Self as Iterator>::Item>{
		if self.end.is_empty(){
			None
		}else{
			let head_len = self.end.char_at(0).len_utf8();
			self.begin   = unsafe{self.begin.slice_unchecked(0,self.begin.len() + head_len)};
			self.end     = unsafe{self.end.slice_unchecked(head_len,self.end.len())};

			Some((self.begin,self.end))
		}
	}
}

pub struct SplitsChar<'l>{
	begin: &'l str,
	end  : &'l str
}

impl<'l> SplitsChar<'l>{
	pub fn from_str(str: &'l str) -> Self{
		SplitsChar{
			begin: unsafe{str.slice_unchecked(0,0)},
			end  : str
		}
	}
}

impl<'l> Iterator for SplitsChar<'l>{
	type Item = (&'l str,char,&'l str);

	fn next(&mut self) -> Option<<Self as Iterator>::Item>{
		if self.end.is_empty(){
			None
		}else{
			let c = self.end.char_at(0);
			let c_len = c.len_utf8();

			self.end   = unsafe{self.end.slice_unchecked(c_len,self.end.len())};
			let begin  = self.begin;
			self.begin = unsafe{self.begin.slice_unchecked(0,self.begin.len() + c_len)};

			Some((begin,c,self.end))
		}
	}
}

#[test]
fn test_splits(){
	let mut splits = Splits::from_str("0123456789");
	/*01*/assert_eq!(splits.next().unwrap(),("0","123456789"));
	/*02*/assert_eq!(splits.next().unwrap(),("01","23456789"));
	/*03*/assert_eq!(splits.next().unwrap(),("012","3456789"));
	/*04*/assert_eq!(splits.next().unwrap(),("0123","456789"));
	/*05*/assert_eq!(splits.next().unwrap(),("01234","56789"));
	/*06*/assert_eq!(splits.next().unwrap(),("012345","6789"));
	/*07*/assert_eq!(splits.next().unwrap(),("0123456","789"));
	/*08*/assert_eq!(splits.next().unwrap(),("01234567","89"));
	/*09*/assert_eq!(splits.next().unwrap(),("012345678","9"));
	/*10*/assert_eq!(splits.next().unwrap(),("0123456789",""));
	/*11*/assert!(splits.next().is_none());
}

#[test]
fn test_splits_empty(){
	let mut splits = Splits::from_str("");
	assert!(splits.next().is_none());
}

#[test]
fn test_splits_single(){
	let mut splits = Splits::from_str("a");
	/*01*/assert_eq!(splits.next().unwrap(),("a",""));
	/*02*/assert!(splits.next().is_none());
}

#[test]
fn test_splits_char(){
	let mut splits = SplitsChar::from_str("0123456789");
	/*01*/assert_eq!(splits.next().unwrap(),("",'0',"123456789"));
	/*02*/assert_eq!(splits.next().unwrap(),("0",'1',"23456789"));
	/*03*/assert_eq!(splits.next().unwrap(),("01",'2',"3456789"));
	/*04*/assert_eq!(splits.next().unwrap(),("012",'3',"456789"));
	/*05*/assert_eq!(splits.next().unwrap(),("0123",'4',"56789"));
	/*06*/assert_eq!(splits.next().unwrap(),("01234",'5',"6789"));
	/*07*/assert_eq!(splits.next().unwrap(),("012345",'6',"789"));
	/*08*/assert_eq!(splits.next().unwrap(),("0123456",'7',"89"));
	/*09*/assert_eq!(splits.next().unwrap(),("01234567",'8',"9"));
	/*10*/assert_eq!(splits.next().unwrap(),("012345678",'9',""));
	/*11*/assert!(splits.next().is_none());
}

#[test]
fn test_splits_char_empty(){
	let mut splits = SplitsChar::from_str("");
	assert!(splits.next().is_none());
}

#[test]
fn test_splits_char_single(){
	let mut splits = SplitsChar::from_str("a");
	/*01*/assert_eq!(splits.next().unwrap(),("",'a',""));
	/*02*/assert!(splits.next().is_none());
}

#[test]
fn test_splits_char_two(){
	let mut splits = SplitsChar::from_str("ab");
	/*01*/assert_eq!(splits.next().unwrap(),("",'a',"b"));
	/*02*/assert_eq!(splits.next().unwrap(),("a",'b',""));
	/*03*/assert!(splits.next().is_none());
}

#[test]
fn test_splits_char_three(){
	let mut splits = SplitsChar::from_str("abc");
	/*01*/assert_eq!(splits.next().unwrap(),("",'a',"bc"));
	/*02*/assert_eq!(splits.next().unwrap(),("a",'b',"c"));
	/*03*/assert_eq!(splits.next().unwrap(),("ab",'c',""));
	/*04*/assert!(splits.next().is_none());
}
<commit_msg>Updated to the latest version of rustc<commit_after>extern crate core;

use core::iter::Iterator;

pub struct Splits<'l>{
	begin: &'l str,
	end  : &'l str
}

impl<'l> Splits<'l>{
	pub fn from_str(str: &'l str) -> Self{
		Splits{
			begin: unsafe{str.slice_unchecked(0,0)},
			end  : str
		}
	}
}

impl<'l> Iterator for Splits<'l>{
	type Item = (&'l str,&'l str);

	fn next(&mut self) -> Option<<Self as Iterator>::Item>{
		self.end.chars().next().map(|head|{
			let head_len = head.len_utf8();

			self.begin   = unsafe{self.begin.slice_unchecked(0,self.begin.len() + head_len)};
			self.end     = unsafe{self.end.slice_unchecked(head_len,self.end.len())};

			(self.begin,self.end)
		})
	}
}

pub struct SplitsChar<'l>{
	begin: &'l str,
	end  : &'l str
}

impl<'l> SplitsChar<'l>{
	pub fn from_str(str: &'l str) -> Self{
		SplitsChar{
			begin: unsafe{str.slice_unchecked(0,0)},
			end  : str
		}
	}
}

impl<'l> Iterator for SplitsChar<'l>{
	type Item = (&'l str,char,&'l str);

	fn next(&mut self) -> Option<<Self as Iterator>::Item>{
		self.end.chars().next().map(|c|{
			let c_len = c.len_utf8();

			self.end   = unsafe{self.end.slice_unchecked(c_len,self.end.len())};
			let begin  = self.begin;
			self.begin = unsafe{self.begin.slice_unchecked(0,self.begin.len() + c_len)};

			(begin,c,self.end)
		})
	}
}

#[test]
fn test_splits(){
	let mut splits = Splits::from_str("0123456789");
	/*01*/assert_eq!(splits.next(),Some(("0","123456789")));
	/*02*/assert_eq!(splits.next(),Some(("01","23456789")));
	/*03*/assert_eq!(splits.next(),Some(("012","3456789")));
	/*04*/assert_eq!(splits.next(),Some(("0123","456789")));
	/*05*/assert_eq!(splits.next(),Some(("01234","56789")));
	/*06*/assert_eq!(splits.next(),Some(("012345","6789")));
	/*07*/assert_eq!(splits.next(),Some(("0123456","789")));
	/*08*/assert_eq!(splits.next(),Some(("01234567","89")));
	/*09*/assert_eq!(splits.next(),Some(("012345678","9")));
	/*10*/assert_eq!(splits.next(),Some(("0123456789","")));
	/*11*/assert_eq!(splits.next(),None);
}

#[test]
fn test_splits_empty(){
	let mut splits = Splits::from_str("");
	assert!(splits.next().is_none());
}

#[test]
fn test_splits_single(){
	let mut splits = Splits::from_str("a");
	/*01*/assert_eq!(splits.next().unwrap(),("a",""));
	/*02*/assert!(splits.next().is_none());
}

#[test]
fn test_splits_char(){
	let mut splits = SplitsChar::from_str("0123456789");
	/*01*/assert_eq!(splits.next(),Some(("",'0',"123456789")));
	/*02*/assert_eq!(splits.next(),Some(("0",'1',"23456789")));
	/*03*/assert_eq!(splits.next(),Some(("01",'2',"3456789")));
	/*04*/assert_eq!(splits.next(),Some(("012",'3',"456789")));
	/*05*/assert_eq!(splits.next(),Some(("0123",'4',"56789")));
	/*06*/assert_eq!(splits.next(),Some(("01234",'5',"6789")));
	/*07*/assert_eq!(splits.next(),Some(("012345",'6',"789")));
	/*08*/assert_eq!(splits.next(),Some(("0123456",'7',"89")));
	/*09*/assert_eq!(splits.next(),Some(("01234567",'8',"9")));
	/*10*/assert_eq!(splits.next(),Some(("012345678",'9',"")));
	/*11*/assert_eq!(splits.next(),None);
}

#[test]
fn test_splits_char_empty(){
	let mut splits = SplitsChar::from_str("");
	assert_eq!(splits.next(),None);
}

#[test]
fn test_splits_char_single(){
	let mut splits = SplitsChar::from_str("a");
	/*01*/assert_eq!(splits.next(),Some(("",'a',"")));
	/*02*/assert_eq!(splits.next(),None);
}

#[test]
fn test_splits_char_two(){
	let mut splits = SplitsChar::from_str("ab");
	/*01*/assert_eq!(splits.next(),Some(("",'a',"b")));
	/*02*/assert_eq!(splits.next(),Some(("a",'b',"")));
	/*03*/assert_eq!(splits.next(),None);
}

#[test]
fn test_splits_char_three(){
	let mut splits = SplitsChar::from_str("abc");
	/*01*/assert_eq!(splits.next(),Some(("",'a',"bc")));
	/*02*/assert_eq!(splits.next(),Some(("a",'b',"c")));
	/*03*/assert_eq!(splits.next(),Some(("ab",'c',"")));
	/*04*/assert_eq!(splits.next(),None);
}
<|endoftext|>


<commit_before>extern crate num;

// Must re-export num for the unit! macro.
#[doc(hidden)]pub use num as num_export;

mod vec2;
mod vec3;
mod vec4;
mod float;
mod clamp;
#[macro_use] pub mod unit;

pub use num::{Zero, One};
pub use self::vec2::{Vec2, Point2};
pub use self::vec3::{Vec3, Point3};
pub use self::vec4::Vec4;
pub use self::float::{EPSILON, Float};
pub use self::clamp::Clamp;
<commit_msg>reexport Num trait<commit_after>extern crate num;

// Must re-export num for the unit! macro.
#[doc(hidden)]pub use num as num_export;

mod vec2;
mod vec3;
mod vec4;
mod float;
mod clamp;
#[macro_use] pub mod unit;

pub use num::{Zero, One, Num};
pub use self::vec2::{Vec2, Point2};
pub use self::vec3::{Vec3, Point3};
pub use self::vec4::Vec4;
pub use self::float::{EPSILON, Float};
pub use self::clamp::Clamp;
<|endoftext|>


<commit_before>//! # Dynamic packet parsing within trees 🌲 🌳 🌴
//!
//! Target of this library is to provide a flexible parsing approach for network packets. This will be done within
//! [arena](https://en.wikipedia.org/wiki/Region-based_memory_management) based [parser trees](https://en.wikipedia.org/wiki/Parse_tree)
//! which can be modified during runtime. Other stacks beside the well known TCP/IP protocol family should be parsable too.
//!
//! ## Example usage
//!
//! ```
//! use peel::prelude::*;
//!
//! // Get the default tree based on the TCP/IP stack
//! let peel = default_peel();
//!
//! let eth_header = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 8, 0];
//!
//! // Traverse the parser tree. If a parser matches check for available child parsers.
//! // Stop parsing if there are no childs left. The `vec![]` memory will be used for
//! // the resulting stack of `Layer`s.
//! let result = peel.traverse(&eth_header, vec![]).unwrap();
//!
//! // There should be one parsed EthernetPacket in:
//! assert_eq!(result.len(), 1);
//! ```
//!
//! For a more advanced usage see the [`Peel`](struct.Peel.html) structure.
//!
#![deny(missing_docs)]

#[macro_use]
extern crate nom;

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate log;

extern crate regex;
extern crate term;

#[macro_use]
pub mod error;

#[macro_use]
pub mod memcmp;
pub mod packet;
pub mod arenatree;
pub mod parser;
mod logger;

use std::iter;
use std::collections::HashMap;
use log::LogLevelFilter;

use self::prelude::*;
use parser::ParserBox;
use logger::Logger;

/// Provides sensible imports at all
pub mod prelude {
    pub use super::Peel;
    pub use error::{PeelResult, PeelError, ErrorType};
    pub use arenatree::{Arena, NodeId, Node};
    pub use parser::{Parser, ParserNode, ParserArena, ParserState};

    pub use packet::prelude::*;
}

/// The main peeling structure
pub struct Peel<R, V> {
    /// The memory arena of the tree
    pub arena: Arena<ParserBox<R, V>>,

    /// The current result stack of the parsers
    pub result: Vec<R>,

    /// The first node added will be the root
    pub root: Option<NodeId>,
}

impl<R, V> Peel<R, V> where V: fmt::Display {
    /// Create a new emtpy `Peel` instance
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    /// let peel :PacketPeel = Peel::new();
    /// ```
    pub fn new() -> Self {
        Peel {
            arena: Arena::new(),
            result: vec![],
            root: None,
        }
    }

    /// Set the global log level for reporting
    ///
    /// # Examples
    /// ```
    /// # extern crate log;
    /// # extern crate peel;
    /// # fn main() {
    /// use log::LogLevelFilter;
    /// use peel::prelude::*;
    ///
    /// let mut peel = default_peel();
    /// peel.set_log_level(LogLevelFilter::Trace);
    /// # }
    /// ```
    pub fn set_log_level(&mut self, level: LogLevelFilter) {
        // Setup the logger if not already set
        if log::set_logger(|max_log_level| {
                max_log_level.set(level);
                Box::new(Logger)
            })
            .is_err() {
            warn!("Logger already set.");
        };
        info!("Log level set to: {:?}", level);
    }

    /// Create a new boxed Parser and return a corresponding Node
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let mut p = Peel::new();
    /// let eth = p.new_parser(EthernetParser);
    /// ```
    pub fn new_parser<T>(&mut self, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + Send + Sync + 'static
    {
        // Create a new node
        let new_node = self.arena.new_node(Box::new(parser));

        // Check if the root node is already set. If not, then this will be the root
        if let None = self.root {
            self.root = Some(new_node);
        }

        // Return the shiny new node
        new_node
    }

    /// Append the second node to the first one within the current tree structure
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let mut p = Peel::new();
    /// let eth = p.new_parser(EthernetParser);
    /// let ipv4 = p.new_parser(Ipv4Parser);
    /// p.link(eth, ipv4);
    /// ```
    pub fn link(&mut self, left: NodeId, right: NodeId) {
        left.append(right, &mut self.arena);
    }

    /// Create a new parser and link it with the provided node
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let mut p = Peel::new();
    /// let eth = p.new_parser(EthernetParser);
    /// let ipv4 = p.link_new_parser(eth, Ipv4Parser);
    /// ```
    pub fn link_new_parser<T>(&mut self, left: NodeId, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + Send + Sync + 'static
    {
        // Create a new node
        let new_parser = self.new_parser(parser);

        // Append the node to the given node
        left.append(new_parser, &mut self.arena);

        // Return the parser
        new_parser
    }

    /// Convenient function for recursive traversal with the root as starting point
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let peel = default_peel();
    /// let eth_header = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 8, 0];
    /// let result = peel.traverse(&eth_header, vec![]).unwrap();
    /// assert_eq!(result.len(), 1);
    /// ```
    ///
    /// # Errors
    /// When no tree root was found or the first parser already fails.
    pub fn traverse(&self, input: &[u8], result: Vec<R>) -> PeelResult<Vec<R>> {
        match self.root {
            Some(node) => self.traverse_recursive(node, input, result),
            None => bail!(ErrorType::NoTreeRoot, "No tree root found"),
        }
    }

    /// Do parsing until all possible paths failed. This is equivalent in finding the deepest
    /// possible parsing result within the tree. The result will be assembled together in the
    /// given result vector, which will be returned at the end.
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let peel = default_peel();
    /// let eth_header = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 8, 0];
    /// let result = peel.traverse_recursive(peel.root.unwrap(), &eth_header, vec![]).unwrap();
    /// assert_eq!(result.len(), 1);
    /// ```
    ///
    /// # Errors
    /// When the first parser already fails.
    pub fn traverse_recursive(&self, start_node: NodeId, input: &[u8], mut result: Vec<R>)
        -> PeelResult<Vec<R>> {

        for node_id in start_node.following_siblings(&self.arena) {
            // Get the initial values from the arena
            let ref node = self.arena[node_id];
            let ref parser = node.data;

            // Do the actual parsing work
            match parser.parse(input, Some(node), Some(&self.arena), Some(&result)) {
                IResult::Done(input_left, (parser_result, state)) => {
                    // Adapt the result
                    debug!("{} parsing succeed, left input length: {}",
                           parser.variant(), input_left.len());
                    result.push(parser_result);

                    // Check the parser state
                    let next_node = match state {
                        // Continue with the first child parser
                         ParserState::ContinueWithFirstChild => {
                            debug!("Continue traversal to first child of the parser");
                            node.first_child()
                         },

                         // Try the next sibling before parsing deeper
                         ParserState::ContinueWithNextSibling => {
                            debug!("Continue traversal to next sibling of the parser");
                            node.next_sibling()
                         },

                         // Try the next sibling before parsing deeper
                         ParserState::ContinueWithCurrent => {
                            debug!("Trying the current parser again");
                            Some(node_id)
                         },

                         // Immediately stop the parser here
                         ParserState::Stop => None,
                    };

                    // Continue traversal if needed
                    if let Some(node) = next_node {
                        result = self.traverse_recursive(node, input_left, result)?;
                    }

                    // Stop here since we already succeed
                    break;
                }
                error @ _ => if log_enabled!(log::LogLevel::Trace) {
                    if Some(start_node) == self.root {
                        bail!(ErrorType::RootParserFailed, "No parser succeed at all");
                    }
                    debug!("Failed parser: {}", parser.variant());
                    self.display_error(input, error);
                },
            }
        }
        Ok(result)
    }

    /// Display the trees children by recursive iteration
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let peel = default_peel();
    /// println!("{}", peel);
    /// ```
    fn display_children(&self, f: &mut fmt::Formatter, node: NodeId, mut level: usize)
        -> fmt::Result {
        level += 2;
        for child in node.children(&self.arena) {
            let indent = iter::repeat(' ').take(level).collect::<String>();
            let ref parser = self.arena[child].data;
            write!(f, "{}- {}: {:?}", indent, parser.variant(), child)?;
            writeln!(f, " ({})", self.arena[child])?;
            self.display_children(f, child, level)?;
        }
        Ok(())
    }

    /// Display an error from a parser
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let peel = default_peel();
    /// let eth_header = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0];
    ///
    /// let parser = EthernetParser;
    /// let res = parser.parse(&eth_header, None, None, None);
    /// peel.display_error(&eth_header, res);
    /// ```
    pub fn display_error(&self, input: &[u8], res: IResult<&[u8], (R, ParserState)>) {
        let mut h: HashMap<u32, &str> = HashMap::new();
        let parsers = ["Custom", "Tag", "MapRes", "MapOpt", "Alt", "IsNot", "IsA", "SeparatedList",
                       "SeparatedNonEmptyList", "Many1", "Count", "TakeUntilAndConsume",
                       "TakeUntil", "TakeUntilEitherAndConsume", "TakeUntilEither", "LengthValue",
                       "TagClosure", "Alpha", "Digit", "AlphaNumeric", "Space", "MultiSpace",
                       "LengthValueFn", "Eof", "ExprOpt", "ExprRes", "CondReduce", "Switch",
                       "TagBits", "OneOf", "NoneOf", "Char", "CrLf", "RegexpMatch",
                       "RegexpMatches", "RegexpFind", "RegexpCapture", "RegexpCaptures",
                       "TakeWhile1", "Complete", "Fix", "Escaped", "EscapedTransform", "TagStr",
                       "IsNotStr", "IsAStr", "TakeWhile1Str", "NonEmpty", "ManyMN",
                       "TakeUntilAndConsumeStr", "HexDigit", "TakeUntilStr", "OctDigit", "Many0",
                       "Not", "Permutation", "ManyTill"];

        for (i, literal) in parsers.iter().enumerate() {
            h.insert(i as u32, literal);
        }

        if let Some(v) = prepare_errors(input, res) {
            let colors = generate_colors(&v);
            println!("Colors: {}", print_codes(colors, h));
            println!("Dump: \n{}",   print_offsets(input, 0, &v));
        }
    }
}

impl<K, V> fmt::Display for Peel<K, V> where V: fmt::Display {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.root {
            Some(node) => {
                write!(f, "- {}: {:?}", self.arena[node].data.variant(), node)?;
                writeln!(f, " ({})", self.arena[node])?;
                self.display_children(f, node, 0)
            }
            None => write!(f, "(no tree root available)"),
        }
    }
}
<commit_msg>Removed useless result within main structure<commit_after>//! # Dynamic packet parsing within trees 🌲 🌳 🌴
//!
//! Target of this library is to provide a flexible parsing approach for network packets. This will be done within
//! [arena](https://en.wikipedia.org/wiki/Region-based_memory_management) based [parser trees](https://en.wikipedia.org/wiki/Parse_tree)
//! which can be modified during runtime. Other stacks beside the well known TCP/IP protocol family should be parsable too.
//!
//! ## Example usage
//!
//! ```
//! use peel::prelude::*;
//!
//! // Get the default tree based on the TCP/IP stack
//! let peel = default_peel();
//!
//! let eth_header = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 8, 0];
//!
//! // Traverse the parser tree. If a parser matches check for available child parsers.
//! // Stop parsing if there are no childs left. The `vec![]` memory will be used for
//! // the resulting stack of `Layer`s.
//! let result = peel.traverse(&eth_header, vec![]).unwrap();
//!
//! // There should be one parsed EthernetPacket in:
//! assert_eq!(result.len(), 1);
//! ```
//!
//! For a more advanced usage see the [`Peel`](struct.Peel.html) structure.
//!
#![deny(missing_docs)]

#[macro_use]
extern crate nom;

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate log;

extern crate regex;
extern crate term;

#[macro_use]
pub mod error;

#[macro_use]
pub mod memcmp;
pub mod packet;
pub mod arenatree;
pub mod parser;
mod logger;

use std::iter;
use std::collections::HashMap;
use log::LogLevelFilter;

use self::prelude::*;
use parser::ParserBox;
use logger::Logger;

/// Provides sensible imports at all
pub mod prelude {
    pub use super::Peel;
    pub use error::{PeelResult, PeelError, ErrorType};
    pub use arenatree::{Arena, NodeId, Node};
    pub use parser::{Parser, ParserNode, ParserArena, ParserState};

    pub use packet::prelude::*;
}

/// The main peeling structure
pub struct Peel<R, V> {
    /// The memory arena of the tree
    pub arena: Arena<ParserBox<R, V>>,

    /// The first node added will be the root
    pub root: Option<NodeId>,
}

impl<R, V> Peel<R, V> where V: fmt::Display {
    /// Create a new emtpy `Peel` instance
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    /// let peel :PacketPeel = Peel::new();
    /// ```
    pub fn new() -> Self {
        Peel {
            arena: Arena::new(),
            root: None,
        }
    }

    /// Set the global log level for reporting
    ///
    /// # Examples
    /// ```
    /// # extern crate log;
    /// # extern crate peel;
    /// # fn main() {
    /// use log::LogLevelFilter;
    /// use peel::prelude::*;
    ///
    /// let mut peel = default_peel();
    /// peel.set_log_level(LogLevelFilter::Trace);
    /// # }
    /// ```
    pub fn set_log_level(&mut self, level: LogLevelFilter) {
        // Setup the logger if not already set
        if log::set_logger(|max_log_level| {
                max_log_level.set(level);
                Box::new(Logger)
            })
            .is_err() {
            warn!("Logger already set.");
        };
        info!("Log level set to: {:?}", level);
    }

    /// Create a new boxed Parser and return a corresponding Node
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let mut p = Peel::new();
    /// let eth = p.new_parser(EthernetParser);
    /// ```
    pub fn new_parser<T>(&mut self, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + Send + Sync + 'static
    {
        // Create a new node
        let new_node = self.arena.new_node(Box::new(parser));

        // Check if the root node is already set. If not, then this will be the root
        if let None = self.root {
            self.root = Some(new_node);
        }

        // Return the shiny new node
        new_node
    }

    /// Append the second node to the first one within the current tree structure
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let mut p = Peel::new();
    /// let eth = p.new_parser(EthernetParser);
    /// let ipv4 = p.new_parser(Ipv4Parser);
    /// p.link(eth, ipv4);
    /// ```
    pub fn link(&mut self, left: NodeId, right: NodeId) {
        left.append(right, &mut self.arena);
    }

    /// Create a new parser and link it with the provided node
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let mut p = Peel::new();
    /// let eth = p.new_parser(EthernetParser);
    /// let ipv4 = p.link_new_parser(eth, Ipv4Parser);
    /// ```
    pub fn link_new_parser<T>(&mut self, left: NodeId, parser: T) -> NodeId
        where T: Parser<Result = R, Variant = V> + Send + Sync + 'static
    {
        // Create a new node
        let new_parser = self.new_parser(parser);

        // Append the node to the given node
        left.append(new_parser, &mut self.arena);

        // Return the parser
        new_parser
    }

    /// Convenient function for recursive traversal with the root as starting point
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let peel = default_peel();
    /// let eth_header = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 8, 0];
    /// let result = peel.traverse(&eth_header, vec![]).unwrap();
    /// assert_eq!(result.len(), 1);
    /// ```
    ///
    /// # Errors
    /// When no tree root was found or the first parser already fails.
    pub fn traverse(&self, input: &[u8], result: Vec<R>) -> PeelResult<Vec<R>> {
        match self.root {
            Some(node) => self.traverse_recursive(node, input, result),
            None => bail!(ErrorType::NoTreeRoot, "No tree root found"),
        }
    }

    /// Do parsing until all possible paths failed. This is equivalent in finding the deepest
    /// possible parsing result within the tree. The result will be assembled together in the
    /// given result vector, which will be returned at the end.
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let peel = default_peel();
    /// let eth_header = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 8, 0];
    /// let result = peel.traverse_recursive(peel.root.unwrap(), &eth_header, vec![]).unwrap();
    /// assert_eq!(result.len(), 1);
    /// ```
    ///
    /// # Errors
    /// When the first parser already fails.
    pub fn traverse_recursive(&self, start_node: NodeId, input: &[u8], mut result: Vec<R>)
        -> PeelResult<Vec<R>> {

        for node_id in start_node.following_siblings(&self.arena) {
            // Get the initial values from the arena
            let ref node = self.arena[node_id];
            let ref parser = node.data;

            // Do the actual parsing work
            match parser.parse(input, Some(node), Some(&self.arena), Some(&result)) {
                IResult::Done(input_left, (parser_result, state)) => {
                    // Adapt the result
                    debug!("{} parsing succeed, left input length: {}",
                           parser.variant(), input_left.len());
                    result.push(parser_result);

                    // Check the parser state
                    let next_node = match state {
                        // Continue with the first child parser
                         ParserState::ContinueWithFirstChild => {
                            debug!("Continue traversal to first child of the parser");
                            node.first_child()
                         },

                         // Try the next sibling before parsing deeper
                         ParserState::ContinueWithNextSibling => {
                            debug!("Continue traversal to next sibling of the parser");
                            node.next_sibling()
                         },

                         // Try the next sibling before parsing deeper
                         ParserState::ContinueWithCurrent => {
                            debug!("Trying the current parser again");
                            Some(node_id)
                         },

                         // Immediately stop the parser here
                         ParserState::Stop => None,
                    };

                    // Continue traversal if needed
                    if let Some(node) = next_node {
                        result = self.traverse_recursive(node, input_left, result)?;
                    }

                    // Stop here since we already succeed
                    break;
                }
                error @ _ => if log_enabled!(log::LogLevel::Trace) {
                    if Some(start_node) == self.root {
                        bail!(ErrorType::RootParserFailed, "No parser succeed at all");
                    }
                    debug!("Failed parser: {}", parser.variant());
                    self.display_error(input, error);
                },
            }
        }
        Ok(result)
    }

    /// Display the trees children by recursive iteration
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let peel = default_peel();
    /// println!("{}", peel);
    /// ```
    fn display_children(&self, f: &mut fmt::Formatter, node: NodeId, mut level: usize)
        -> fmt::Result {
        level += 2;
        for child in node.children(&self.arena) {
            let indent = iter::repeat(' ').take(level).collect::<String>();
            let ref parser = self.arena[child].data;
            write!(f, "{}- {}: {:?}", indent, parser.variant(), child)?;
            writeln!(f, " ({})", self.arena[child])?;
            self.display_children(f, child, level)?;
        }
        Ok(())
    }

    /// Display an error from a parser
    ///
    /// # Examples
    /// ```
    /// use peel::prelude::*;
    ///
    /// let peel = default_peel();
    /// let eth_header = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0];
    ///
    /// let parser = EthernetParser;
    /// let res = parser.parse(&eth_header, None, None, None);
    /// peel.display_error(&eth_header, res);
    /// ```
    pub fn display_error(&self, input: &[u8], res: IResult<&[u8], (R, ParserState)>) {
        let mut h: HashMap<u32, &str> = HashMap::new();
        let parsers = ["Custom", "Tag", "MapRes", "MapOpt", "Alt", "IsNot", "IsA", "SeparatedList",
                       "SeparatedNonEmptyList", "Many1", "Count", "TakeUntilAndConsume",
                       "TakeUntil", "TakeUntilEitherAndConsume", "TakeUntilEither", "LengthValue",
                       "TagClosure", "Alpha", "Digit", "AlphaNumeric", "Space", "MultiSpace",
                       "LengthValueFn", "Eof", "ExprOpt", "ExprRes", "CondReduce", "Switch",
                       "TagBits", "OneOf", "NoneOf", "Char", "CrLf", "RegexpMatch",
                       "RegexpMatches", "RegexpFind", "RegexpCapture", "RegexpCaptures",
                       "TakeWhile1", "Complete", "Fix", "Escaped", "EscapedTransform", "TagStr",
                       "IsNotStr", "IsAStr", "TakeWhile1Str", "NonEmpty", "ManyMN",
                       "TakeUntilAndConsumeStr", "HexDigit", "TakeUntilStr", "OctDigit", "Many0",
                       "Not", "Permutation", "ManyTill"];

        for (i, literal) in parsers.iter().enumerate() {
            h.insert(i as u32, literal);
        }

        if let Some(v) = prepare_errors(input, res) {
            let colors = generate_colors(&v);
            println!("Colors: {}", print_codes(colors, h));
            println!("Dump: \n{}",   print_offsets(input, 0, &v));
        }
    }
}

impl<K, V> fmt::Display for Peel<K, V> where V: fmt::Display {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.root {
            Some(node) => {
                write!(f, "- {}: {:?}", self.arena[node].data.variant(), node)?;
                writeln!(f, " ({})", self.arena[node])?;
                self.display_children(f, node, 0)
            }
            None => write!(f, "(no tree root available)"),
        }
    }
}
<|endoftext|>


<commit_before>//! Temporary files and directories.
//!
//! - Use the [`tempfile()`] function for temporary files
//! - Use the [`tempdir()`] function for temporary directories.
//!
//! # Design
//!
//! This crate provides several approaches to creating temporary files and directories.
//! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed.
//! [`TempDir`] and [`NamedTempFile`] both rely on Rust destructors for cleanup.
//!
//! When choosing between the temporary file variants, prefer `tempfile`
//! unless you either need to know the file's path or to be able to persist it.
//!
//! ## Resource Leaking
//!
//! `tempfile` will (almost) never fail to cleanup temporary resources but `TempDir` and `NamedTempFile` will if
//! their destructors don't run. This is because `tempfile` relies on the OS to cleanup the
//! underlying file so the file while `TempDir` and `NamedTempFile` rely on their destructors to do so.
//!
//! ## Security
//!
//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
//!
//! `tempfile` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
//! rely on file paths.
//!
//! ## Examples
//!
//! Create a temporary file and write some data into it:
//!
//! ```
//! # extern crate tempfile;
//! use tempfile::tempfile;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a file inside of `std::env::temp_dir()`.
//! let mut file = tempfile()?;
//!
//! writeln!(file, "Brian was here. Briefly.")?;
//! # Ok(())
//! # }
//! ```
//!
//! Create a temporary directory and add a file to it:
//!
//! ```
//! # extern crate tempfile;
//! use tempfile::tempdir;
//! use std::fs::File;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a directory inside of `std::env::temp_dir()`.
//! let dir = tempdir()?;
//!
//! let file_path = dir.path().join("my-temporary-note.txt");
//! let mut file = File::create(file_path)?;
//! writeln!(file, "Brian was here. Briefly.")?;
//!
//! // By closing the `TempDir` explicitly, we can check that it has
//! // been deleted successfully. If we don't close it explicitly,
//! // the directory will still be deleted when `dir` goes out
//! // of scope, but we won't know whether deleting the directory
//! // succeeded.
//! drop(file);
//! dir.close()?;
//! # Ok(())
//! # }
//! ```
//!
//! [`tempfile()`]: fn.tempfile.html
//! [`tempdir()`]: fn.tempdir.html
//! [`TempDir`]: struct.TempDir.html
//! [`NamedTempFile`]: struct.NamedTempFile.html
//! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://docs.rs/tempfile/2.2.0")]
#![cfg_attr(test, deny(warnings))]

extern crate rand;
extern crate remove_dir_all;

#[cfg(unix)]
extern crate libc;

#[cfg(windows)]
extern crate winapi;

#[cfg(target_os = "redox")]
extern crate syscall;

const NUM_RETRIES: u32 = 1 << 31;
const NUM_RAND_CHARS: usize = 6;

use std::path::Path;
use std::{env, io};

mod dir;
mod file;
mod util;

pub use dir::{tempdir, tempdir_in, TempDir};
pub use file::{tempfile, tempfile_in, NamedTempFile, PersistError, TempPath};

/// Create a new temporary file or directory with custom parameters.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Builder<'a, 'b> {
    random_len: usize,
    prefix: &'a str,
    suffix: &'b str,
}

impl<'a, 'b> Default for Builder<'a, 'b> {
    fn default() -> Self {
        Builder {
            random_len: ::NUM_RAND_CHARS,
            prefix: ".tmp",
            suffix: "",
        }
    }
}

impl<'a, 'b> Builder<'a, 'b> {
    /// Create a new `Builder`.
    ///
    /// # Examples
    ///
    /// Create a named temporary file and write some data into it:
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # use std::ffi::OsStr;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// use tempfile::Builder;
    ///
    /// let named_tempfile = Builder::new()
    ///     .prefix("my-temporary-note")
    ///     .suffix(".txt")
    ///     .rand_bytes(5)
    ///     .tempfile()?;
    ///
    /// let name = named_tempfile
    ///     .path()
    ///     .file_name().and_then(OsStr::to_str);
    ///
    /// if let Some(name) = name {
    ///     assert!(name.starts_with("my-temporary-note"));
    ///     assert!(name.ends_with(".txt"));
    ///     assert_eq!(name.len(), "my-temporary-note.txt".len() + 5);
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Create a temporary directory and add a file to it:
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io::{self, Write};
    /// # use std::fs::File;
    /// # use std::ffi::OsStr;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// use tempfile::Builder;
    ///
    /// let dir = Builder::new()
    ///     .prefix("my-temporary-dir")
    ///     .rand_bytes(5)
    ///     .tempdir()?;
    ///
    /// let file_path = dir.path().join("my-temporary-note.txt");
    /// let mut file = File::create(file_path)?;
    /// writeln!(file, "Brian was here. Briefly.")?;
    ///
    /// // By closing the `TempDir` explicitly, we can check that it has
    /// // been deleted successfully. If we don't close it explicitly,
    /// // the directory will still be deleted when `dir` goes out
    /// // of scope, but we won't know whether deleting the directory
    /// // succeeded.
    /// drop(file);
    /// dir.close()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new() -> Self {
        Self::default()
    }

    /// Set a custom filename prefix.
    ///
    /// Path separators are legal but not advisable.
    /// Default: `.tmp`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .prefix("my-temporary-note")
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn prefix(&mut self, prefix: &'a str) -> &mut Self {
        self.prefix = prefix;
        self
    }

    /// Set a custom filename suffix.
    ///
    /// Path separators are legal but not advisable.
    /// Default: empty.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .suffix(".txt")
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn suffix(&mut self, suffix: &'b str) -> &mut Self {
        self.suffix = suffix;
        self
    }

    /// Set the number of random bytes.
    ///
    /// Default: `6`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .rand_bytes(5)
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
        self.random_len = rand;
        self
    }

    /// Create the named temporary file.
    ///
    /// # Security
    ///
    /// See [the security][security] docs on `NamedTempFile`.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
    ///
    /// # Errors
    ///
    /// If the file cannot be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let tempfile = Builder::new().tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [security]: struct.NamedTempFile.html#security
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
    pub fn tempfile(&self) -> io::Result<NamedTempFile> {
        self.tempfile_in(&env::temp_dir())
    }

    /// Create the named temporary file in the specified directory.
    ///
    /// # Security
    ///
    /// See [the security][security] docs on `NamedTempFile`.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
    ///
    /// # Errors
    ///
    /// If the file cannot be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let tempfile = Builder::new().tempfile_in("./")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [security]: struct.NamedTempFile.html#security
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
    pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
        util::create_helper(
            dir.as_ref(),
            self.prefix,
            self.suffix,
            self.random_len,
            file::create_named,
        )
    }

    /// Attempts to make a temporary directory inside of `env::temp_dir()` whose
    /// name will have the prefix, `prefix`. The directory and
    /// everything inside it will be automatically deleted once the
    /// returned `TempDir` is destroyed.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::Builder;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = Builder::new().tempdir()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
    pub fn tempdir(&self) -> io::Result<TempDir> {
        self.tempdir_in(&env::temp_dir())
    }

    /// Attempts to make a temporary directory inside of `dir`.
    /// The directory and everything inside it will be automatically
    /// deleted once the returned `TempDir` is destroyed.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::{self, File};
    /// use std::io::Write;
    /// use tempfile::Builder;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = Builder::new().tempdir_in("./")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
    pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
        let storage;
        let mut dir = dir.as_ref();
        if !dir.is_absolute() {
            let cur_dir = env::current_dir()?;
            storage = cur_dir.join(dir);
            dir = &storage;
        }

        util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create)
    }
}
<commit_msg>slight doc fixes<commit_after>//! Temporary files and directories.
//!
//! - Use the [`tempfile()`] function for temporary files
//! - Use the [`tempdir()`] function for temporary directories.
//!
//! # Design
//!
//! This crate provides several approaches to creating temporary files and directories.
//! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed.
//! [`TempDir`] and [`NamedTempFile`] both rely on Rust destructors for cleanup.
//!
//! When choosing between the temporary file variants, prefer `tempfile`
//! unless you either need to know the file's path or to be able to persist it.
//!
//! ## Resource Leaking
//!
//! `tempfile` will (almost) never fail to cleanup temporary resources, but `TempDir` and `NamedTempFile` will if
//! their destructors don't run. This is because `tempfile` relies on the OS to cleanup the
//! underlying file, while `TempDir` and `NamedTempFile` rely on their destructors to do so.
//!
//! ## Security
//!
//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
//!
//! `tempfile` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
//! rely on file paths.
//!
//! ## Examples
//!
//! Create a temporary file and write some data into it:
//!
//! ```
//! # extern crate tempfile;
//! use tempfile::tempfile;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a file inside of `std::env::temp_dir()`.
//! let mut file = tempfile()?;
//!
//! writeln!(file, "Brian was here. Briefly.")?;
//! # Ok(())
//! # }
//! ```
//!
//! Create a temporary directory and add a file to it:
//!
//! ```
//! # extern crate tempfile;
//! use tempfile::tempdir;
//! use std::fs::File;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! #     if let Err(_) = run() {
//! #         ::std::process::exit(1);
//! #     }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a directory inside of `std::env::temp_dir()`.
//! let dir = tempdir()?;
//!
//! let file_path = dir.path().join("my-temporary-note.txt");
//! let mut file = File::create(file_path)?;
//! writeln!(file, "Brian was here. Briefly.")?;
//!
//! // By closing the `TempDir` explicitly, we can check that it has
//! // been deleted successfully. If we don't close it explicitly,
//! // the directory will still be deleted when `dir` goes out
//! // of scope, but we won't know whether deleting the directory
//! // succeeded.
//! drop(file);
//! dir.close()?;
//! # Ok(())
//! # }
//! ```
//!
//! [`tempfile()`]: fn.tempfile.html
//! [`tempdir()`]: fn.tempdir.html
//! [`TempDir`]: struct.TempDir.html
//! [`NamedTempFile`]: struct.NamedTempFile.html
//! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://docs.rs/tempfile/2.2.0")]
#![cfg_attr(test, deny(warnings))]

extern crate rand;
extern crate remove_dir_all;

#[cfg(unix)]
extern crate libc;

#[cfg(windows)]
extern crate winapi;

#[cfg(target_os = "redox")]
extern crate syscall;

const NUM_RETRIES: u32 = 1 << 31;
const NUM_RAND_CHARS: usize = 6;

use std::path::Path;
use std::{env, io};

mod dir;
mod file;
mod util;

pub use dir::{tempdir, tempdir_in, TempDir};
pub use file::{tempfile, tempfile_in, NamedTempFile, PersistError, TempPath};

/// Create a new temporary file or directory with custom parameters.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Builder<'a, 'b> {
    random_len: usize,
    prefix: &'a str,
    suffix: &'b str,
}

impl<'a, 'b> Default for Builder<'a, 'b> {
    fn default() -> Self {
        Builder {
            random_len: ::NUM_RAND_CHARS,
            prefix: ".tmp",
            suffix: "",
        }
    }
}

impl<'a, 'b> Builder<'a, 'b> {
    /// Create a new `Builder`.
    ///
    /// # Examples
    ///
    /// Create a named temporary file and write some data into it:
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # use std::ffi::OsStr;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// use tempfile::Builder;
    ///
    /// let named_tempfile = Builder::new()
    ///     .prefix("my-temporary-note")
    ///     .suffix(".txt")
    ///     .rand_bytes(5)
    ///     .tempfile()?;
    ///
    /// let name = named_tempfile
    ///     .path()
    ///     .file_name().and_then(OsStr::to_str);
    ///
    /// if let Some(name) = name {
    ///     assert!(name.starts_with("my-temporary-note"));
    ///     assert!(name.ends_with(".txt"));
    ///     assert_eq!(name.len(), "my-temporary-note.txt".len() + 5);
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Create a temporary directory and add a file to it:
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io::{self, Write};
    /// # use std::fs::File;
    /// # use std::ffi::OsStr;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// use tempfile::Builder;
    ///
    /// let dir = Builder::new()
    ///     .prefix("my-temporary-dir")
    ///     .rand_bytes(5)
    ///     .tempdir()?;
    ///
    /// let file_path = dir.path().join("my-temporary-note.txt");
    /// let mut file = File::create(file_path)?;
    /// writeln!(file, "Brian was here. Briefly.")?;
    ///
    /// // By closing the `TempDir` explicitly, we can check that it has
    /// // been deleted successfully. If we don't close it explicitly,
    /// // the directory will still be deleted when `dir` goes out
    /// // of scope, but we won't know whether deleting the directory
    /// // succeeded.
    /// drop(file);
    /// dir.close()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new() -> Self {
        Self::default()
    }

    /// Set a custom filename prefix.
    ///
    /// Path separators are legal but not advisable.
    /// Default: `.tmp`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .prefix("my-temporary-note")
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn prefix(&mut self, prefix: &'a str) -> &mut Self {
        self.prefix = prefix;
        self
    }

    /// Set a custom filename suffix.
    ///
    /// Path separators are legal but not advisable.
    /// Default: empty.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .suffix(".txt")
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn suffix(&mut self, suffix: &'b str) -> &mut Self {
        self.suffix = suffix;
        self
    }

    /// Set the number of random bytes.
    ///
    /// Default: `6`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let named_tempfile = Builder::new()
    ///     .rand_bytes(5)
    ///     .tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
        self.random_len = rand;
        self
    }

    /// Create the named temporary file.
    ///
    /// # Security
    ///
    /// See [the security][security] docs on `NamedTempFile`.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
    ///
    /// # Errors
    ///
    /// If the file cannot be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let tempfile = Builder::new().tempfile()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [security]: struct.NamedTempFile.html#security
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
    pub fn tempfile(&self) -> io::Result<NamedTempFile> {
        self.tempfile_in(&env::temp_dir())
    }

    /// Create the named temporary file in the specified directory.
    ///
    /// # Security
    ///
    /// See [the security][security] docs on `NamedTempFile`.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
    ///
    /// # Errors
    ///
    /// If the file cannot be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tempfile;
    /// # use std::io;
    /// # fn main() {
    /// #     if let Err(_) = run() {
    /// #         ::std::process::exit(1);
    /// #     }
    /// # }
    /// # fn run() -> Result<(), io::Error> {
    /// # use tempfile::Builder;
    /// let tempfile = Builder::new().tempfile_in("./")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [security]: struct.NamedTempFile.html#security
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
    pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
        util::create_helper(
            dir.as_ref(),
            self.prefix,
            self.suffix,
            self.random_len,
            file::create_named,
        )
    }

    /// Attempts to make a temporary directory inside of `env::temp_dir()` whose
    /// name will have the prefix, `prefix`. The directory and
    /// everything inside it will be automatically deleted once the
    /// returned `TempDir` is destroyed.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::Write;
    /// use tempfile::Builder;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = Builder::new().tempdir()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
    pub fn tempdir(&self) -> io::Result<TempDir> {
        self.tempdir_in(&env::temp_dir())
    }

    /// Attempts to make a temporary directory inside of `dir`.
    /// The directory and everything inside it will be automatically
    /// deleted once the returned `TempDir` is destroyed.
    ///
    /// # Resource leaking
    ///
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
    ///
    /// # Errors
    ///
    /// If the directory can not be created, `Err` is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::{self, File};
    /// use std::io::Write;
    /// use tempfile::Builder;
    ///
    /// # use std::io;
    /// # fn run() -> Result<(), io::Error> {
    /// let tmp_dir = Builder::new().tempdir_in("./")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
    pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
        let storage;
        let mut dir = dir.as_ref();
        if !dir.is_absolute() {
            let cur_dir = env::current_dir()?;
            storage = cur_dir.join(dir);
            dir = &storage;
        }

        util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create)
    }
}
<|endoftext|>