<commit_before>//! Library service providers implementation.

use reqwest::{Client, Response};
use reqwest::header::{ContentType, UserAgent};
use url::form_urlencoded;

/// A user agent for faking weird serices.
const FAKE_USER_AGENT: &str =
    "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0";

/// A slice of all `Provider` variants which do not require authentication.
/// This list is in order of provider quality.
///
/// The providers which are discouraged from use - due to problems such as rate
/// limitations - are at the end of the resultant slice.
///
/// Note that some providers may not provide a generated short URL because the
/// submitted URL may already be short enough and would not benefit from
/// shortening via their service.
pub const PROVIDERS: &[Provider] = &[
    Provider::IsGd,
    Provider::BnGy,
    Provider::VGd,
    Provider::BamBz,
    Provider::TinyPh,
    Provider::FifoCc,
    Provider::SCoop,
    Provider::Bmeo,
    Provider::UrlShortenerIo,
    Provider::HmmRs,
    // The following list are items that have long response sometimes:
    Provider::TnyIm,
    // The following list are items that are discouraged from use:

        // Reasons:
        //
        // * rate limit (250 requests per 15 minutes)
        // * does not accept short urls (ex: http://google.com)
    Provider::SirBz,
    // Reason: rate limit (100 requests per hour)
    Provider::Rlu,
    // Reason: rate limit (3000 requests per day)
    Provider::HecSu,
    // Reason: rate limit (20r/3min; 60r/15min for a UNIQUE urls only)
    Provider::Abv8,
    // Reason: does not provide an api
    Provider::TinyUrl,
    // Reason: unstable work
    Provider::PsbeCo,
    // The following list are items that show previews instead of direct
    // links.
    Provider::NowLinks,
];

macro_rules! parse_xml_tag {
    ($fname: ident, $tag: expr) => {
        fn $fname(res: &str) -> Option<String> {
            res.split(&format!("<{}>", $tag))
                .nth(1)
                .unwrap_or("")
                .split(&format!("</{}>", $tag))
                .next()
                .map(String::from)
        }
    }
}

macro_rules! parse_json_tag {
    ($fname: ident, $tag: expr, $prefix: expr) => {
        fn $fname(res: &str) -> Option<String> {
            res.to_owned()
                .split(&format!("\"{}\"", $tag))
                .nth(1)
                .unwrap_or("")
                .split(",")
                .next()
                .unwrap_or("")
                .split("\"")
                .nth(1)
                .map(|v| format!("{}{}", $prefix, v.replace("\\", "")))
        }
    }
}

macro_rules! parse {
    ($name:ident) => {
        fn $name(res: &str) -> Option<String> {
            Some(res.to_owned())
        }
    };
}

macro_rules! request {
    ($name:ident, $method:ident, $req_url:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            let url = form_urlencoded::byte_serialize(url.as_bytes())
                .collect::<String>();
            client.$method(&format!($req_url, url)).send().ok()
        }
    };

    (B, $name:ident, $method: ident, $req_url:expr, $body:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            client.$method($req_url).body(format!($body, url)).send().ok()
        }
    };

    ($name:ident, $method:ident, $req_url:expr, $body:expr, $header:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            client.$method($req_url).body(format!($body, url)).header($header).send().ok()
        }
    };
}

/// Used to specify which provider to use to generate a short URL.
#[derive(Clone, Debug)]
pub enum Provider {
    /// http://abv8.me provider
    ///
    /// Notes:
    ///
    /// * You may not shorten more than 20 unique URLs within a 3-minute period.
    /// * You may not shorten more than 60 unique URLs within a 15-minute
    ///   period.
    Abv8,
    /// https://bam.bz provider
    BamBz,
    /// https://bit.ly provider
    BitLy { token: String },
    /// http://bmeo.org provider
    Bmeo,
    /// https://bn.gy provider
    BnGy,
    /// http://fifo.cc provider
    FifoCc,
    /// https://goo.gl provider of Google
    GooGl { api_key: String },
    /// https://hec.su provider
    ///
    /// Notes:
    ///
    /// * Limited to 3000 API requests per day
    HecSu,
    /// http://hmm.rs provider
    HmmRs,
    /// https://is.gd provider
    IsGd,
    /// http://nowlinks.net provider
    NowLinks,
    /// http://phx.co.in provider
    ///
    /// Notes:
    ///
    /// * After some time the service will display ads
    /// * Instead of redirecting, a preview page will be displayed
    /// * Currently unstable
    PhxCoIn,
    /// http://psbe.co provider
    PsbeCo,
    /// http://s.coop provider
    SCoop,
    /// http://rlu.ru provider
    ///
    /// Notes:
    ///
    /// * If you send a lot of requests from one IP, it can be
    ///   blocked. If you plan to add more then 100 URLs in one hour, please let
    ///   the technical support know. Otherwise your IP can be blocked
    ///   unexpectedly. Prior added URLs can be deleted.
    Rlu,
    /// http://sirbz.com provider
    ///
    /// Notes:
    ///
    /// * By default, you are limited to 250 requests per 15 minutes.
    SirBz,
    /// http://tinyurl.com provider
    ///
    /// Notes:
    ///
    /// * This service does not provide any API.
    /// * The implementation result depends on the service result web page.
    TinyUrl,
    /// http://tiny.ph provider
    TinyPh,
    /// http://tny.im provider
    TnyIm,
    /// http://url-shortener.io provider
    UrlShortenerIo,
    /// https://v.gd provider
    VGd,
}

impl Provider {
    /// Converts the Provider variant into its domain name equivilant
    pub fn to_name(&self) -> &str {
        match *self {
            Provider::Abv8 => "abv8.me",
            Provider::BamBz => "bam.bz",
            Provider::BitLy { .. } => "bitly.com",
            Provider::Bmeo => "bmeo.org",
            Provider::BnGy => "bn.gy",
            Provider::FifoCc => "fifo.cc",
            Provider::GooGl { .. } => "goo.gl",
            Provider::HmmRs => "hmm.rs",
            Provider::HecSu => "hec.su",
            Provider::IsGd => "is.gd",
            Provider::NowLinks => "nowlinks.net",
            Provider::PhxCoIn => "phx.co.in",
            Provider::PsbeCo => "psbe.co",
            Provider::SCoop => "s.coop",
            Provider::SirBz => "sirbz.com",
            Provider::Rlu => "rlu.ru",
            Provider::TinyUrl => "tinyurl.com",
            Provider::TinyPh => "tiny.ph",
            Provider::TnyIm => "tny.im",
            Provider::UrlShortenerIo => "url-shortener.io",
            Provider::VGd => "v.gd",
        }
    }
}

parse!(abv8_parse);
request!(abv8_req, get, "http://abv8.me/?url={}");

parse_json_tag!(bambz_parse, "url", "");
request!(
    bambz_req,
    post,
    "https://bam.bz/api/short",
    "target={}",
    ContentType::form_url_encoded()
);

parse!(bitly_parse);
fn bitly_req(url: &str, key: &str, client: &Client) -> Option<Response> {
    let address = format!(
        "https://api-ssl.bitly.com/v3/shorten?access_token={}&longUrl={}&format=txt",
        key, url
    );
    client.get(&address).send().ok()
}

parse_json_tag!(bmeo_parse, "short", "");
request!(bmeo_req, get, "http://bmeo.org/api.php?url={}");

parse_xml_tag!(bngy_parse, "ShortenedUrl");
request!(
    bngy_req,
    get,
    "https://bn.gy/API.asmx/CreateUrl?real_url={}"
);

parse_json_tag!(fifocc_parse, "shortner", "http://fifo.cc/");
request!(fifocc_req, get, "https://fifo.cc/api/v2?url={}");

parse_json_tag!(googl_parse, "id", "");
fn googl_req(url: &str, key: &str, client: &Client) -> Option<Response> {
    client
        .post(&format!(
            "https://www.googleapis.com/urlshortener/v1/url?key={}",
            key
        ))
        .body(format!(r#"{{"longUrl": "{}"}}"#, url))
        .header(ContentType::json())
        .send()
        .ok()
}

parse_json_tag!(hmmrs_parse, "shortUrl", "");
fn hmmrs_req(url: &str, client: &Client) -> Option<Response> {
    client
        .post("http:/hmm.rs/x/shorten")
        .body(format!(r#"{{"url": "{}"}}"#, url))
        .header(ContentType::json())
        .header(UserAgent::new(FAKE_USER_AGENT))
        .send()
        .ok()
}

parse_xml_tag!(hecsu_parse, "short");
request!(hecsu_req, get, "https://hec.su/api?url={}&method=xml");

parse!(isgd_parse);
request!(
    isgd_req,
    get,
    "https://is.gd/create.php?format=simple&url={}"
);

parse!(nowlinks_parse);
request!(nowlinks_req, get, "http://nowlinks.net/api?url={}");

parse!(phxcoin_parse);
request!(phxcoin_req, get, "http://phx.co.in/shrink.asp?url={}");

parse_xml_tag!(psbeco_parse, "ShortUrl");
request!(
    psbeco_req,
    get,
    "http://psbe.co/API.asmx/CreateUrl?real_url={}"
);

parse!(scoop_parse);
request!(
    scoop_req,
    get,
    "http://s.coop/devapi.php?action=shorturl&url={}&format=RETURN"
);

parse!(rlu_parse);
request!(rlu_req, get, "http://rlu.ru/index.sema?a=api&link={}");

parse_json_tag!(sirbz_parse, "short_link", "");
request!(
    sirbz_req,
    post,
    "http://sirbz.com/api/shorten_url",
    "url={}",
    ContentType::form_url_encoded()
);

fn tinyurl_parse(res: &str) -> Option<String> {
    res.split("data-clipboard-text=\"")
        .nth(1)
        .unwrap_or("")
        .split("\">")
        .next()
        .map(String::from)
}
request!(tinyurl_req, get, "http://tinyurl.com/create.php?url={}");

parse_json_tag!(tinyph_parse, "hash", "http://tiny.ph/");
request!(
    tinyph_req,
    post,
    "http://tiny.ph/api/url/create",
    "url={}",
    ContentType::form_url_encoded()
);

parse_xml_tag!(tnyim_parse, "shorturl");
request!(
    tnyim_req,
    get,
    "http://tny.im/yourls-api.php?action=shorturl&url={}"
);

parse!(urlshortenerio_parse);
request!(
    urlshortenerio_req,
    post,
    "http://url-shortener.io/shorten",
    "url_param={}",
    ContentType::form_url_encoded()
);

parse!(vgd_parse);
request!(vgd_req, get, "http://is.gd/create.php?format=simple&url={}");

/// Parses the response from a successful request to a provider into the
/// URL-shortened string.
pub fn parse(res: &str, provider: &Provider) -> Option<String> {
    match *provider {
        Provider::Abv8 => abv8_parse(res),
        Provider::BamBz => bambz_parse(res),
        Provider::BitLy { .. } => bitly_parse(res),
        Provider::Bmeo => bmeo_parse(res),
        Provider::BnGy => bngy_parse(res),
        Provider::FifoCc => fifocc_parse(res),
        Provider::GooGl { .. } => googl_parse(res),
        Provider::HmmRs => hmmrs_parse(res),
        Provider::HecSu => hecsu_parse(res),
        Provider::IsGd => isgd_parse(res),
        Provider::NowLinks => nowlinks_parse(res),
        Provider::PhxCoIn => phxcoin_parse(res),
        Provider::PsbeCo => psbeco_parse(res),
        Provider::SCoop => scoop_parse(res),
        Provider::SirBz => sirbz_parse(res),
        Provider::Rlu => rlu_parse(res),
        Provider::TinyUrl => tinyurl_parse(res),
        Provider::TinyPh => tinyph_parse(res),
        Provider::TnyIm => tnyim_parse(res),
        Provider::UrlShortenerIo => urlshortenerio_parse(res),
        Provider::VGd => vgd_parse(res),
    }
}

/// Performs a request to the short link provider.
/// Returns the parsed response on success or a `None` on error.
pub fn request(url: &str, client: &Client, provider: &Provider) -> Option<Response> {
    match *provider {
        Provider::Abv8 => abv8_req(url, client),
        Provider::BamBz => bambz_req(url, client),
        Provider::BitLy { token: ref key } => bitly_req(url, &key, client),
        Provider::Bmeo => bmeo_req(url, client),
        Provider::BnGy => bngy_req(url, client),
        Provider::FifoCc => fifocc_req(url, client),
        Provider::GooGl { api_key: ref key } => googl_req(url, &key, client),
        Provider::HmmRs => hmmrs_req(url, client),
        Provider::HecSu => hecsu_req(url, client),
        Provider::IsGd => isgd_req(url, client),
        Provider::NowLinks => nowlinks_req(url, client),
        Provider::PhxCoIn => phxcoin_req(url, client),
        Provider::PsbeCo => psbeco_req(url, client),
        Provider::SCoop => scoop_req(url, client),
        Provider::SirBz => sirbz_req(url, client),
        Provider::Rlu => rlu_req(url, client),
        Provider::TinyUrl => tinyurl_req(url, client),
        Provider::TinyPh => tinyph_req(url, client),
        Provider::TnyIm => tnyim_req(url, client),
        Provider::UrlShortenerIo => urlshortenerio_req(url, client),
        Provider::VGd => vgd_req(url, client),
    }
}
<commit_msg>Fix percent encoding (#10)<commit_after>//! Library service providers implementation.

use reqwest::{Client, Response};
use reqwest::header::{ContentType, UserAgent};
use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

/// A user agent for faking weird serices.
const FAKE_USER_AGENT: &str =
    "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0";

/// A slice of all `Provider` variants which do not require authentication.
/// This list is in order of provider quality.
///
/// The providers which are discouraged from use - due to problems such as rate
/// limitations - are at the end of the resultant slice.
///
/// Note that some providers may not provide a generated short URL because the
/// submitted URL may already be short enough and would not benefit from
/// shortening via their service.
pub const PROVIDERS: &[Provider] = &[
    Provider::IsGd,
    Provider::BnGy,
    Provider::VGd,
    Provider::BamBz,
    Provider::TinyPh,
    Provider::FifoCc,
    Provider::SCoop,
    Provider::Bmeo,
    Provider::UrlShortenerIo,
    Provider::HmmRs,
    // The following list are items that have long response sometimes:
    Provider::TnyIm,
    // The following list are items that are discouraged from use:

        // Reasons:
        //
        // * rate limit (250 requests per 15 minutes)
        // * does not accept short urls (ex: http://google.com)
    Provider::SirBz,
    // Reason: rate limit (100 requests per hour)
    Provider::Rlu,
    // Reason: rate limit (3000 requests per day)
    Provider::HecSu,
    // Reason: rate limit (20r/3min; 60r/15min for a UNIQUE urls only)
    Provider::Abv8,
    // Reason: does not provide an api
    Provider::TinyUrl,
    // Reason: unstable work
    Provider::PsbeCo,
    // The following list are items that show previews instead of direct
    // links.
    Provider::NowLinks,
];

macro_rules! parse_xml_tag {
    ($fname: ident, $tag: expr) => {
        fn $fname(res: &str) -> Option<String> {
            res.split(&format!("<{}>", $tag))
                .nth(1)
                .unwrap_or("")
                .split(&format!("</{}>", $tag))
                .next()
                .map(String::from)
        }
    }
}

macro_rules! parse_json_tag {
    ($fname: ident, $tag: expr, $prefix: expr) => {
        fn $fname(res: &str) -> Option<String> {
            res.to_owned()
                .split(&format!("\"{}\"", $tag))
                .nth(1)
                .unwrap_or("")
                .split(",")
                .next()
                .unwrap_or("")
                .split("\"")
                .nth(1)
                .map(|v| format!("{}{}", $prefix, v.replace("\\", "")))
        }
    }
}

macro_rules! parse {
    ($name:ident) => {
        fn $name(res: &str) -> Option<String> {
            Some(res.to_owned())
        }
    };
}

macro_rules! request {
    ($name:ident, $method:ident, $req_url:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            let url = form_urlencoded::byte_serialize(url.as_bytes())
                .collect::<String>();
            client.$method(&format!($req_url, url)).send().ok()
        }
    };

    (B, $name:ident, $method: ident, $req_url:expr, $body:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            client.$method($req_url).body(format!($body, url)).send().ok()
        }
    };

    ($name:ident, $method:ident, $req_url:expr, $body:expr, $header:expr) => {
        fn $name(url: &str, client: &Client) -> Option<Response> {
            client.$method($req_url).body(format!($body, url)).header($header).send().ok()
        }
    };
}

/// Used to specify which provider to use to generate a short URL.
#[derive(Clone, Debug)]
pub enum Provider {
    /// http://abv8.me provider
    ///
    /// Notes:
    ///
    /// * You may not shorten more than 20 unique URLs within a 3-minute period.
    /// * You may not shorten more than 60 unique URLs within a 15-minute
    ///   period.
    Abv8,
    /// https://bam.bz provider
    BamBz,
    /// https://bit.ly provider
    BitLy { token: String },
    /// http://bmeo.org provider
    Bmeo,
    /// https://bn.gy provider
    BnGy,
    /// http://fifo.cc provider
    FifoCc,
    /// https://goo.gl provider of Google
    GooGl { api_key: String },
    /// https://hec.su provider
    ///
    /// Notes:
    ///
    /// * Limited to 3000 API requests per day
    HecSu,
    /// http://hmm.rs provider
    HmmRs,
    /// https://is.gd provider
    IsGd,
    /// http://nowlinks.net provider
    NowLinks,
    /// http://phx.co.in provider
    ///
    /// Notes:
    ///
    /// * After some time the service will display ads
    /// * Instead of redirecting, a preview page will be displayed
    /// * Currently unstable
    PhxCoIn,
    /// http://psbe.co provider
    PsbeCo,
    /// http://s.coop provider
    SCoop,
    /// http://rlu.ru provider
    ///
    /// Notes:
    ///
    /// * If you send a lot of requests from one IP, it can be
    ///   blocked. If you plan to add more then 100 URLs in one hour, please let
    ///   the technical support know. Otherwise your IP can be blocked
    ///   unexpectedly. Prior added URLs can be deleted.
    Rlu,
    /// http://sirbz.com provider
    ///
    /// Notes:
    ///
    /// * By default, you are limited to 250 requests per 15 minutes.
    SirBz,
    /// http://tinyurl.com provider
    ///
    /// Notes:
    ///
    /// * This service does not provide any API.
    /// * The implementation result depends on the service result web page.
    TinyUrl,
    /// http://tiny.ph provider
    TinyPh,
    /// http://tny.im provider
    TnyIm,
    /// http://url-shortener.io provider
    UrlShortenerIo,
    /// https://v.gd provider
    VGd,
}

impl Provider {
    /// Converts the Provider variant into its domain name equivilant
    pub fn to_name(&self) -> &str {
        match *self {
            Provider::Abv8 => "abv8.me",
            Provider::BamBz => "bam.bz",
            Provider::BitLy { .. } => "bitly.com",
            Provider::Bmeo => "bmeo.org",
            Provider::BnGy => "bn.gy",
            Provider::FifoCc => "fifo.cc",
            Provider::GooGl { .. } => "goo.gl",
            Provider::HmmRs => "hmm.rs",
            Provider::HecSu => "hec.su",
            Provider::IsGd => "is.gd",
            Provider::NowLinks => "nowlinks.net",
            Provider::PhxCoIn => "phx.co.in",
            Provider::PsbeCo => "psbe.co",
            Provider::SCoop => "s.coop",
            Provider::SirBz => "sirbz.com",
            Provider::Rlu => "rlu.ru",
            Provider::TinyUrl => "tinyurl.com",
            Provider::TinyPh => "tiny.ph",
            Provider::TnyIm => "tny.im",
            Provider::UrlShortenerIo => "url-shortener.io",
            Provider::VGd => "v.gd",
        }
    }
}

parse!(abv8_parse);
request!(abv8_req, get, "http://abv8.me/?url={}");

parse_json_tag!(bambz_parse, "url", "");
request!(
    bambz_req,
    post,
    "https://bam.bz/api/short",
    "target={}",
    ContentType::form_url_encoded()
);

parse!(bitly_parse);
fn bitly_req(url: &str, key: &str, client: &Client) -> Option<Response> {
    let encoded_url = utf8_percent_encode(url, QUERY_ENCODE_SET).collect::<String>();
    let address = format!(
        "https://api-ssl.bitly.com/v3/shorten?access_token={}&longUrl={}&format=txt",
        key, encoded_url
    );
    client.get(&address).send().ok()
}

parse_json_tag!(bmeo_parse, "short", "");
request!(bmeo_req, get, "http://bmeo.org/api.php?url={}");

parse_xml_tag!(bngy_parse, "ShortenedUrl");
request!(
    bngy_req,
    get,
    "https://bn.gy/API.asmx/CreateUrl?real_url={}"
);

parse_json_tag!(fifocc_parse, "shortner", "http://fifo.cc/");
request!(fifocc_req, get, "https://fifo.cc/api/v2?url={}");

parse_json_tag!(googl_parse, "id", "");
fn googl_req(url: &str, key: &str, client: &Client) -> Option<Response> {
    client
        .post(&format!(
            "https://www.googleapis.com/urlshortener/v1/url?key={}",
            key
        ))
        .body(format!(r#"{{"longUrl": "{}"}}"#, url))
        .header(ContentType::json())
        .send()
        .ok()
}

parse_json_tag!(hmmrs_parse, "shortUrl", "");
fn hmmrs_req(url: &str, client: &Client) -> Option<Response> {
    client
        .post("http:/hmm.rs/x/shorten")
        .body(format!(r#"{{"url": "{}"}}"#, url))
        .header(ContentType::json())
        .header(UserAgent::new(FAKE_USER_AGENT))
        .send()
        .ok()
}

parse_xml_tag!(hecsu_parse, "short");
request!(hecsu_req, get, "https://hec.su/api?url={}&method=xml");

parse!(isgd_parse);
request!(
    isgd_req,
    get,
    "https://is.gd/create.php?format=simple&url={}"
);

parse!(nowlinks_parse);
request!(nowlinks_req, get, "http://nowlinks.net/api?url={}");

parse!(phxcoin_parse);
request!(phxcoin_req, get, "http://phx.co.in/shrink.asp?url={}");

parse_xml_tag!(psbeco_parse, "ShortUrl");
request!(
    psbeco_req,
    get,
    "http://psbe.co/API.asmx/CreateUrl?real_url={}"
);

parse!(scoop_parse);
request!(
    scoop_req,
    get,
    "http://s.coop/devapi.php?action=shorturl&url={}&format=RETURN"
);

parse!(rlu_parse);
request!(rlu_req, get, "http://rlu.ru/index.sema?a=api&link={}");

parse_json_tag!(sirbz_parse, "short_link", "");
request!(
    sirbz_req,
    post,
    "http://sirbz.com/api/shorten_url",
    "url={}",
    ContentType::form_url_encoded()
);

fn tinyurl_parse(res: &str) -> Option<String> {
    res.split("data-clipboard-text=\"")
        .nth(1)
        .unwrap_or("")
        .split("\">")
        .next()
        .map(String::from)
}
request!(tinyurl_req, get, "http://tinyurl.com/create.php?url={}");

parse_json_tag!(tinyph_parse, "hash", "http://tiny.ph/");
request!(
    tinyph_req,
    post,
    "http://tiny.ph/api/url/create",
    "url={}",
    ContentType::form_url_encoded()
);

parse_xml_tag!(tnyim_parse, "shorturl");
request!(
    tnyim_req,
    get,
    "http://tny.im/yourls-api.php?action=shorturl&url={}"
);

parse!(urlshortenerio_parse);
request!(
    urlshortenerio_req,
    post,
    "http://url-shortener.io/shorten",
    "url_param={}",
    ContentType::form_url_encoded()
);

parse!(vgd_parse);
request!(vgd_req, get, "http://is.gd/create.php?format=simple&url={}");

/// Parses the response from a successful request to a provider into the
/// URL-shortened string.
pub fn parse(res: &str, provider: &Provider) -> Option<String> {
    match *provider {
        Provider::Abv8 => abv8_parse(res),
        Provider::BamBz => bambz_parse(res),
        Provider::BitLy { .. } => bitly_parse(res),
        Provider::Bmeo => bmeo_parse(res),
        Provider::BnGy => bngy_parse(res),
        Provider::FifoCc => fifocc_parse(res),
        Provider::GooGl { .. } => googl_parse(res),
        Provider::HmmRs => hmmrs_parse(res),
        Provider::HecSu => hecsu_parse(res),
        Provider::IsGd => isgd_parse(res),
        Provider::NowLinks => nowlinks_parse(res),
        Provider::PhxCoIn => phxcoin_parse(res),
        Provider::PsbeCo => psbeco_parse(res),
        Provider::SCoop => scoop_parse(res),
        Provider::SirBz => sirbz_parse(res),
        Provider::Rlu => rlu_parse(res),
        Provider::TinyUrl => tinyurl_parse(res),
        Provider::TinyPh => tinyph_parse(res),
        Provider::TnyIm => tnyim_parse(res),
        Provider::UrlShortenerIo => urlshortenerio_parse(res),
        Provider::VGd => vgd_parse(res),
    }
}

/// Performs a request to the short link provider.
/// Returns the parsed response on success or a `None` on error.
pub fn request(url: &str, client: &Client, provider: &Provider) -> Option<Response> {
    match *provider {
        Provider::Abv8 => abv8_req(url, client),
        Provider::BamBz => bambz_req(url, client),
        Provider::BitLy { token: ref key } => bitly_req(url, &key, client),
        Provider::Bmeo => bmeo_req(url, client),
        Provider::BnGy => bngy_req(url, client),
        Provider::FifoCc => fifocc_req(url, client),
        Provider::GooGl { api_key: ref key } => googl_req(url, &key, client),
        Provider::HmmRs => hmmrs_req(url, client),
        Provider::HecSu => hecsu_req(url, client),
        Provider::IsGd => isgd_req(url, client),
        Provider::NowLinks => nowlinks_req(url, client),
        Provider::PhxCoIn => phxcoin_req(url, client),
        Provider::PsbeCo => psbeco_req(url, client),
        Provider::SCoop => scoop_req(url, client),
        Provider::SirBz => sirbz_req(url, client),
        Provider::Rlu => rlu_req(url, client),
        Provider::TinyUrl => tinyurl_req(url, client),
        Provider::TinyPh => tinyph_req(url, client),
        Provider::TnyIm => tnyim_req(url, client),
        Provider::UrlShortenerIo => urlshortenerio_req(url, client),
        Provider::VGd => vgd_req(url, client),
    }
}
<|endoftext|>


<commit_before>//! Library service providers implementation.

extern crate hyper;

use hyper::client::{Client, Response};

/// Used to specify which provider to use to generate a short URL.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Provider {
    /// http://bit.do provider
    BitDo,
    /// https://bn.gy provider
    BnGy,
    /// https://is.gd provider
    IsGd,
    /// http://psbe.co provider
    PsbeCo,
    /// http://readbility.com provider
    Rdd,
    /// http://rlu.ru provider
    /// * Attention! If you send a lot of requests from one IP, it can be
    /// blocked. If you plan to add more then 100 URLs in one hour, please let
    /// the technical support know. Otherwise your IP can be blocked
    /// unexpectedly. Prior added URLs can be deleted.
    Rlu,
    /// https://v.gd provider
    VGd,
}

impl Provider {
    /// Converts the Provider variant into its domain name equivilant
    pub fn to_name(&self) -> &str {
        match *self {
            Provider::BitDo => "bit.do",
            Provider::BnGy => "bn.gy",
            Provider::IsGd => "is.gd",
            Provider::PsbeCo => "psbe.co",
            Provider::Rdd => "readability.com",
            Provider::Rlu => "rlu.ru",
            Provider::VGd => "v.gd",
        }
    }
}

/// Returns a vector of all `Provider` variants.
///
/// The providers which are discouraged from use due to limitations such as
/// rate limitations are at the end of the resultant vector.
pub fn providers() -> Vec<Provider> {
    vec![
        Provider::BnGy,
        Provider::IsGd,
        Provider::Rdd,
        Provider::VGd,

        // The following list are items that are discouraged from use.
        Provider::BitDo,
        Provider::PsbeCo,
        Provider::Rlu,
    ]
}

fn bitdo_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn bitdo_request(url: &str, client: &Client) -> Option<Response> {
    let mut h_url = hyper::Url::parse("http://bit.do/mod_perl/url-shortener.pl").unwrap();
    h_url.query_pairs_mut()
        .append_pair("action", "shorten")
        .append_pair("url", url)
        .append_pair("url2", "site2")
        .append_pair("url_hash", "")
        .append_pair("url_stats_is_private", &0.to_string());
    let body = &*format!("action=shorten&url={}&url2=site2&url_hash=&url_stats_is_private=0", url)
        .into_bytes();
    let resp = client.post(h_url.as_str())
        .body(body)
        .send();

    if resp.is_ok() {
        return Some(resp.unwrap())
    }
    None
}

fn bngy_parse(res: &str) -> Option<String> {
    if res.is_empty() {
        return None
    }
    let string = res.to_owned();
    let iter = string.split("<ShortenedUrl>").skip(1).next();
    if iter.is_none() {
        return None
    }
    if let Some(string) = iter.unwrap().split("</ShortenedUrl>").next() {
        return Some(string.to_owned())
    }
    None
}

fn bngy_request(url: &str, client: &Client) -> Option<Response> {
    let resp = client.get(&format!("https://bn.gy/API.asmx/CreateUrl?real_url={}", url))
                     .send();
    if resp.is_ok() {
        return Some(resp.unwrap())
    }
    None
}

fn isgd_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn isgd_request(url: &str, client: &Client) -> Option<Response> {
    let resp = client.get(&format!("https://is.gd/create.php?format=simple&url={}", url))
                     .send();
    if resp.is_ok() {
        return Some(resp.unwrap())
    }
    None
}

fn psbeco_parse(res: &str) -> Option<String> {
    if res.is_empty() {
        return None
    }
    let string = res.to_owned();
    let iter = string.split("<ShortUrl>").skip(1).next();
    if iter.is_none() {
        return None
    }
    if let Some(string) = iter.unwrap().split("</ShortUrl>").next() {
        return Some(string.to_owned())
    }
    None
}

fn psbeco_request(url: &str, client: &Client) -> Option<Response> {
    let resp = client.get(&format!("http://psbe.co/API.asmx/CreateUrl?real_url={}", url))
                     .send();
    if resp.is_ok() {
        return Some(resp.unwrap())
    }
    None
}

fn rdd_parse(res: &str) -> Option<String> {
    if res.is_empty() {
        return None
    }
    let string = res.to_owned();
    let value = string.split("\"rdd_url\"")
                      .nth(1).unwrap_or("")
                      .split(",").next().unwrap_or("")
                      .split("\"").nth(1);
    if let Some(string) = value {
        let mut short_url = string.to_owned();
        let _ = short_url.pop();
        return Some(short_url)
    }
    None
}

fn rdd_request(url: &str, client: &Client) -> Option<Response> {
    let body = &format!("url={}", url);
    let resp = client.post("https://readability.com/api/shortener/v1/urls")
                     .body(body)
                     .send();
    if resp.is_ok() {
        return Some(resp.unwrap())
    }
    None
}

fn rlu_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn rlu_request(url: &str, client: &Client) -> Option<Response> {
    let resp = client.get(&format!("http://rlu.ru/index.sema?a=api&link={}", url))
                     .send();
    if resp.is_ok() {
        return Some(resp.unwrap())
    }
    None
}

fn vgd_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn vgd_request(url: &str, client: &Client) -> Option<Response> {
    let resp = client.get(&format!("http://v.gd/create.php?format=simple&url={}", url))
                     .send();
    if resp.is_ok() {
        return Some(resp.unwrap())
    }
    None
}

/// Parses the response from a successful request to a provider into the
/// URL-shortened string.
pub fn parse(res: &str, provider: Provider) -> Option<String> {
    match provider {
        Provider::BitDo => bitdo_parse(res),
        Provider::BnGy => bngy_parse(res),
        Provider::IsGd => isgd_parse(res),
        Provider::PsbeCo => psbeco_parse(res),
        Provider::Rdd => rdd_parse(res),
        Provider::Rlu => rlu_parse(res),
        Provider::VGd => vgd_parse(res),
    }
}

/// Performs a request to the short link provider.
/// Response to be parsed or `None` on a error.
pub fn request(url: &str, client: &Client, provider: Provider) -> Option<Response> {
    match provider {
        Provider::BitDo => bitdo_request(url, client),
        Provider::BnGy => bngy_request(url, client),
        Provider::IsGd => isgd_request(url, client),
        Provider::PsbeCo => psbeco_request(url, client),
        Provider::Rdd => rdd_request(url, client),
        Provider::Rlu => rlu_request(url, client),
        Provider::VGd => vgd_request(url, client),
    }
}
<commit_msg>Simplify provider request methods<commit_after>//! Library service providers implementation.

extern crate hyper;

use hyper::client::{Client, Response};

/// Used to specify which provider to use to generate a short URL.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Provider {
    /// http://bit.do provider
    BitDo,
    /// https://bn.gy provider
    BnGy,
    /// https://is.gd provider
    IsGd,
    /// http://psbe.co provider
    PsbeCo,
    /// http://readbility.com provider
    Rdd,
    /// http://rlu.ru provider
    /// * Attention! If you send a lot of requests from one IP, it can be
    /// blocked. If you plan to add more then 100 URLs in one hour, please let
    /// the technical support know. Otherwise your IP can be blocked
    /// unexpectedly. Prior added URLs can be deleted.
    Rlu,
    /// https://v.gd provider
    VGd,
}

impl Provider {
    /// Converts the Provider variant into its domain name equivilant
    pub fn to_name(&self) -> &str {
        match *self {
            Provider::BitDo => "bit.do",
            Provider::BnGy => "bn.gy",
            Provider::IsGd => "is.gd",
            Provider::PsbeCo => "psbe.co",
            Provider::Rdd => "readability.com",
            Provider::Rlu => "rlu.ru",
            Provider::VGd => "v.gd",
        }
    }
}

/// Returns a vector of all `Provider` variants.
///
/// The providers which are discouraged from use due to limitations such as
/// rate limitations are at the end of the resultant vector.
pub fn providers() -> Vec<Provider> {
    vec![
        Provider::BnGy,
        Provider::IsGd,
        Provider::Rdd,
        Provider::VGd,

        // The following list are items that are discouraged from use.
        Provider::BitDo,
        Provider::PsbeCo,
        Provider::Rlu,
    ]
}

fn bitdo_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn bitdo_request(url: &str, client: &Client) -> Option<Response> {
    let mut h_url = hyper::Url::parse("http://bit.do/mod_perl/url-shortener.pl").unwrap();
    h_url.query_pairs_mut()
        .append_pair("action", "shorten")
        .append_pair("url", url)
        .append_pair("url2", "site2")
        .append_pair("url_hash", "")
        .append_pair("url_stats_is_private", &0.to_string());
    let body = &*format!("action=shorten&url={}&url2=site2&url_hash=&url_stats_is_private=0", url)
        .into_bytes();

    client.post(h_url.as_str())
        .body(body)
        .send()
        .ok()
}

fn bngy_parse(res: &str) -> Option<String> {
    if res.is_empty() {
        return None
    }
    let string = res.to_owned();
    let iter = string.split("<ShortenedUrl>").skip(1).next();
    if iter.is_none() {
        return None
    }
    if let Some(string) = iter.unwrap().split("</ShortenedUrl>").next() {
        Some(string.to_owned())
    } else {
        None
    }
}

fn bngy_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://bn.gy/API.asmx/CreateUrl?real_url={}", url))
        .send()
        .ok()
}

fn isgd_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn isgd_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://is.gd/create.php?format=simple&url={}", url))
        .send()
        .ok()
}

fn psbeco_parse(res: &str) -> Option<String> {
    if res.is_empty() {
        return None
    }
    let string = res.to_owned();
    let iter = string.split("<ShortUrl>").skip(1).next();
    if iter.is_none() {
        return None
    }
    if let Some(string) = iter.unwrap().split("</ShortUrl>").next() {
        Some(string.to_owned())
    } else {
        None
    }
}

fn psbeco_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://psbe.co/API.asmx/CreateUrl?real_url={}", url))
        .send()
        .ok()
}

fn rdd_parse(res: &str) -> Option<String> {
    if res.is_empty() {
        return None
    }
    let string = res.to_owned();
    let value = string.split("\"rdd_url\"")
                      .nth(1).unwrap_or("")
                      .split(",").next().unwrap_or("")
                      .split("\"").nth(1);
    if let Some(string) = value {
        let mut short_url = string.to_owned();
        let _ = short_url.pop();
        Some(short_url)
    } else {
        None
    }
}

fn rdd_request(url: &str, client: &Client) -> Option<Response> {
    client.post("https://readability.com/api/shortener/v1/urls")
        .body(&format!("url={}", url))
        .send()
        .ok()
}

fn rlu_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn rlu_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://rlu.ru/index.sema?a=api&link={}", url))
        .send()
        .ok()
}

fn vgd_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn vgd_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://v.gd/create.php?format=simple&url={}", url))
        .send()
        .ok()
}

/// Parses the response from a successful request to a provider into the
/// URL-shortened string.
pub fn parse(res: &str, provider: Provider) -> Option<String> {
    match provider {
        Provider::BitDo => bitdo_parse(res),
        Provider::BnGy => bngy_parse(res),
        Provider::IsGd => isgd_parse(res),
        Provider::PsbeCo => psbeco_parse(res),
        Provider::Rdd => rdd_parse(res),
        Provider::Rlu => rlu_parse(res),
        Provider::VGd => vgd_parse(res),
    }
}

/// Performs a request to the short link provider.
/// Response to be parsed or `None` on a error.
pub fn request(url: &str, client: &Client, provider: Provider) -> Option<Response> {
    match provider {
        Provider::BitDo => bitdo_request(url, client),
        Provider::BnGy => bngy_request(url, client),
        Provider::IsGd => isgd_request(url, client),
        Provider::PsbeCo => psbeco_request(url, client),
        Provider::Rdd => rdd_request(url, client),
        Provider::Rlu => rlu_request(url, client),
        Provider::VGd => vgd_request(url, client),
    }
}
<|endoftext|>


<commit_before>use error::Result;
use serde;
use std::io;

/// An optional Read trait for advanced Bincode usage.
///
/// It is highly recommended to use bincode with `io::Read` or `&[u8]` before
/// implementing a custom `BincodeRead`.
pub trait BincodeRead<'storage>: io::Read {
    /// Forwards reading `length` bytes of a string on to the serde reader.
    fn forward_read_str<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'storage>;

    /// Return the first `length` bytes of the internal byte buffer.
    fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>>;

    /// Forwards reading `length` bytes on to the serde reader.
    fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'storage>;
}

/// A BincodeRead implementation for byte slices
/// NOT A PART OF THE STABLE PUBLIC API
#[doc(hidden)]
pub struct SliceReader<'storage> {
    slice: &'storage [u8],
}

/// A BincodeRead implementation for io::Readers
/// NOT A PART OF THE STABLE PUBLIC API
#[doc(hidden)]
pub struct IoReader<R> {
    reader: R,
    temp_buffer: Vec<u8>,
}

impl<'storage> SliceReader<'storage> {
    /// Constructs a slice reader
    pub fn new(bytes: &'storage [u8]) -> SliceReader<'storage> {
        SliceReader { slice: bytes }
    }
}

impl<R> IoReader<R> {
    /// Constructs an IoReadReader
    pub fn new(r: R) -> IoReader<R> {
        IoReader {
            reader: r,
            temp_buffer: vec![],
        }
    }
}

impl<'storage> io::Read for SliceReader<'storage> {
    #[inline(always)]
    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
        (&mut self.slice).read(out)
    }
    #[inline(always)]
    fn read_exact(&mut self, out: &mut [u8]) -> io::Result<()> {
        (&mut self.slice).read_exact(out)
    }
}

impl<R: io::Read> io::Read for IoReader<R> {
    #[inline(always)]
    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
        self.reader.read(out)
    }
    #[inline(always)]
    fn read_exact(&mut self, out: &mut [u8]) -> io::Result<()> {
        self.reader.read_exact(out)
    }
}

impl<'storage> SliceReader<'storage> {
    #[inline(always)]
    fn unexpected_eof() -> Box<::ErrorKind> {
        return Box::new(::ErrorKind::Io(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "",
        )));
    }
}

impl<'storage> BincodeRead<'storage> for SliceReader<'storage> {
    #[inline(always)]
    fn forward_read_str<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'storage>,
    {
        use ErrorKind;
        if length > self.slice.len() {
            return Err(SliceReader::unexpected_eof());
        }

        let string = match ::std::str::from_utf8(&self.slice[..length]) {
            Ok(s) => s,
            Err(e) => return Err(ErrorKind::InvalidUtf8Encoding(e).into()),
        };
        let r = visitor.visit_borrowed_str(string);
        self.slice = &self.slice[length..];
        r
    }

    #[inline(always)]
    fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>> {
        if length > self.slice.len() {
            return Err(SliceReader::unexpected_eof());
        }

        let r = &self.slice[..length];
        self.slice = &self.slice[length..];
        Ok(r.to_vec())
    }

    #[inline(always)]
    fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'storage>,
    {
        if length > self.slice.len() {
            return Err(SliceReader::unexpected_eof());
        }

        let r = visitor.visit_borrowed_bytes(&self.slice[..length]);
        self.slice = &self.slice[length..];
        r
    }
}

impl<R> IoReader<R>
where
    R: io::Read,
{
    fn fill_buffer(&mut self, length: usize) -> Result<()> {
        let current_length = self.temp_buffer.len();
        if length > current_length {
            self.temp_buffer.reserve_exact(length - current_length);
        }

        unsafe {
            self.temp_buffer.set_len(length);
        }

        self.reader.read_exact(&mut self.temp_buffer)?;
        Ok(())
    }
}

impl<'a, R> BincodeRead<'a> for IoReader<R>
where
    R: io::Read,
{
    fn forward_read_str<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'a>,
    {
        self.fill_buffer(length)?;

        let string = match ::std::str::from_utf8(&self.temp_buffer[..]) {
            Ok(s) => s,
            Err(e) => return Err(::ErrorKind::InvalidUtf8Encoding(e).into()),
        };

        let r = visitor.visit_str(string);
        r
    }

    fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>> {
        self.fill_buffer(length)?;
        Ok(::std::mem::replace(&mut self.temp_buffer, Vec::new()))
    }

    fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'a>,
    {
        self.fill_buffer(length)?;
        let r = visitor.visit_bytes(&self.temp_buffer[..]);
        r
    }
}
<commit_msg>improve safety of fill_buffer - see issue #260<commit_after>use error::Result;
use serde;
use std::{io, slice};

/// An optional Read trait for advanced Bincode usage.
///
/// It is highly recommended to use bincode with `io::Read` or `&[u8]` before
/// implementing a custom `BincodeRead`.
pub trait BincodeRead<'storage>: io::Read {
    /// Forwards reading `length` bytes of a string on to the serde reader.
    fn forward_read_str<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'storage>;

    /// Return the first `length` bytes of the internal byte buffer.
    fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>>;

    /// Forwards reading `length` bytes on to the serde reader.
    fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'storage>;
}

/// A BincodeRead implementation for byte slices
/// NOT A PART OF THE STABLE PUBLIC API
#[doc(hidden)]
pub struct SliceReader<'storage> {
    slice: &'storage [u8],
}

/// A BincodeRead implementation for io::Readers
/// NOT A PART OF THE STABLE PUBLIC API
#[doc(hidden)]
pub struct IoReader<R> {
    reader: R,
    temp_buffer: Vec<u8>,
}

impl<'storage> SliceReader<'storage> {
    /// Constructs a slice reader
    pub fn new(bytes: &'storage [u8]) -> SliceReader<'storage> {
        SliceReader { slice: bytes }
    }
}

impl<R> IoReader<R> {
    /// Constructs an IoReadReader
    pub fn new(r: R) -> IoReader<R> {
        IoReader {
            reader: r,
            temp_buffer: vec![],
        }
    }
}

impl<'storage> io::Read for SliceReader<'storage> {
    #[inline(always)]
    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
        (&mut self.slice).read(out)
    }
    #[inline(always)]
    fn read_exact(&mut self, out: &mut [u8]) -> io::Result<()> {
        (&mut self.slice).read_exact(out)
    }
}

impl<R: io::Read> io::Read for IoReader<R> {
    #[inline(always)]
    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
        self.reader.read(out)
    }
    #[inline(always)]
    fn read_exact(&mut self, out: &mut [u8]) -> io::Result<()> {
        self.reader.read_exact(out)
    }
}

impl<'storage> SliceReader<'storage> {
    #[inline(always)]
    fn unexpected_eof() -> Box<::ErrorKind> {
        return Box::new(::ErrorKind::Io(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "",
        )));
    }
}

impl<'storage> BincodeRead<'storage> for SliceReader<'storage> {
    #[inline(always)]
    fn forward_read_str<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'storage>,
    {
        use ErrorKind;
        if length > self.slice.len() {
            return Err(SliceReader::unexpected_eof());
        }

        let string = match ::std::str::from_utf8(&self.slice[..length]) {
            Ok(s) => s,
            Err(e) => return Err(ErrorKind::InvalidUtf8Encoding(e).into()),
        };
        let r = visitor.visit_borrowed_str(string);
        self.slice = &self.slice[length..];
        r
    }

    #[inline(always)]
    fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>> {
        if length > self.slice.len() {
            return Err(SliceReader::unexpected_eof());
        }

        let r = &self.slice[..length];
        self.slice = &self.slice[length..];
        Ok(r.to_vec())
    }

    #[inline(always)]
    fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'storage>,
    {
        if length > self.slice.len() {
            return Err(SliceReader::unexpected_eof());
        }

        let r = visitor.visit_borrowed_bytes(&self.slice[..length]);
        self.slice = &self.slice[length..];
        r
    }
}

impl<R> IoReader<R>
where
    R: io::Read,
{
    fn fill_buffer(&mut self, length: usize) -> Result<()> {
        // We first reserve the space needed in our buffer.
        let current_length = self.temp_buffer.len();
        if length > current_length {
            self.temp_buffer.reserve_exact(length - current_length);
        }

        // Then create a slice with the length as our desired length. This is
        // safe as long as we only write (no reads) to this buffer, because
        // `reserve_exact` above has allocated this space.
        let buf = unsafe {
            slice::from_raw_parts_mut(self.temp_buffer.as_mut_ptr(), length)
        };

        // This method is assumed to properly handle slices which include
        // uninitialized bytes (as ours does). See discussion at the link below.
        // https://github.com/servo/bincode/issues/260
        self.reader.read_exact(buf)?;

        // Only after `read_exact` successfully returns do we set the buffer
        // length. By doing this after the call to `read_exact`, we can avoid
        // exposing uninitialized memory in the case of `read_exact` returning
        // an error.
        unsafe {
            self.temp_buffer.set_len(length);
        }

        Ok(())
    }
}

impl<'a, R> BincodeRead<'a> for IoReader<R>
where
    R: io::Read,
{
    fn forward_read_str<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'a>,
    {
        self.fill_buffer(length)?;

        let string = match ::std::str::from_utf8(&self.temp_buffer[..]) {
            Ok(s) => s,
            Err(e) => return Err(::ErrorKind::InvalidUtf8Encoding(e).into()),
        };

        let r = visitor.visit_str(string);
        r
    }

    fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>> {
        self.fill_buffer(length)?;
        Ok(::std::mem::replace(&mut self.temp_buffer, Vec::new()))
    }

    fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
    where
        V: serde::de::Visitor<'a>,
    {
        self.fill_buffer(length)?;
        let r = visitor.visit_bytes(&self.temp_buffer[..]);
        r
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use std::cell::{Cell, RefCell, Ref, RefMut};

use math::Vector3;
use math::Matrix4;
use math::Point;
use math::Quaternion;

use ecs::{Entity, System, ComponentManager};
use scene::Scene;

#[derive(Debug, Clone)]
pub struct TransformManager {
    transforms: Vec<Vec<RefCell<Transform>>>,
    entities: Vec<Vec<Entity>>,

    /// A map between the entity owning the transform and the location of the transform.
    ///
    /// The first value of the mapped tuple is the row containing the transform, the
    /// second is the index of the transform within that row.
    indices: HashMap<Entity, (usize, usize)>,

    marked_for_destroy: RefCell<HashSet<Entity>>,
}

impl TransformManager {
    pub fn new() -> TransformManager {
        let mut transform_manager = TransformManager {
            transforms: Vec::new(),
            entities: Vec::new(),
            indices: HashMap::new(),
            marked_for_destroy: RefCell::new(HashSet::new()),
        };

        transform_manager.transforms.push(Vec::new());
        transform_manager.entities.push(Vec::new());
        transform_manager
    }

    pub fn assign(&mut self, entity: Entity) -> RefMut<Transform> {
        let index = self.transforms[0].len();
        self.transforms[0].push(RefCell::new(Transform::new()));
        self.entities[0].push(entity);

        assert!(self.transforms[0].len() == self.entities[0].len());

        self.indices.insert(entity, (0, index));
        self.transforms[0][index].borrow_mut()
    }

    pub fn get(&self, entity: Entity) -> Ref<Transform> {
        let (row, index) = *self.indices.get(&entity).expect("Transform manager does not contain a transform for the given entity.");
        self.transforms[row][index].borrow()
    }

    pub fn get_mut(&self, entity: Entity) -> RefMut<Transform> {
        let (row, index) = *self.indices.get(&entity).expect("Transform manager does not contain a transform for the given entity.");
        self.transforms[row][index].borrow_mut()
    }

    pub fn set_child(&mut self, parent: Entity, child: Entity) {
        // Remove old transform component.
        let mut transform = self.remove(child);

        // Get the indices of the parent.
        let (parent_row, _) = *self.indices.get(&parent).unwrap();
        let child_row = parent_row + 1;

        // Ensure that there are enough rows for the child.
        while self.transforms.len() < child_row + 1 {
            self.transforms.push(Vec::new());
            self.entities.push(Vec::new());
        }
        // Add the child to the correct row.
        transform.parent = Some(parent);
        let child_index = self.transforms[child_row].len();
        self.transforms[child_row].push(RefCell::new(transform));
        self.entities[child_row].push(child);

        // Update the index map.
        self.indices.insert(child, (child_row, child_index));
    }

    pub fn update_single(&self, entity: Entity) {
        let transform = self.get(entity);
        self.update_transform(&*transform);
    }

    pub fn update_transform(&self, transform: &Transform) {
        let (parent_matrix, parent_rotation) = match transform.parent {
            None => {
                (Matrix4::identity(), Quaternion::identity())
            },
            Some(parent) => {
                let parent_transform = self.get(parent);

                if parent_transform.out_of_date.get() {
                    self.update_transform(&*parent_transform);
                }

                let parent_matrix = parent_transform.matrix_derived.get();
                let parent_rotation = parent_transform.rotation_derived.get();

                (parent_matrix, parent_rotation)
            }
        };

        transform.update(parent_matrix, parent_rotation);
    }

    /// Marks the transform associated with the entity for destruction.
    ///
    /// # Details
    ///
    /// Components marked for destruction are destroyed at the end of every frame. It can be used
    /// to destroy components without needing a mutable borrow on the component manager.
    ///
    /// TODO: Actually support deferred destruction.
    pub fn destroy(&self, entity: Entity) {
        let mut marked_for_destroy = self.marked_for_destroy.borrow_mut();
        marked_for_destroy.insert(entity); // TODO: Warning, error if entity has already been marked?
    }

    pub fn destroy_immediate(&mut self, entity: Entity) {
        self.remove(entity);

        // TODO: Destroy children.
    }

    // Removes and returns the transform associated with the given entity.
    //
    // # Details
    //
    // NOTE: This does not handle updating/removing children. So be warned.
    fn remove(&mut self, entity: Entity) -> Transform {
        // Retrieve indices of removed entity and the one it's swapped with.
        let (row, index) = self.indices.remove(&entity).unwrap();
        debug_assert!(self.transforms[row].len() == self.entities[row].len());

        // Remove transform and the associate entity.
        let removed_entity = self.entities[row].swap_remove(index);
        debug_assert!(removed_entity == entity);

        // Update the index mapping for the moved entity, but only if the one we removed
        // wasn't the only one in the row (or the last one in the row).
        if index != self.entities[row].len() {
            let moved_entity = self.entities[row][index];
            self.indices.insert(moved_entity, (row, index));
        }

        // Defer removing the transform until the very end to avoid a bunch of memcpys.
        // Transform is a pretty fat struct so if we remove it, cache it to a variable,
        // and then return it at the end we wind up with 2 or 3 memcpys. Doing it all at
        // once at the end (hopefully) means only a single memcpy.
        self.transforms[row].swap_remove(index).into_inner()
    }
}

impl ComponentManager for TransformManager {
}

/// TODO: This should be module-level documentation.
///
/// A component representing the total transform (position, orientation,
/// and scale) of an object in the world.
///
/// # Details
///
/// The `Transform` component is a fundamental part of the Gunship engine.
/// It has the dual role of managing each individual entity's local transformation,
/// as well as representing the individual nodes within the scene hierarchy.
///
/// ## Scene hierarchy
///
/// Each transform component may have one parent and any number of children. If a transform has
/// a parent then its world transformation is the concatenation of its local transformation with
/// its parent's world transformation. Using simple combinations of nested transforms can allow
/// otherwise complex patterns of movement and positioning to be much easier to represent.
///
/// Transforms that have no parent are said to be at the root level and have the property
/// that their local transformation is also their world transformation. If a transform is
/// known to be at the root of the hierarchy it is recommended that its local values be modified
/// directly to achieve best performance.
#[derive(Debug, Clone)]
pub struct Transform {
    position:         Point,
    rotation:         Quaternion,
    scale:            Vector3,
    local_matrix:     Cell<Matrix4>,
    position_derived: Cell<Point>,
    rotation_derived: Cell<Quaternion>,
    scale_derived:    Cell<Vector3>,
    matrix_derived:   Cell<Matrix4>,
    parent:           Option<Entity>,
    out_of_date:      Cell<bool>,
}

impl Transform {
    pub fn new() -> Transform {
        Transform {
            position:         Point::origin(),
            rotation:         Quaternion::identity(),
            scale:            Vector3::one(),
            local_matrix:     Cell::new(Matrix4::identity()),
            position_derived: Cell::new(Point::origin()),
            rotation_derived: Cell::new(Quaternion::identity()),
            scale_derived:    Cell::new(Vector3::one()),
            matrix_derived:   Cell::new(Matrix4::identity()),
            parent:           None,
            out_of_date:      Cell::new(false),
        }
    }

    pub fn position(&self) -> Point {
        self.position
    }

    pub fn set_position(&mut self, new_position: Point) {
        self.position = new_position;
        self.out_of_date.set(true);
    }

    pub fn rotation(&self) -> Quaternion {
        self.rotation
    }

    pub fn set_rotation(&mut self, new_rotation: Quaternion) {
        self.rotation = new_rotation;
        self.out_of_date.set(true);
    }

    pub fn scale(&self) -> Vector3 {
        self.scale
    }

    pub fn set_scale(&mut self, new_scale: Vector3) {
        self.scale = new_scale;
        self.out_of_date.set(true);
    }

    /// Retrieves the derived position of the transform.
    ///
    /// In debug builds this method asserts if the transform is out of date.
    pub fn position_derived(&self) -> Point {
        assert!(!self.out_of_date.get());

        self.position_derived.get()
    }

    /// Retrieves the derived rotation of the transform.
    ///
    /// In debug builds this method asserts if the transform is out of date.
    pub fn rotation_derived(&self) -> Quaternion {
        assert!(!self.out_of_date.get());

        self.rotation_derived.get()
    }

    /// Retrieves the derived scale of the transform.
    ///
    /// In debug builds this method asserts if the transform is out of date.
    pub fn scale_derived(&self) -> Vector3 {
        assert!(!self.out_of_date.get());

        self.scale_derived.get()
    }

    /// Retrieves the composite matrix representing the local transform.
    ///
    /// # Details
    ///
    /// The composite matrix combines the affine matrices representing translation,
    /// scale, and rotation into a single transformation matrix. The local maxtrix does
    /// not include the parent's transformation. The local matrix transforms a local point
    /// into the parent's coordinate system.
    pub fn local_matrix(&self) -> Matrix4 {
        if self.out_of_date.get() {
            let local_matrix =
                Matrix4::from_point(self.position)
                * (self.rotation.as_matrix() * Matrix4::from_scale_vector(self.scale));
            self.local_matrix.set(local_matrix);
        }

        self.local_matrix.get()
    }

    pub fn derived_matrix(&self) -> Matrix4 {
        assert!(!self.out_of_date.get());

        self.matrix_derived.get()
    }

    pub fn derived_normal_matrix(&self) -> Matrix4 {
        assert!(!self.out_of_date.get());

        let inverse =
            Matrix4::from_scale_vector(1.0 / self.scale_derived.get())
          * (self.rotation_derived.get().as_matrix().transpose()
          *  Matrix4::from_point(-self.position_derived.get()));

        inverse.transpose()
    }

    pub fn translate(&mut self, translation: Vector3) {
        self.position = self.position + translation;
        self.out_of_date.set(true);
    }

    pub fn rotate(&mut self, rotation: Quaternion) {
        self.rotation = self.rotation * rotation;
        self.out_of_date.set(true);
    }

    pub fn look_at(&mut self, interest: Point, up: Vector3) {
        let forward = interest - self.position;
        self.rotation = Quaternion::look_rotation(forward, up);
        self.out_of_date.set(true);
    }

    pub fn look_direction(&mut self, forward: Vector3, up: Vector3) {
        self.rotation = Quaternion::look_rotation(forward, up);
        self.out_of_date.set(true);
    }

    /// Updates the local and derived matrices for the transform.
    fn update(&self, parent_matrix: Matrix4, parent_rotation: Quaternion) {
        let local_matrix = self.local_matrix();

        let derived_matrix = parent_matrix * local_matrix;
        self.matrix_derived.set(derived_matrix);

        self.position_derived.set(derived_matrix.translation_part());
        self.rotation_derived.set(parent_rotation * self.rotation);

        self.out_of_date.set(false);
    }
}

pub struct TransformUpdateSystem;

impl System for TransformUpdateSystem {
    fn update(&mut self, scene: &Scene, _: f32) {
        let transform_manager = scene.get_manager::<TransformManager>();

        for row in transform_manager.transforms.iter() {
            for transform in row.iter() {
                // Retrieve the parent's transformation matrix, using the identity
                // matrix if the transform has no parent.
                let (parent_matrix, parent_rotation) = match transform.borrow().parent {
                    None => {
                        (Matrix4::identity(), Quaternion::identity())
                    },
                    Some(parent) => {
                        let parent_transform = transform_manager.get(parent);

                        let parent_matrix = parent_transform.derived_matrix();
                        let parent_rotation = parent_transform.rotation_derived();

                        (parent_matrix, parent_rotation)
                    }
                };

                transform.borrow().update(parent_matrix, parent_rotation);
            }
        }
    }
}
<commit_msg>Only Update Out Of Date Transforms<commit_after>use std::collections::{HashMap, HashSet};
use std::cell::{Cell, RefCell, Ref, RefMut};

use math::Vector3;
use math::Matrix4;
use math::Point;
use math::Quaternion;

use ecs::{Entity, System, ComponentManager};
use scene::Scene;

#[derive(Debug, Clone)]
pub struct TransformManager {
    transforms: Vec<Vec<RefCell<Transform>>>,
    entities: Vec<Vec<Entity>>,

    /// A map between the entity owning the transform and the location of the transform.
    ///
    /// The first value of the mapped tuple is the row containing the transform, the
    /// second is the index of the transform within that row.
    indices: HashMap<Entity, (usize, usize)>,

    marked_for_destroy: RefCell<HashSet<Entity>>,
}

impl TransformManager {
    pub fn new() -> TransformManager {
        let mut transform_manager = TransformManager {
            transforms: Vec::new(),
            entities: Vec::new(),
            indices: HashMap::new(),
            marked_for_destroy: RefCell::new(HashSet::new()),
        };

        transform_manager.transforms.push(Vec::new());
        transform_manager.entities.push(Vec::new());
        transform_manager
    }

    pub fn assign(&mut self, entity: Entity) -> RefMut<Transform> {
        let index = self.transforms[0].len();
        self.transforms[0].push(RefCell::new(Transform::new()));
        self.entities[0].push(entity);

        assert!(self.transforms[0].len() == self.entities[0].len());

        self.indices.insert(entity, (0, index));
        self.transforms[0][index].borrow_mut()
    }

    pub fn get(&self, entity: Entity) -> Ref<Transform> {
        let (row, index) = *self.indices.get(&entity).expect("Transform manager does not contain a transform for the given entity.");
        self.transforms[row][index].borrow()
    }

    pub fn get_mut(&self, entity: Entity) -> RefMut<Transform> {
        let (row, index) = *self.indices.get(&entity).expect("Transform manager does not contain a transform for the given entity.");
        self.transforms[row][index].borrow_mut()
    }

    pub fn set_child(&mut self, parent: Entity, child: Entity) {
        // Remove old transform component.
        let mut transform = self.remove(child);

        // Get the indices of the parent.
        let (parent_row, _) = *self.indices.get(&parent).unwrap();
        let child_row = parent_row + 1;

        // Ensure that there are enough rows for the child.
        while self.transforms.len() < child_row + 1 {
            self.transforms.push(Vec::new());
            self.entities.push(Vec::new());
        }
        // Add the child to the correct row.
        transform.parent = Some(parent);
        let child_index = self.transforms[child_row].len();
        self.transforms[child_row].push(RefCell::new(transform));
        self.entities[child_row].push(child);

        // Update the index map.
        self.indices.insert(child, (child_row, child_index));
    }

    pub fn update_single(&self, entity: Entity) {
        let transform = self.get(entity);
        self.update_transform(&*transform);
    }

    pub fn update_transform(&self, transform: &Transform) {
        let (parent_matrix, parent_rotation) = match transform.parent {
            None => {
                (Matrix4::identity(), Quaternion::identity())
            },
            Some(parent) => {
                let parent_transform = self.get(parent);

                if parent_transform.out_of_date.get() {
                    self.update_transform(&*parent_transform);
                }

                let parent_matrix = parent_transform.matrix_derived.get();
                let parent_rotation = parent_transform.rotation_derived.get();

                (parent_matrix, parent_rotation)
            }
        };

        transform.update(parent_matrix, parent_rotation);
    }

    /// Marks the transform associated with the entity for destruction.
    ///
    /// # Details
    ///
    /// Components marked for destruction are destroyed at the end of every frame. It can be used
    /// to destroy components without needing a mutable borrow on the component manager.
    ///
    /// TODO: Actually support deferred destruction.
    pub fn destroy(&self, entity: Entity) {
        let mut marked_for_destroy = self.marked_for_destroy.borrow_mut();
        marked_for_destroy.insert(entity); // TODO: Warning, error if entity has already been marked?
    }

    pub fn destroy_immediate(&mut self, entity: Entity) {
        self.remove(entity);

        // TODO: Destroy children.
    }

    // Removes and returns the transform associated with the given entity.
    //
    // # Details
    //
    // NOTE: This does not handle updating/removing children. So be warned.
    fn remove(&mut self, entity: Entity) -> Transform {
        // Retrieve indices of removed entity and the one it's swapped with.
        let (row, index) = self.indices.remove(&entity).unwrap();
        debug_assert!(self.transforms[row].len() == self.entities[row].len());

        // Remove transform and the associate entity.
        let removed_entity = self.entities[row].swap_remove(index);
        debug_assert!(removed_entity == entity);

        // Update the index mapping for the moved entity, but only if the one we removed
        // wasn't the only one in the row (or the last one in the row).
        if index != self.entities[row].len() {
            let moved_entity = self.entities[row][index];
            self.indices.insert(moved_entity, (row, index));
        }

        // Defer removing the transform until the very end to avoid a bunch of memcpys.
        // Transform is a pretty fat struct so if we remove it, cache it to a variable,
        // and then return it at the end we wind up with 2 or 3 memcpys. Doing it all at
        // once at the end (hopefully) means only a single memcpy.
        self.transforms[row].swap_remove(index).into_inner()
    }
}

impl ComponentManager for TransformManager {
}

/// TODO: This should be module-level documentation.
///
/// A component representing the total transform (position, orientation,
/// and scale) of an object in the world.
///
/// # Details
///
/// The `Transform` component is a fundamental part of the Gunship engine.
/// It has the dual role of managing each individual entity's local transformation,
/// as well as representing the individual nodes within the scene hierarchy.
///
/// ## Scene hierarchy
///
/// Each transform component may have one parent and any number of children. If a transform has
/// a parent then its world transformation is the concatenation of its local transformation with
/// its parent's world transformation. Using simple combinations of nested transforms can allow
/// otherwise complex patterns of movement and positioning to be much easier to represent.
///
/// Transforms that have no parent are said to be at the root level and have the property
/// that their local transformation is also their world transformation. If a transform is
/// known to be at the root of the hierarchy it is recommended that its local values be modified
/// directly to achieve best performance.
#[derive(Debug, Clone)]
pub struct Transform {
    position:         Point,
    rotation:         Quaternion,
    scale:            Vector3,
    local_matrix:     Cell<Matrix4>,
    position_derived: Cell<Point>,
    rotation_derived: Cell<Quaternion>,
    scale_derived:    Cell<Vector3>,
    matrix_derived:   Cell<Matrix4>,
    parent:           Option<Entity>,
    out_of_date:      Cell<bool>,
}

impl Transform {
    pub fn new() -> Transform {
        Transform {
            position:         Point::origin(),
            rotation:         Quaternion::identity(),
            scale:            Vector3::one(),
            local_matrix:     Cell::new(Matrix4::identity()),
            position_derived: Cell::new(Point::origin()),
            rotation_derived: Cell::new(Quaternion::identity()),
            scale_derived:    Cell::new(Vector3::one()),
            matrix_derived:   Cell::new(Matrix4::identity()),
            parent:           None,
            out_of_date:      Cell::new(false),
        }
    }

    pub fn position(&self) -> Point {
        self.position
    }

    pub fn set_position(&mut self, new_position: Point) {
        self.position = new_position;
        self.out_of_date.set(true);
    }

    pub fn rotation(&self) -> Quaternion {
        self.rotation
    }

    pub fn set_rotation(&mut self, new_rotation: Quaternion) {
        self.rotation = new_rotation;
        self.out_of_date.set(true);
    }

    pub fn scale(&self) -> Vector3 {
        self.scale
    }

    pub fn set_scale(&mut self, new_scale: Vector3) {
        self.scale = new_scale;
        self.out_of_date.set(true);
    }

    /// Retrieves the derived position of the transform.
    ///
    /// In debug builds this method asserts if the transform is out of date.
    pub fn position_derived(&self) -> Point {
        assert!(!self.out_of_date.get());

        self.position_derived.get()
    }

    /// Retrieves the derived rotation of the transform.
    ///
    /// In debug builds this method asserts if the transform is out of date.
    pub fn rotation_derived(&self) -> Quaternion {
        assert!(!self.out_of_date.get());

        self.rotation_derived.get()
    }

    /// Retrieves the derived scale of the transform.
    ///
    /// In debug builds this method asserts if the transform is out of date.
    pub fn scale_derived(&self) -> Vector3 {
        assert!(!self.out_of_date.get());

        self.scale_derived.get()
    }

    /// Retrieves the composite matrix representing the local transform.
    ///
    /// # Details
    ///
    /// The composite matrix combines the affine matrices representing translation,
    /// scale, and rotation into a single transformation matrix. The local maxtrix does
    /// not include the parent's transformation. The local matrix transforms a local point
    /// into the parent's coordinate system.
    pub fn local_matrix(&self) -> Matrix4 {
        if self.out_of_date.get() {
            let local_matrix =
                Matrix4::from_point(self.position)
                * (self.rotation.as_matrix() * Matrix4::from_scale_vector(self.scale));
            self.local_matrix.set(local_matrix);
        }

        self.local_matrix.get()
    }

    pub fn derived_matrix(&self) -> Matrix4 {
        assert!(!self.out_of_date.get());

        self.matrix_derived.get()
    }

    pub fn derived_normal_matrix(&self) -> Matrix4 {
        assert!(!self.out_of_date.get());

        let inverse =
            Matrix4::from_scale_vector(1.0 / self.scale_derived.get())
          * (self.rotation_derived.get().as_matrix().transpose()
          *  Matrix4::from_point(-self.position_derived.get()));

        inverse.transpose()
    }

    pub fn translate(&mut self, translation: Vector3) {
        self.position = self.position + translation;
        self.out_of_date.set(true);
    }

    pub fn rotate(&mut self, rotation: Quaternion) {
        self.rotation = self.rotation * rotation;
        self.out_of_date.set(true);
    }

    pub fn look_at(&mut self, interest: Point, up: Vector3) {
        let forward = interest - self.position;
        self.rotation = Quaternion::look_rotation(forward, up);
        self.out_of_date.set(true);
    }

    pub fn look_direction(&mut self, forward: Vector3, up: Vector3) {
        self.rotation = Quaternion::look_rotation(forward, up);
        self.out_of_date.set(true);
    }

    /// Updates the local and derived matrices for the transform.
    fn update(&self, parent_matrix: Matrix4, parent_rotation: Quaternion) {
        let local_matrix = self.local_matrix();

        let derived_matrix = parent_matrix * local_matrix;
        self.matrix_derived.set(derived_matrix);

        self.position_derived.set(derived_matrix.translation_part());
        self.rotation_derived.set(parent_rotation * self.rotation);

        self.out_of_date.set(false);
    }
}

pub struct TransformUpdateSystem;

impl System for TransformUpdateSystem {
    fn update(&mut self, scene: &Scene, _: f32) {
        let transform_manager = scene.get_manager::<TransformManager>();

        for row in transform_manager.transforms.iter() {
            for transform in row.iter().filter(|transform| transform.borrow().out_of_date.get()) {
                // Retrieve the parent's transformation matrix, using the identity
                // matrix if the transform has no parent.
                let (parent_matrix, parent_rotation) = match transform.borrow().parent {
                    None => {
                        (Matrix4::identity(), Quaternion::identity())
                    },
                    Some(parent) => {
                        let parent_transform = transform_manager.get(parent);

                        let parent_matrix = parent_transform.derived_matrix();
                        let parent_rotation = parent_transform.rotation_derived();

                        (parent_matrix, parent_rotation)
                    }
                };

                transform.borrow().update(parent_matrix, parent_rotation);
            }
        }
    }
}
<|endoftext|>


<commit_before>use types::{OscPacket, OscType, Result, OscMessage, OscMidiMessage, OscColor, OscBundle};
use errors::OscError;
use utils;

use std::{io, char};
use std::io::{Read, BufRead};

use byteorder::{BigEndian, ReadBytesExt};

/// Common MTP size for ethernet
pub const MTP: usize = 1536;

pub fn decode(msg: &[u8]) -> Result<OscPacket> {
    if msg.len() == 0 {
        return Err(OscError::BadPacket("Empty packet."));
    }

    match msg[0] as char {
        '/' => {
            decode_message(msg)
        }
        '#' => {
            decode_bundle(msg)
        }
        _ => Err(OscError::BadPacket("Unknown message format.")),
    }
}

fn decode_message(msg: &[u8]) -> Result<OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);

    let addr: String = try!(read_osc_string(&mut cursor));
    let type_tags: String = try!(read_osc_string(&mut cursor));

    if type_tags.len() > 1 {
        let args: Vec<OscType> = try!(read_osc_args(&mut cursor, type_tags));

        Ok(OscPacket::Message(OscMessage {
            addr: addr,
            args: Some(args),
        }))
    } else {
        Ok(OscPacket::Message(OscMessage {
            addr: addr,
            args: None,
        }))
    }
}

fn decode_bundle(msg: &[u8]) -> Result<OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);

    let b = try!(read_osc_string(&mut cursor));
    if b != "bundle" {
        return Err(OscError::BadBundle(format!("Wrong bundle specifier: {}", bundle_tag)));
    }

    let time_tag = try!(read_time_tag(&mut cursor));
    let mut bundle: Vec<OscPacket> = Vec::new();
    let size: usize = try!(cursor.read_u32::<BigEndian>()
                                 .map_err(OscError::ByteOrderError)) as usize;
    let mut buf: Vec<u8> = Vec::with_capacity(size);
    let cnt: usize = try!(cursor.take(size as u64)
                                .read_to_end(&mut buf)
                                .map_err(OscError::ReadError));
    if cnt == size {
        try!(decode(&mut buf).map(|p| bundle.push(p)));
    } else {
        return Err(OscError::BadBundle("Bundle shorter than expected!".to_string()));
    }

    Ok(OscPacket::Bundle(OscBundle {
        timetag: time_tag,
        content: bundle,
    }))
}

fn read_osc_string(cursor: &mut io::Cursor<&[u8]>) -> Result<String> {
    let mut str_buf: Vec<u8> = Vec::new();
    try!(cursor.read_until(0, &mut str_buf)
               .map_err(OscError::ReadError)); // ignore returned byte count
    pad_cursor(cursor);
    // convert to String and remove nul bytes
    String::from_utf8(str_buf)
        .map_err(|e| OscError::StringError(e))
        .map(|s| {
            s.trim_matches(0u8 as char)
             .to_string()
        })
}

fn read_osc_args(cursor: &mut io::Cursor<&[u8]>, raw_type_tags: String) -> Result<Vec<OscType>> {
    let type_tags: Vec<char> = raw_type_tags.chars()
                                            .skip(1)
                                            .map(|c| c as char)
                                            .collect();
    let mut args: Vec<OscType> = Vec::with_capacity(type_tags.len());
    for tag in type_tags {
        let arg: OscType = try!(read_osc_arg(cursor, tag));
        args.push(arg);
    }
    Ok(args)
}

fn read_osc_arg(cursor: &mut io::Cursor<&[u8]>, tag: char) -> Result<OscType> {
    match tag {
        'f' => {
            cursor.read_f32::<BigEndian>()
                  .map(OscType::Float)
                  .map_err(OscError::ByteOrderError)
        }
        'd' => {
            cursor.read_f64::<BigEndian>()
                  .map(OscType::Double)
                  .map_err(OscError::ByteOrderError)
        }
        'i' => {
            cursor.read_i32::<BigEndian>()
                  .map(OscType::Int)
                  .map_err(OscError::ByteOrderError)
        }
        'h' => {
            cursor.read_i64::<BigEndian>()
                  .map(OscType::Long)
                  .map_err(OscError::ByteOrderError)
        }
        's' => {
            read_osc_string(cursor).map(|s| OscType::String(s))
        }
        't' => {
            read_time_tag(cursor)
        }
        'b' => {
            read_blob(cursor)
        }
        'r' => {
            read_osc_color(cursor)
        }
        'T' => {
            Ok(OscType::Bool(true))
        }
        'F' => {
            Ok(OscType::Bool(false))
        }
        'N' => {
            Ok(OscType::Nil)
        }
        'I' => {
            Ok(OscType::Inf)
        }
        'c' => {
            read_char(cursor)
        }
        'm' => {
            read_midi_message(cursor)
        }
        _ => Err(OscError::BadArg(format!("Type tag \"{}\" is not implemented!", tag))),
    }
}

fn read_char(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let opt_char = try!(cursor.read_u32::<BigEndian>()
                              .map(char::from_u32)
                              .map_err(OscError::ByteOrderError));
    match opt_char {
        Some(c) => Ok(OscType::Char(c)),
        None => Err(OscError::BadArg("Argument is not a char!".to_string())),
    }
}

fn read_blob(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let size: usize = try!(cursor.read_u32::<BigEndian>()
                                 .map_err(OscError::ByteOrderError)) as usize;
    let mut byte_buf: Vec<u8> = Vec::with_capacity(size);

    try!(cursor.take(size as u64)
               .read_to_end(&mut byte_buf)
               .map_err(OscError::ReadError));

    pad_cursor(cursor);

    Ok(OscType::Blob(byte_buf))
}

fn read_time_tag(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let date = try!(cursor.read_u32::<BigEndian>()
                          .map_err(OscError::ByteOrderError));
    let frac = try!(cursor.read_u32::<BigEndian>()
                          .map_err(OscError::ByteOrderError));

    Ok(OscType::Time(date, frac))
}

fn read_midi_message(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let mut buf: Vec<u8> = Vec::with_capacity(4);
    try!(cursor.take(4).read_to_end(&mut buf).map_err(OscError::ReadError));

    Ok(OscType::Midi(OscMidiMessage {
        port: buf[0],
        status: buf[1],
        data1: buf[2],
        data2: buf[3],
    }))

}

fn read_osc_color(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let mut buf: Vec<u8> = Vec::with_capacity(4);
    try!(cursor.take(4).read_to_end(&mut buf).map_err(OscError::ReadError));

    Ok(OscType::Color(OscColor {
        red: buf[0],
        green: buf[1],
        blue: buf[2],
        alpha: buf[3],
    }))
}

fn pad_cursor(cursor: &mut io::Cursor<&[u8]>) {
    let pos = cursor.position();
    cursor.set_position(utils::pad(pos));
}
<commit_msg>Check against whole bundle tag and rename variable<commit_after>use types::{OscPacket, OscType, Result, OscMessage, OscMidiMessage, OscColor, OscBundle};
use errors::OscError;
use utils;

use std::{io, char};
use std::io::{Read, BufRead};

use byteorder::{BigEndian, ReadBytesExt};

/// Common MTP size for ethernet
pub const MTP: usize = 1536;

pub fn decode(msg: &[u8]) -> Result<OscPacket> {
    if msg.len() == 0 {
        return Err(OscError::BadPacket("Empty packet."));
    }

    match msg[0] as char {
        '/' => {
            decode_message(msg)
        }
        '#' => {
            decode_bundle(msg)
        }
        _ => Err(OscError::BadPacket("Unknown message format.")),
    }
}

fn decode_message(msg: &[u8]) -> Result<OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);

    let addr: String = try!(read_osc_string(&mut cursor));
    let type_tags: String = try!(read_osc_string(&mut cursor));

    if type_tags.len() > 1 {
        let args: Vec<OscType> = try!(read_osc_args(&mut cursor, type_tags));

        Ok(OscPacket::Message(OscMessage {
            addr: addr,
            args: Some(args),
        }))
    } else {
        Ok(OscPacket::Message(OscMessage {
            addr: addr,
            args: None,
        }))
    }
}

fn decode_bundle(msg: &[u8]) -> Result<OscPacket> {
    let mut cursor: io::Cursor<&[u8]> = io::Cursor::new(msg);

    let bundle_tag = try!(read_osc_string(&mut cursor));
    if bundle_tag != "#bundle" {
        return Err(OscError::BadBundle(format!("Wrong bundle specifier: {}", bundle_tag)));
    }

    let time_tag = try!(read_time_tag(&mut cursor));
    let mut bundle: Vec<OscPacket> = Vec::new();
    let size: usize = try!(cursor.read_u32::<BigEndian>()
                                 .map_err(OscError::ByteOrderError)) as usize;
    let mut buf: Vec<u8> = Vec::with_capacity(size);
    let cnt: usize = try!(cursor.take(size as u64)
                                .read_to_end(&mut buf)
                                .map_err(OscError::ReadError));
    if cnt == size {
        try!(decode(&mut buf).map(|p| bundle.push(p)));
    } else {
        return Err(OscError::BadBundle("Bundle shorter than expected!".to_string()));
    }

    Ok(OscPacket::Bundle(OscBundle {
        timetag: time_tag,
        content: bundle,
    }))
}

fn read_osc_string(cursor: &mut io::Cursor<&[u8]>) -> Result<String> {
    let mut str_buf: Vec<u8> = Vec::new();
    try!(cursor.read_until(0, &mut str_buf)
               .map_err(OscError::ReadError)); // ignore returned byte count
    pad_cursor(cursor);
    // convert to String and remove nul bytes
    String::from_utf8(str_buf)
        .map_err(|e| OscError::StringError(e))
        .map(|s| {
            s.trim_matches(0u8 as char)
             .to_string()
        })
}

fn read_osc_args(cursor: &mut io::Cursor<&[u8]>, raw_type_tags: String) -> Result<Vec<OscType>> {
    let type_tags: Vec<char> = raw_type_tags.chars()
                                            .skip(1)
                                            .map(|c| c as char)
                                            .collect();
    let mut args: Vec<OscType> = Vec::with_capacity(type_tags.len());
    for tag in type_tags {
        let arg: OscType = try!(read_osc_arg(cursor, tag));
        args.push(arg);
    }
    Ok(args)
}

fn read_osc_arg(cursor: &mut io::Cursor<&[u8]>, tag: char) -> Result<OscType> {
    match tag {
        'f' => {
            cursor.read_f32::<BigEndian>()
                  .map(OscType::Float)
                  .map_err(OscError::ByteOrderError)
        }
        'd' => {
            cursor.read_f64::<BigEndian>()
                  .map(OscType::Double)
                  .map_err(OscError::ByteOrderError)
        }
        'i' => {
            cursor.read_i32::<BigEndian>()
                  .map(OscType::Int)
                  .map_err(OscError::ByteOrderError)
        }
        'h' => {
            cursor.read_i64::<BigEndian>()
                  .map(OscType::Long)
                  .map_err(OscError::ByteOrderError)
        }
        's' => {
            read_osc_string(cursor).map(|s| OscType::String(s))
        }
        't' => {
            read_time_tag(cursor)
        }
        'b' => {
            read_blob(cursor)
        }
        'r' => {
            read_osc_color(cursor)
        }
        'T' => {
            Ok(OscType::Bool(true))
        }
        'F' => {
            Ok(OscType::Bool(false))
        }
        'N' => {
            Ok(OscType::Nil)
        }
        'I' => {
            Ok(OscType::Inf)
        }
        'c' => {
            read_char(cursor)
        }
        'm' => {
            read_midi_message(cursor)
        }
        _ => Err(OscError::BadArg(format!("Type tag \"{}\" is not implemented!", tag))),
    }
}

fn read_char(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let opt_char = try!(cursor.read_u32::<BigEndian>()
                              .map(char::from_u32)
                              .map_err(OscError::ByteOrderError));
    match opt_char {
        Some(c) => Ok(OscType::Char(c)),
        None => Err(OscError::BadArg("Argument is not a char!".to_string())),
    }
}

fn read_blob(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let size: usize = try!(cursor.read_u32::<BigEndian>()
                                 .map_err(OscError::ByteOrderError)) as usize;
    let mut byte_buf: Vec<u8> = Vec::with_capacity(size);

    try!(cursor.take(size as u64)
               .read_to_end(&mut byte_buf)
               .map_err(OscError::ReadError));

    pad_cursor(cursor);

    Ok(OscType::Blob(byte_buf))
}

fn read_time_tag(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let date = try!(cursor.read_u32::<BigEndian>()
                          .map_err(OscError::ByteOrderError));
    let frac = try!(cursor.read_u32::<BigEndian>()
                          .map_err(OscError::ByteOrderError));

    Ok(OscType::Time(date, frac))
}

fn read_midi_message(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let mut buf: Vec<u8> = Vec::with_capacity(4);
    try!(cursor.take(4).read_to_end(&mut buf).map_err(OscError::ReadError));

    Ok(OscType::Midi(OscMidiMessage {
        port: buf[0],
        status: buf[1],
        data1: buf[2],
        data2: buf[3],
    }))

}

fn read_osc_color(cursor: &mut io::Cursor<&[u8]>) -> Result<OscType> {
    let mut buf: Vec<u8> = Vec::with_capacity(4);
    try!(cursor.take(4).read_to_end(&mut buf).map_err(OscError::ReadError));

    Ok(OscType::Color(OscColor {
        red: buf[0],
        green: buf[1],
        blue: buf[2],
        alpha: buf[3],
    }))
}

fn pad_cursor(cursor: &mut io::Cursor<&[u8]>) {
    let pos = cursor.position();
    cursor.set_position(utils::pad(pos));
}
<|endoftext|>


<commit_before>use std::io::{Read, Seek, SeekFrom};
use byteorder::{BigEndian, ByteOrder};
use {HEADER_LEN, FarbfeldError, FarbfeldResult};

/// A farbfeld decoder
#[derive(Debug)]
pub struct FarbfeldDecoder<R> {
    r: R,

    width: u32,
    height: u32,
}

impl<R: Read + Seek> FarbfeldDecoder<R> {
    /// Create a new decoder from `r` and parse the header.
    /// # Failures
    /// Returns FarbfeldError::FormatError if the magic number does not match `farbfeld`
    pub fn new(r: R) -> FarbfeldResult<FarbfeldDecoder<R>> {
        let mut r = r;
        try!(r.seek(SeekFrom::Start(0)));
        let head = &mut [0; HEADER_LEN as usize];
        try!(r.read_exact(head));
        if &head[..8] != "farbfeld".as_bytes() {
                return Err(FarbfeldError::FormatError("unexpected magic number".to_string()))
        }

        Ok(FarbfeldDecoder {
            r: r,
            width: BigEndian::read_u32(&head[8..]),
            height: BigEndian::read_u32(&head[12..]),
        })
    }

    /// Returns the `(width, height)` of the image.
    pub fn dimensions(&mut self) -> (u32, u32) {
        (self.width, self.height)
    }

    /// Returns the length in bytes for a row.
    pub fn row_len(&mut self) -> usize {
        self.width as usize * 4
    }

    /// Read a single row from the image and return the bytes read.
    /// # Failures
    /// Returns a `FarbfeldError::ImageEnd` if the `row` is greater as the `height`
    pub fn read_row(&mut self, row: u32,  buf: &mut [u8]) -> FarbfeldResult<usize> {
        if row > self.height { return Err(FarbfeldError::ImageEnd) }

        let row_len = self.row_len();
        let offset = HEADER_LEN + row as u64 * row_len as u64;
        try!(self.r.seek(SeekFrom::Start(offset)));
        try!(self.r.read_exact(&mut buf[..row_len]));
        Ok(row_len)
    }

    /// Read whole image into a `Vec<u8>`.
    pub fn read_image(&mut self) -> FarbfeldResult<Vec<u8>> {
        try!(self.r.seek(SeekFrom::Start(HEADER_LEN)));
        let num_raw_bytes = self.height as usize * self.row_len();
        let mut buf = vec![0; num_raw_bytes];
        try!(self.r.read_exact(&mut buf));
        Ok(buf)
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor, ErrorKind, Write};
    use FarbfeldDecoder;
    use tests::IMAGE_DATA;
    use FarbfeldError;

    macro_rules! try_panic {
        ($e:expr) => {
            match $e {
                Err(e) => panic!(e),
                Ok(res) => res,
            }
        }
    }

    #[test]
    fn invalid_magic() {
        let mut img_data = Vec::new();
        try_panic!(img_data.write(b"test fail"));
        try_panic!(img_data.write(&IMAGE_DATA[8..]));
        let buf = Cursor::new(img_data);

        match FarbfeldDecoder::new(buf) {
            Err(e) => match e {
                FarbfeldError::FormatError(_) => return,
                e => panic!("{:?}", e),
            },
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }
    }

    #[test]
    fn truncate_header() {
        let buf = Cursor::new(&IMAGE_DATA[0..8]);

        match FarbfeldDecoder::new(buf) {
            Err(FarbfeldError::IoError(e)) => {
                if e.kind() == ErrorKind::UnexpectedEof {
                    return
                } else { panic!("{:?}", e) }
            },
            Err(e) => panic!("{:?}", e),
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }

    }

    #[test]
    fn truncate_data() {
        let mut img_data = Vec::with_capacity(IMAGE_DATA.len()-1);
        try_panic!(img_data.write_all(&IMAGE_DATA[..IMAGE_DATA.len()-1]));
        let buf = Cursor::new(img_data);
        let mut img = FarbfeldDecoder::new(buf).unwrap();
        match img.read_image() {
            Err(FarbfeldError::IoError(e)) => {
                if e.kind() == ErrorKind::UnexpectedEof {
                    return
                } else { panic!("{:?}", e) }
            },
            Err(e) => panic!("{:?}", e),
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }
    }
}
<commit_msg>decode data as RGBA64<commit_after>use std::io::{Read, Seek, SeekFrom};
use byteorder::{BigEndian, ByteOrder};
use {HEADER_LEN, FarbfeldError, FarbfeldResult};

/// A farbfeld decoder
#[derive(Debug)]
pub struct FarbfeldDecoder<R> {
    r: R,

    width: u32,
    height: u32,
}

impl<R: Read + Seek> FarbfeldDecoder<R> {
    /// Create a new decoder from `r` and parse the header.
    /// # Failures
    /// Returns FarbfeldError::FormatError if the magic number does not match `farbfeld`
    pub fn new(r: R) -> FarbfeldResult<FarbfeldDecoder<R>> {
        let mut r = r;
        try!(r.seek(SeekFrom::Start(0)));
        let head = &mut [0; HEADER_LEN as usize];
        try!(r.read_exact(head));
        if &head[..8] != "farbfeld".as_bytes() {
                return Err(FarbfeldError::FormatError("unexpected magic number".to_string()))
        }

        Ok(FarbfeldDecoder {
            r: r,
            width: BigEndian::read_u32(&head[8..]),
            height: BigEndian::read_u32(&head[12..]),
        })
    }

    /// Returns the `(width, height)` of the image.
    pub fn dimensions(&mut self) -> (u32, u32) {
        (self.width, self.height)
    }

    /// Returns the length in bytes for a row.
    pub fn row_len(&mut self) -> usize {
        self.width as usize * 8
    }

    /// Read a single row from the image and return the bytes read.
    /// # Failures
    /// Returns a `FarbfeldError::ImageEnd` if the `row` is greater as the `height`
    pub fn read_row(&mut self, row: u32,  buf: &mut [u8]) -> FarbfeldResult<usize> {
        if row > self.height { return Err(FarbfeldError::ImageEnd) }

        let row_len = self.row_len();
        let offset = HEADER_LEN + row as u64 * row_len as u64;
        try!(self.r.seek(SeekFrom::Start(offset)));
        try!(self.r.read_exact(&mut buf[..row_len]));
        Ok(row_len)
    }

    /// Read whole image into a `Vec<u8>`.
    pub fn read_image(&mut self) -> FarbfeldResult<Vec<u8>> {
        try!(self.r.seek(SeekFrom::Start(HEADER_LEN)));
        let num_raw_bytes = self.height as usize * self.row_len();
        let mut buf = vec![0; num_raw_bytes];
        try!(self.r.read_exact(&mut buf));
        Ok(buf)
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor, ErrorKind, Write};
    use FarbfeldDecoder;
    use tests::IMAGE_DATA;
    use FarbfeldError;

    macro_rules! try_panic {
        ($e:expr) => {
            match $e {
                Err(e) => panic!(e),
                Ok(res) => res,
            }
        }
    }

    #[test]
    fn invalid_magic() {
        let mut img_data = Vec::new();
        try_panic!(img_data.write(b"test fail"));
        try_panic!(img_data.write(&IMAGE_DATA[8..]));
        let buf = Cursor::new(img_data);

        match FarbfeldDecoder::new(buf) {
            Err(e) => match e {
                FarbfeldError::FormatError(_) => return,
                e => panic!("{:?}", e),
            },
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }
    }

    #[test]
    fn truncate_header() {
        let buf = Cursor::new(&IMAGE_DATA[0..8]);

        match FarbfeldDecoder::new(buf) {
            Err(FarbfeldError::IoError(e)) => {
                if e.kind() == ErrorKind::UnexpectedEof {
                    return
                } else { panic!("{:?}", e) }
            },
            Err(e) => panic!("{:?}", e),
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }

    }

    #[test]
    fn truncate_data() {
        let mut img_data = Vec::with_capacity(IMAGE_DATA.len()-1);
        try_panic!(img_data.write_all(&IMAGE_DATA[..IMAGE_DATA.len()-1]));
        let buf = Cursor::new(img_data);
        let mut img = FarbfeldDecoder::new(buf).unwrap();
        match img.read_image() {
            Err(FarbfeldError::IoError(e)) => {
                if e.kind() == ErrorKind::UnexpectedEof {
                    return
                } else { panic!("{:?}", e) }
            },
            Err(e) => panic!("{:?}", e),
            Ok(_) => panic!("Got Ok expected FarbfeldError::FormatError"),
        }
    }
}
<|endoftext|>