<commit_before>use std::io::{Read, Write};
use std::fmt;
use std::cell::{RefMut, RefCell};
use std::ascii::AsciiExt;

use serde::{Serialize, Deserialize};
use serde_json;
use curl;
use url;

use error::GGRResult;
use error::GGRError;

fn send_req<W: Write>(handle: &mut curl::easy::Easy,
                      out: &mut W,
                      body: Option<Vec<u8>>)
                      -> GGRResult<(u32, Vec<String>)> {
    match body {
        Some(body) => {
            let mut body = &body[..];
            handle.upload(true)?;
            handle.in_filesize(body.len() as u64)?;
            handle_req(handle, out, &mut |buf| body.read(buf).unwrap_or(0))
        }
        None => handle_req(handle, out, &mut |_| 0),
    }
}

fn handle_req<W: Write>(handle: &mut curl::easy::Easy,
                        out: &mut W,
                        read: &mut FnMut(&mut [u8]) -> usize)
                        -> GGRResult<(u32, Vec<String>)> {
    let mut headers = Vec::new();
    {
        let mut handle = handle.transfer();
        handle.read_function(|buf| Ok(read(buf)))?;
        handle.write_function(|data| {
                Ok(match out.write_all(data) {
                    Ok(_) => data.len(),
                    Err(_) => 0,
                })
            })?;
        handle.header_function(|data| {
                headers.push(String::from_utf8_lossy(data).into_owned());
                true
            })?;
        handle.perform()?;
    }

    Ok((handle.response_code()?, headers))
}

#[derive(PartialEq, Debug)]
enum CallMethod {
    Get,
    Post,
    Put,
    Delete,
}

impl fmt::Display for CallMethod {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CallMethod::Get => write!(f, "GET"),
            CallMethod::Post => write!(f, "POST"),
            CallMethod::Put => write!(f, "PUT"),
            CallMethod::Delete => write!(f, "DELETE"),
        }
    }
}

pub struct Call {
    shared_handle: RefCell<curl::easy::Easy>,
    base: url::Url,
}

impl Call {
    pub fn new(url: &url::Url) -> Call {
        Call {
            shared_handle: RefCell::new(curl::easy::Easy::new()),
            base: url.clone(),
        }
    }

    pub fn set_url_query(&mut self, q: Option<&str>) {
        self.base.set_query(q);
    }

    // Low Level Methods

    fn do_request(&self, method: &CallMethod, url: &str) -> GGRResult<CallRequest> {
        let mut handle = self.shared_handle.borrow_mut();
        try!(handle.cookie_session(true));
        try!(handle.netrc(curl::easy::NetRc::Required));

        CallRequest::new(handle, method, url)
    }

    fn request<S: Serialize>(&self, method: CallMethod, path: &str, body: Option<&S>) -> GGRResult<CallResponse> {
        let mut sendurl = self.base.clone();
        // double replace for pathes with three ///.
        let complete_path = format!("{}/{}", sendurl.path(), path).replace("//", "/").replace("//", "/");
        sendurl.set_path(&complete_path);

        debug!("url-to-send: {:?}", sendurl);

        for am in vec!(
            curl::easy::Auth::new().digest(true),
            curl::easy::Auth::new().basic(true),
        ) {
            let mut call_request = try!(self.do_request(&method, &sendurl.to_owned().into_string()));
            if let Some(body) = body {
                call_request.with_json_body(&body).ok();
            }

            try!(call_request.handle.http_auth(am));
            let call_response = try!(call_request.send());
            if call_response.status == 401 {
                continue;
            }
            return Ok(call_response);
        }
        Err(GGRError::General("No Authentication algorithm found for your gerrit server. 'basic' and 'digest' tested".into()))
    }

    /// Convenience method that performs a `GET` request.
    pub fn get(&self, path: &str) -> GGRResult<CallResponse> {
        self.request::<String>(CallMethod::Get, path, None)
    }

    /// Convenience method that performs a `DELETE` request.
    pub fn delete(&self, path: &str) -> GGRResult<CallResponse> {
        self.request::<String>(CallMethod::Delete, path, None)
    }

    /// Convenience method that performs a `POST` request with JSON data.
    pub fn post<S: Serialize>(&self, path: &str, body: &S) -> GGRResult<CallResponse> {
        self.request(CallMethod::Post, path, Some(body))
    }

    /// Convenience method that performs a `PUT` request with JSON data.
    pub fn put<S: Serialize>(&self, path: &str, body: &S) -> GGRResult<CallResponse> {
        self.request(CallMethod::Put, path, Some(body))
    }
}

/// Iterator over response headers
#[allow(dead_code)]
pub struct Headers<'a> {
    lines: &'a [String],
    idx: usize,
}

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

    fn next(&mut self) -> Option<(&'a str, &'a str)> {
        self.lines.get(self.idx).map(|line| {
            self.idx += 1;
            match line.find(':') {
                Some(i) => (&line[..i], line[i + 1..].trim()),
                None => (line[..].trim(), ""),
            }
        })
    }
}

pub struct CallRequest<'a> {
    handle: RefMut<'a, curl::easy::Easy>,
    headers: curl::easy::List,
    body: Option<Vec<u8>>,
}

impl<'a> CallRequest<'a> {
    fn new(mut handle: RefMut<'a, curl::easy::Easy>,
           method: &CallMethod,
           url: &str)
           -> GGRResult<CallRequest<'a>> {
        debug!("request {} {}", method, url);

        let mut headers = curl::easy::List::new();
        headers.append("Accept: application/json").ok();

        match *method {
            CallMethod::Get => try!(handle.get(true)),
            CallMethod::Post => try!(handle.custom_request("POST")),
            CallMethod::Put => try!(handle.custom_request("PUT")),
            CallMethod::Delete => try!(handle.custom_request("DELETE")),
        }

        handle.url(url)?;

        Ok(CallRequest {
            handle: handle,
            headers: headers,
            body: None,
        })
    }

    /// adds a specific header to the request
    pub fn with_header(mut self, key: &str, value: &str) -> GGRResult<CallRequest<'a>> {
        self.headers.append(&format!("{}: {}", key, value))?;
        Ok(self)
    }

    /// sets the JSON request body for the request.
    pub fn with_json_body<S: Serialize>(&mut self, body: &S) -> GGRResult<&mut CallRequest<'a>> {
        let mut body_bytes: Vec<u8> = vec![];
        serde_json::to_writer(&mut body_bytes, &body)?;
        debug!("sending JSON data ({} bytes)", body_bytes.len());
        self.body = Some(body_bytes);
        self.headers.append("Content-Type: application/json")?;
        Ok(self)
    }

    /// attaches some form data to the request.
    pub fn with_form_data(&mut self, form: curl::easy::Form) -> GGRResult<&mut CallRequest<'a>> {
        debug!("sending form data");
        self.handle.httppost(form)?;
        self.body = None;
        Ok(self)
    }

    /// enables or disables redirects.  The default is off.
    pub fn follow_location(&mut self, val: bool) -> GGRResult<&mut CallRequest<'a>> {
        debug!("follow redirects: {}", val);
        self.handle.follow_location(val)?;
        Ok(self)
    }

    /// Sends the request and writes response data into the given file
    /// instead of the response object's in memory buffer.
    pub fn send_into<W: Write>(mut self, out: &mut W) -> GGRResult<CallResponse> {
        self.handle.http_headers(self.headers)?;
        let local_body = self.body.clone();
        let (status, headers) = send_req(&mut self.handle, out, local_body)?;
        debug!("response: {}", status);
        Ok(CallResponse {
            status: status,
            headers: headers,
            body: None,
        })
    }

    /// Sends the request and reads the response body into the response object.
    pub fn send(self) -> GGRResult<CallResponse> {
        let mut out = vec![];
        let mut rv = self.send_into(&mut out)?;

        debug!("return-from-server: {:?}", rv);

        // cut first 4 bytes from output stream
        // *NOTICE**: The first 4 characters are cutted from the returned content. We want only
        // json data which has a prevention against XSSI attacks. More here:
        // <https://gerrit-documentation.storage.googleapis.com/Documentation/2.12.3/rest-api.html#output>
        if out.starts_with(b")]}'") {
            out = out[4..].into();
        }

        rv.body = Some(out);
        Ok(rv)
    }
}

#[derive(Clone, Debug)]
pub struct CallResponse {
    status: u32,
    headers: Vec<String>,
    body: Option<Vec<u8>>,
}

impl CallResponse {
    /// Returns the status code of the response
    pub fn status(&self) -> u32 {
        self.status
    }

    /// Indicates that the request failed
    pub fn failed(&self) -> bool {
        self.status >= 400 && self.status <= 600
    }

    /// Indicates that the request succeeded
    pub fn ok(&self) -> bool {
        !self.failed()
    }

    /// Converts the response into a result object.  This also converts non okay response codes
    /// into errors.
    pub fn to_result(&self) -> GGRResult<&CallResponse> {
        debug!("headers:");
        for (header_key, header_value) in self.headers() {
            if !header_value.is_empty() {
                debug!("  {}: {}", header_key, header_value);
            }
        }
        if let Some(ref body) = self.body {
            debug!("body: {}", String::from_utf8_lossy(body));
        }
        if self.ok() {
            return Ok(self);
        }
        Err(GGRError::General(format!("generic error: {}", self.status())))
    }

    /// Deserializes the response body into the given type
    pub fn deserialize<T: Deserialize>(&self) -> GGRResult<T> {
        Ok(serde_json::from_reader(match self.body {
            Some(ref body) => body,
            None => &b""[..],
        })?)
    }

    /// Like `deserialize` but consumes the response and will convert
    /// failed requests into proper errors.
    pub fn convert<T: Deserialize>(self) -> GGRResult<T> {
        self.to_result().and_then(|x| x.deserialize())
    }

    /// Iterates over the headers.
    #[allow(dead_code)]
    pub fn headers(&self) -> Headers {
        Headers {
            lines: &self.headers[..],
            idx: 0,
        }
    }

    /// Looks up the first matching header for a key.
    #[allow(dead_code)]
    pub fn get_header(&self, key: &str) -> Option<&str> {
        for (header_key, header_value) in self.headers() {
            if header_key.eq_ignore_ascii_case(key) {
                return Some(header_value);
            }
        }
        None
    }

    pub fn get_body(&self) -> Option<Vec<u8>> {
        self.body.clone()
    }
}
<commit_msg>fix problem with errorhandling<commit_after>use std::io::{Read, Write};
use std::fmt;
use std::cell::{RefMut, RefCell};
use std::ascii::AsciiExt;

use serde::{Serialize, Deserialize};
use serde_json;
use curl;
use url;

use error::GGRResult;
use error::GGRError;

fn send_req<W: Write>(handle: &mut curl::easy::Easy,
                      out: &mut W,
                      body: Option<Vec<u8>>)
                      -> GGRResult<(u32, Vec<String>)> {
    match body {
        Some(body) => {
            let mut body = &body[..];
            handle.upload(true)?;
            handle.in_filesize(body.len() as u64)?;
            handle_req(handle, out, &mut |buf| body.read(buf).unwrap_or(0))
        }
        None => handle_req(handle, out, &mut |_| 0),
    }
}

fn handle_req<W: Write>(handle: &mut curl::easy::Easy,
                        out: &mut W,
                        read: &mut FnMut(&mut [u8]) -> usize)
                        -> GGRResult<(u32, Vec<String>)> {
    let mut headers = Vec::new();
    {
        let mut handle = handle.transfer();
        handle.read_function(|buf| Ok(read(buf)))?;
        handle.write_function(|data| {
                Ok(match out.write_all(data) {
                    Ok(_) => data.len(),
                    Err(_) => 0,
                })
            })?;
        handle.header_function(|data| {
                headers.push(String::from_utf8_lossy(data).into_owned());
                true
            })?;
        handle.perform()?;
    }

    Ok((handle.response_code()?, headers))
}

#[derive(PartialEq, Debug)]
enum CallMethod {
    Get,
    Post,
    Put,
    Delete,
}

impl fmt::Display for CallMethod {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CallMethod::Get => write!(f, "GET"),
            CallMethod::Post => write!(f, "POST"),
            CallMethod::Put => write!(f, "PUT"),
            CallMethod::Delete => write!(f, "DELETE"),
        }
    }
}

pub struct Call {
    shared_handle: RefCell<curl::easy::Easy>,
    base: url::Url,
}

impl Call {
    pub fn new(url: &url::Url) -> Call {
        Call {
            shared_handle: RefCell::new(curl::easy::Easy::new()),
            base: url.clone(),
        }
    }

    pub fn set_url_query(&mut self, q: Option<&str>) {
        self.base.set_query(q);
    }

    // Low Level Methods

    fn do_request(&self, method: &CallMethod, url: &str) -> GGRResult<CallRequest> {
        let mut handle = self.shared_handle.borrow_mut();
        try!(handle.cookie_session(true));
        try!(handle.netrc(curl::easy::NetRc::Required));

        CallRequest::new(handle, method, url)
    }

    fn request<S: Serialize>(&self, method: CallMethod, path: &str, body: Option<&S>) -> GGRResult<CallResponse> {
        let mut sendurl = self.base.clone();
        // double replace for pathes with three ///.
        let complete_path = format!("{}/{}", sendurl.path(), path).replace("//", "/").replace("//", "/");
        sendurl.set_path(&complete_path);

        debug!("url-to-send: {:?}", sendurl);

        for am in vec!(
            curl::easy::Auth::new().digest(true),
            curl::easy::Auth::new().basic(true),
        ) {
            let mut call_request = try!(self.do_request(&method, &sendurl.to_owned().into_string()));
            if let Some(body) = body {
                call_request.with_json_body(&body).ok();
            }

            try!(call_request.handle.http_auth(am));
            let call_response = try!(call_request.send());
            if call_response.status == 401 {
                continue;
            }
            return Ok(call_response);
        }
        Err(GGRError::General("No Authentication algorithm found for your gerrit server. 'basic' and 'digest' tested".into()))
    }

    /// Convenience method that performs a `GET` request.
    pub fn get(&self, path: &str) -> GGRResult<CallResponse> {
        self.request::<String>(CallMethod::Get, path, None)
    }

    /// Convenience method that performs a `DELETE` request.
    pub fn delete(&self, path: &str) -> GGRResult<CallResponse> {
        self.request::<String>(CallMethod::Delete, path, None)
    }

    /// Convenience method that performs a `POST` request with JSON data.
    pub fn post<S: Serialize>(&self, path: &str, body: &S) -> GGRResult<CallResponse> {
        self.request(CallMethod::Post, path, Some(body))
    }

    /// Convenience method that performs a `PUT` request with JSON data.
    pub fn put<S: Serialize>(&self, path: &str, body: &S) -> GGRResult<CallResponse> {
        self.request(CallMethod::Put, path, Some(body))
    }
}

/// Iterator over response headers
#[allow(dead_code)]
pub struct Headers<'a> {
    lines: &'a [String],
    idx: usize,
}

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

    fn next(&mut self) -> Option<(&'a str, &'a str)> {
        self.lines.get(self.idx).map(|line| {
            self.idx += 1;
            match line.find(':') {
                Some(i) => (&line[..i], line[i + 1..].trim()),
                None => (line[..].trim(), ""),
            }
        })
    }
}

pub struct CallRequest<'a> {
    handle: RefMut<'a, curl::easy::Easy>,
    headers: curl::easy::List,
    body: Option<Vec<u8>>,
}

impl<'a> CallRequest<'a> {
    fn new(mut handle: RefMut<'a, curl::easy::Easy>,
           method: &CallMethod,
           url: &str)
           -> GGRResult<CallRequest<'a>> {
        debug!("request {} {}", method, url);

        let mut headers = curl::easy::List::new();
        headers.append("Accept: application/json").ok();

        match *method {
            CallMethod::Get => try!(handle.get(true)),
            CallMethod::Post => try!(handle.custom_request("POST")),
            CallMethod::Put => try!(handle.custom_request("PUT")),
            CallMethod::Delete => try!(handle.custom_request("DELETE")),
        }

        handle.url(url)?;

        Ok(CallRequest {
            handle: handle,
            headers: headers,
            body: None,
        })
    }

    /// adds a specific header to the request
    pub fn with_header(mut self, key: &str, value: &str) -> GGRResult<CallRequest<'a>> {
        self.headers.append(&format!("{}: {}", key, value))?;
        Ok(self)
    }

    /// sets the JSON request body for the request.
    pub fn with_json_body<S: Serialize>(&mut self, body: &S) -> GGRResult<&mut CallRequest<'a>> {
        let mut body_bytes: Vec<u8> = vec![];
        serde_json::to_writer(&mut body_bytes, &body)?;
        debug!("sending JSON data ({} bytes)", body_bytes.len());
        self.body = Some(body_bytes);
        self.headers.append("Content-Type: application/json")?;
        Ok(self)
    }

    /// attaches some form data to the request.
    pub fn with_form_data(&mut self, form: curl::easy::Form) -> GGRResult<&mut CallRequest<'a>> {
        debug!("sending form data");
        self.handle.httppost(form)?;
        self.body = None;
        Ok(self)
    }

    /// enables or disables redirects.  The default is off.
    pub fn follow_location(&mut self, val: bool) -> GGRResult<&mut CallRequest<'a>> {
        debug!("follow redirects: {}", val);
        self.handle.follow_location(val)?;
        Ok(self)
    }

    /// Sends the request and writes response data into the given file
    /// instead of the response object's in memory buffer.
    pub fn send_into<W: Write>(mut self, out: &mut W) -> GGRResult<CallResponse> {
        self.handle.http_headers(self.headers)?;
        let local_body = self.body.clone();
        let (status, headers) = send_req(&mut self.handle, out, local_body)?;
        debug!("response: {}", status);
        Ok(CallResponse {
            status: status,
            headers: headers,
            body: None,
        })
    }

    /// Sends the request and reads the response body into the response object.
    pub fn send(self) -> GGRResult<CallResponse> {
        let mut out = vec![];
        let mut rv = self.send_into(&mut out)?;

        let mut valid = false;

        debug!("return-from-server: {:?}", rv);

        // cut first 4 bytes from output stream
        // **NOTICE**: The first 4 characters are cutted from the returned content. We want only
        // json data which has a prevention against XSSI attacks. More here:
        // <https://gerrit-documentation.storage.googleapis.com/Documentation/2.12.3/rest-api.html#output>
        if out.starts_with(b")]}'") {
            out = out[4..].into();
            valid = true;
        }

        if valid {
            rv.body = Some(out);
            Ok(rv)
        } else {
            Err(GGRError::General(String::from_utf8_lossy(&out).into_owned().trim().into()))
        }
    }
}

#[derive(Clone, Debug)]
pub struct CallResponse {
    status: u32,
    headers: Vec<String>,
    body: Option<Vec<u8>>,
}

impl CallResponse {
    /// Returns the status code of the response
    pub fn status(&self) -> u32 {
        self.status
    }

    /// Indicates that the request failed
    pub fn failed(&self) -> bool {
        self.status >= 400 && self.status <= 600
    }

    /// Indicates that the request succeeded
    pub fn ok(&self) -> bool {
        !self.failed()
    }

    /// Converts the response into a result object.  This also converts non okay response codes
    /// into errors.
    pub fn to_result(&self) -> GGRResult<&CallResponse> {
        debug!("headers:");
        for (header_key, header_value) in self.headers() {
            if !header_value.is_empty() {
                debug!("  {}: {}", header_key, header_value);
            }
        }
        if let Some(ref body) = self.body {
            debug!("body: {}", String::from_utf8_lossy(body));
        }
        if self.ok() {
            return Ok(self);
        }
        Err(GGRError::General(format!("generic error: {}", self.status())))
    }

    /// Deserializes the response body into the given type
    pub fn deserialize<T: Deserialize>(&self) -> GGRResult<T> {
        Ok(serde_json::from_reader(match self.body {
            Some(ref body) => body,
            None => &b""[..],
        })?)
    }

    /// Like `deserialize` but consumes the response and will convert
    /// failed requests into proper errors.
    pub fn convert<T: Deserialize>(self) -> GGRResult<T> {
        self.to_result().and_then(|x| x.deserialize())
    }

    /// Iterates over the headers.
    #[allow(dead_code)]
    pub fn headers(&self) -> Headers {
        Headers {
            lines: &self.headers[..],
            idx: 0,
        }
    }

    /// Looks up the first matching header for a key.
    #[allow(dead_code)]
    pub fn get_header(&self, key: &str) -> Option<&str> {
        for (header_key, header_value) in self.headers() {
            if header_key.eq_ignore_ascii_case(key) {
                return Some(header_value);
            }
        }
        None
    }

    pub fn get_body(&self) -> Option<Vec<u8>> {
        self.body.clone()
    }
}
<|endoftext|>


<commit_before>extern crate ansi_term;
extern crate failure;
extern crate rawr;

use ansi_term::Colour::Blue;
use ansi_term::Colour::Green;
use ansi_term::Colour::Purple;

use configuration::Configuration;

use LibResult;

use rawr::options::ListingOptions;
use rawr::structures::comment::Comment;
use rawr::structures::subreddit::Subreddit;
use rawr::structures::submission::Submission;
use rawr::traits::Commentable;
use rawr::traits::Content;
use rawr::traits::Editable;

/*
 * Reddit module to handle rawr api, exposing RedditContent for other
 * modules to use
 */

pub enum RedditContent<'a, 'b> {
    PostComment(&'a Comment<'a>, &'b Configuration),
    SelfPost(&'a Submission<'a>, &'b Configuration),
    LinkPost(&'a Submission<'a>, &'b Configuration),
}

pub fn new_reddit_content_from_post<'a, 'b>(
    post: &'a Submission,
    config: &'b Configuration,
) -> RedditContent<'a, 'b> {
    if post.is_self_post() {
        RedditContent::SelfPost(post, config)
    } else {
        RedditContent::LinkPost(post, config)
    }
}

pub fn new_reddit_content_from_comment<'a, 'b>(
    comment: &'a Comment,
    config: &'b Configuration,
) -> RedditContent<'a, 'b> {
    RedditContent::PostComment(comment, config)
}

impl <'a, 'b> RedditContent<'a, 'b> {
    /*
     * Gets the name of the content, for storing in the db.
     */
    pub fn name(&self) -> &str {
        self.info().name()
    }

    /*
     * Gets the body of the reddit content.
     * Link posts have no body so return None
     */
    pub fn body(&self) -> Option<String> {
        match self {
            &RedditContent::PostComment(comment, _) => Some(comment.body().unwrap()),
            &RedditContent::SelfPost(post, _) => Some(post.body().unwrap()),
            _ => None,
        }
    }

    /*
     * Gets the title of the reddit content.
     * Comments have no title so return None
     */
    pub fn title(&self) -> Option<String> {
        match self {
            &RedditContent::SelfPost(post, _) => Some(post.title().to_string()),
            &RedditContent::LinkPost(post, _) => Some(post.title().to_string()),
            _ => None,
        }
    }

    /*
     * Gets the link url of the reddit content.
     * This is only defined for link posts
     */
    pub fn link_url(&self) -> Option<String> {
        match self {
             &RedditContent::LinkPost(post, _) => Some(post.link_url().unwrap()),
             _ => None,
        }
    }

    /**
     * Gets the name of the author of this reddit content.
     */
    pub fn author(&self) -> String {
        match self {
            &RedditContent::PostComment(post, _) => post.author().name,
            &RedditContent::SelfPost(post, _) => post.author().name,
            &RedditContent::LinkPost(post, _) => post.author().name,
        }
    }

    /*
    * The type of reddit content as a string
    */
    pub fn content_type(&self) -> String {
        match self {
            &RedditContent::SelfPost(_, _) => String::from("self post"),
            &RedditContent::LinkPost(_, _) => String::from("link post"),
            &RedditContent::PostComment(_, _) => String::from("comment"),
        }
    }

    pub fn is_comment(&self) -> bool {
        match self {
            &RedditContent::PostComment(_, _) => true,
            _ => false,
        }
    }

    /*
     * Checks if the content has been replied to by the bot already,
     * either due to an existing bot's comment in reply or because
     * the bot has logged this reply in the database
     * (in case its comment was deleted)
     */
    pub fn replied_to(&self) -> LibResult<(bool)> {
        // create a copy of the content to fetch its replies as the copy will
        // be consumed
        let replies = match self {
            &RedditContent::PostComment(comment, _) => comment.clone().replies(),
            &RedditContent::SelfPost(post, _) => post.clone().replies(),
            &RedditContent::LinkPost(post, _) => post.clone().replies(),
        };
        // check database first as it doesn't use up rate limits
        Ok(
            self.config().database.replied(self.name())? ||
            replies?.any(|c| {
                c.author().name == self.config().authentication.username
            })
        )
    }

    /*
     * Replies to the comment and logs it in the database
     */
    pub fn reply(&self, reply: &str) -> LibResult<()> {
        println!("{} {}", Blue.paint("Replying:"), reply);
        self.commentable().reply(reply)?; // TODO check why this might have failed
        println!("{}", Green.paint("Replied, saving to db"));
        self.config().database.reply(self.name())?;
        Ok(())
    }

    fn commentable(&self) -> &'a Commentable {
        match self {
            &RedditContent::PostComment(comment, _) => comment,
            &RedditContent::SelfPost(post, _) => post,
            &RedditContent::LinkPost(post, _) => post,
        }
    }

    fn info(&self) -> &'a Content {
        match self {
            &RedditContent::PostComment(comment, _) => comment,
            &RedditContent::SelfPost(post, _) => post,
            &RedditContent::LinkPost(post, _) => post,
        }
    }

    fn config(&self) -> &'b Configuration {
        match self {
            &RedditContent::PostComment(_, config) => config,
            &RedditContent::SelfPost(_, config) => config,
            &RedditContent::LinkPost(_, config) => config,
        }
    }
}

// Since the crawler owns the Subreddit its lifetime is only tied to the config reference
pub struct SubredditCrawler<'c> {
    config: &'c Configuration,
    subreddit: Subreddit<'c>,
}

/*
 * Creates a crawler for this subreddit and configuration
 */
pub fn new_subreddit_crawler<'c>(
    subreddit: &str,
    config: &'c Configuration,
) -> SubredditCrawler<'c> {
    SubredditCrawler {
        config,
        subreddit: config.client.subreddit(subreddit)
    }
}

impl<'c> SubredditCrawler<'c> {
    /*
     * Runs the crawler with this behavior
     */
    pub fn run<F>(&self, behavior: &F) -> LibResult<()>
    where F: Fn(&RedditContent) -> LibResult<()> {
        self.search(behavior)
    }

    fn search<F>(&self, behavior: &F) -> LibResult<()>
    where F: Fn(&RedditContent) -> LibResult<()> {
        let about = self.subreddit.about();
        if about.is_ok() {
            println!("{} {} {}", Purple.paint("Subreddit"), self.subreddit.name,
                    about.unwrap().display_name());
        } else {
            eprintln!("Could not fetch about data in {}", self.subreddit.name);
        }
        let hot = self.subreddit.hot(ListingOptions::default())?;
        for post in hot.take(7) {
            println!("Found '{}' in '{}'", post.title(), self.subreddit.name);
            println!();
            self.search_post(post, &behavior)?;
        }
        Ok(())
    }

    /*
     * Scans the post, possibly replying and then recurses on the post comments
     */
    fn search_post<F>(&self, post: Submission, behavior: &F) -> LibResult<()>
    where F: Fn(&RedditContent) -> LibResult<()> {
        // make a copy of the title to continue referring to after post is consumed
        let title = String::from(post.title()).clone();
        println!("Scanning '{}'", title);

        {
            let content = new_reddit_content_from_post(&post, self.config);
            if !content.replied_to()? {
                behavior(&content)?;
            }
            // take back the post
        }

        // give the post to `replies` which will consume it
        let comments = post.replies()?.take(100);
        for comment in comments {
            self.scan_comment_tree(comment, behavior)?;
        }
        Ok(())
    }

    fn scan_comment_tree<F>(&self, comment: Comment, behavior: &F) -> LibResult<()>
    where F: Fn(&RedditContent) -> LibResult<()> {
        {
            let content = new_reddit_content_from_comment(&comment, self.config);

            if !content.replied_to()? {
                behavior(&content)?;
            }
            // take back the comment
        }

        // consume the Comment to get its replies
        let replies = comment.replies()?;
        for reply in replies.take(10) {
            self.scan_comment_tree(reply, behavior)?;
        }
        Ok(())
    }
}
<commit_msg>Fix empty self post breaking the bot<commit_after>extern crate ansi_term;
extern crate failure;
extern crate rawr;

use ansi_term::Colour::Blue;
use ansi_term::Colour::Green;
use ansi_term::Colour::Purple;

use configuration::Configuration;

use LibResult;

use rawr::options::ListingOptions;
use rawr::structures::comment::Comment;
use rawr::structures::subreddit::Subreddit;
use rawr::structures::submission::Submission;
use rawr::traits::Commentable;
use rawr::traits::Content;
use rawr::traits::Editable;

/*
 * Reddit module to handle rawr api, exposing RedditContent for other
 * modules to use
 */

pub enum RedditContent<'a, 'b> {
    PostComment(&'a Comment<'a>, &'b Configuration),
    SelfPost(&'a Submission<'a>, &'b Configuration),
    LinkPost(&'a Submission<'a>, &'b Configuration),
}

pub fn new_reddit_content_from_post<'a, 'b>(
    post: &'a Submission,
    config: &'b Configuration,
) -> RedditContent<'a, 'b> {
    if post.is_self_post() {
        RedditContent::SelfPost(post, config)
    } else {
        RedditContent::LinkPost(post, config)
    }
}

pub fn new_reddit_content_from_comment<'a, 'b>(
    comment: &'a Comment,
    config: &'b Configuration,
) -> RedditContent<'a, 'b> {
    RedditContent::PostComment(comment, config)
}

impl <'a, 'b> RedditContent<'a, 'b> {
    /*
     * Gets the name of the content, for storing in the db.
     */
    pub fn name(&self) -> &str {
        self.info().name()
    }

    /*
     * Gets the body of the reddit content.
     * Link posts have no body so return None
     */
    pub fn body(&self) -> Option<String> {
        match self {
            &RedditContent::PostComment(comment, _) => Some(
                // If no comment body return empty string.
                // This should happen only in unusual circumstances
                // like deleted comments.
                comment.body().unwrap_or(String::new())),
            &RedditContent::SelfPost(post, _) => Some(
                // If no self post body then return empty string.
                // The self post that broke the bot prior to
                // this fix:
                // https://www.reddit.com/r/demisexuality/comments/9ian5v/do_demisexuals_experience_actual_lust_for_their/
                post.body().unwrap_or(String::new())),
                // Link posts are never expected to have a body
            _ => None,
        }
    }

    /*
     * Gets the title of the reddit content.
     * Comments have no title so return None
     */
    pub fn title(&self) -> Option<String> {
        match self {
            &RedditContent::SelfPost(post, _) => Some(post.title().to_string()),
            &RedditContent::LinkPost(post, _) => Some(post.title().to_string()),
            _ => None,
        }
    }

    /*
     * Gets the link url of the reddit content.
     * This is only defined for link posts
     */
    pub fn link_url(&self) -> Option<String> {
        match self {
             &RedditContent::LinkPost(post, _) => Some(
                 post.link_url().expect("Failed to get link url of link post")),
             _ => None,
        }
    }

    /**
     * Gets the name of the author of this reddit content.
     */
    pub fn author(&self) -> String {
        match self {
            &RedditContent::PostComment(post, _) => post.author().name,
            &RedditContent::SelfPost(post, _) => post.author().name,
            &RedditContent::LinkPost(post, _) => post.author().name,
        }
    }

    /*
    * The type of reddit content as a string
    */
    pub fn content_type(&self) -> String {
        match self {
            &RedditContent::SelfPost(_, _) => String::from("self post"),
            &RedditContent::LinkPost(_, _) => String::from("link post"),
            &RedditContent::PostComment(_, _) => String::from("comment"),
        }
    }

    pub fn is_comment(&self) -> bool {
        match self {
            &RedditContent::PostComment(_, _) => true,
            _ => false,
        }
    }

    /*
     * Checks if the content has been replied to by the bot already,
     * either due to an existing bot's comment in reply or because
     * the bot has logged this reply in the database
     * (in case its comment was deleted)
     */
    pub fn replied_to(&self) -> LibResult<(bool)> {
        // create a copy of the content to fetch its replies as the copy will
        // be consumed
        let replies = match self {
            &RedditContent::PostComment(comment, _) => comment.clone().replies(),
            &RedditContent::SelfPost(post, _) => post.clone().replies(),
            &RedditContent::LinkPost(post, _) => post.clone().replies(),
        };
        // check database first as it doesn't use up rate limits
        Ok(
            self.config().database.replied(self.name())? ||
            replies?.any(|c| {
                c.author().name == self.config().authentication.username
            })
        )
    }

    /*
     * Replies to the comment and logs it in the database
     */
    pub fn reply(&self, reply: &str) -> LibResult<()> {
        println!("{} {}", Blue.paint("Replying:"), reply);
        self.commentable().reply(reply)?; // TODO check why this might have failed
        println!("{}", Green.paint("Replied, saving to db"));
        self.config().database.reply(self.name())?;
        Ok(())
    }

    fn commentable(&self) -> &'a Commentable {
        match self {
            &RedditContent::PostComment(comment, _) => comment,
            &RedditContent::SelfPost(post, _) => post,
            &RedditContent::LinkPost(post, _) => post,
        }
    }

    fn info(&self) -> &'a Content {
        match self {
            &RedditContent::PostComment(comment, _) => comment,
            &RedditContent::SelfPost(post, _) => post,
            &RedditContent::LinkPost(post, _) => post,
        }
    }

    fn config(&self) -> &'b Configuration {
        match self {
            &RedditContent::PostComment(_, config) => config,
            &RedditContent::SelfPost(_, config) => config,
            &RedditContent::LinkPost(_, config) => config,
        }
    }
}

// Since the crawler owns the Subreddit its lifetime is only tied to the config reference
pub struct SubredditCrawler<'c> {
    config: &'c Configuration,
    subreddit: Subreddit<'c>,
}

/*
 * Creates a crawler for this subreddit and configuration
 */
pub fn new_subreddit_crawler<'c>(
    subreddit: &str,
    config: &'c Configuration,
) -> SubredditCrawler<'c> {
    SubredditCrawler {
        config,
        subreddit: config.client.subreddit(subreddit)
    }
}

impl<'c> SubredditCrawler<'c> {
    /*
     * Runs the crawler with this behavior
     */
    pub fn run<F>(&self, behavior: &F) -> LibResult<()>
    where F: Fn(&RedditContent) -> LibResult<()> {
        self.search(behavior)
    }

    fn search<F>(&self, behavior: &F) -> LibResult<()>
    where F: Fn(&RedditContent) -> LibResult<()> {
        let about = self.subreddit.about();
        if about.is_ok() {
            println!("{} {} {}", Purple.paint("Subreddit"), self.subreddit.name,
                    about.expect("Failed to get subreddit about").display_name());
        } else {
            eprintln!("Could not fetch about data in {}", self.subreddit.name);
        }
        let hot = self.subreddit.hot(ListingOptions::default())?;
        for post in hot.take(7) {
            println!("Found '{}' in '{}'", post.title(), self.subreddit.name);
            println!();
            self.search_post(post, &behavior)?;
        }
        Ok(())
    }

    /*
     * Scans the post, possibly replying and then recurses on the post comments
     */
    fn search_post<F>(&self, post: Submission, behavior: &F) -> LibResult<()>
    where F: Fn(&RedditContent) -> LibResult<()> {
        // make a copy of the title to continue referring to after post is consumed
        let title = String::from(post.title()).clone();
        println!("Scanning '{}'", title);

        {
            let content = new_reddit_content_from_post(&post, self.config);
            if !content.replied_to()? {
                behavior(&content)?;
            }
            // take back the post
        }

        // give the post to `replies` which will consume it
        let comments = post.replies()?.take(100);
        for comment in comments {
            self.scan_comment_tree(comment, behavior)?;
        }
        Ok(())
    }

    fn scan_comment_tree<F>(&self, comment: Comment, behavior: &F) -> LibResult<()>
    where F: Fn(&RedditContent) -> LibResult<()> {
        {
            let content = new_reddit_content_from_comment(&comment, self.config);

            if !content.replied_to()? {
                behavior(&content)?;
            }
            // take back the comment
        }

        // consume the Comment to get its replies
        let replies = comment.replies()?;
        for reply in replies.take(10) {
            self.scan_comment_tree(reply, behavior)?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use ::*;

fn out<S: ToString>(screen : &mut RawTerminal<Stdout>, output : S) {
    write!(*screen, "{}", output.to_string()).unwrap();
}

fn curs(screen : &mut RawTerminal<Stdout>, coord : Coord) {
    // Coordinate translation assumes floor is being rendered at (1, 1)
    out(screen, termion::cursor::Goto(coord.col+1, coord.row+1));
}

fn color<C: Color>(screen : &mut RawTerminal<Stdout>, clr : C) {
    out(screen, Fg(clr));
}

pub fn render_loop(
    stop         : Arc<Mutex<bool>>,
    floor        : Arc<Mutex<Floor>>,
    dirty_coords : Arc<Mutex<Vec<Coord>>>,
    messages     : Arc<Mutex<Vec<String>>>,
    player       : Arc<Mutex<Player>>,
    monsters     : Arc<Mutex<Vec<Monster>>>,
) {
    let mut screen = &mut stdout().into_raw_mode().unwrap();
    out(screen, termion::cursor::Hide); // Hide the cursor
    out(screen, termion::clear::All);   // Clear the screen

    // Draw the entire floor
    curs(screen, Coord::new(0,0));
    {
        let floor = floor.lock().unwrap();
        for row in &floor.tiles {
            for tile in row {
                if let Some(ref wall) = tile.wall {
                    out(screen, wall);
                } else {
                    out(screen, " ");
                }
            }
            out(screen, "\r\n");
        }
    }

    // Render Loop
    loop {
        sleep(Duration::from_millis(10));
        {
            if *stop.lock().unwrap() {
                break;
            }
        }

        // Lock floor first...
        let floor = floor.lock().unwrap();

        // Redraw any dirty coordinates with floor tiles
        let mut dirty_coords = dirty_coords.lock().unwrap();
        for coord in dirty_coords.drain(..) {
            curs(screen, coord);
            out(screen, floor.get_symbol(&coord));
        }

        // Render Player
        {
            let mut player = player.lock().unwrap();
            if player.dirty {
                player.dirty = false;
                // Player's sword
                curs(screen, player.sword_coord);
                color(screen, Red);
                out(screen, sword_symbol(&player.facing));
                // Player himself
                curs(screen, player.coord);
                color(screen, Blue);
                out(screen, &player.symbol);
                color(screen, Reset);
            }
            // Player Score
            let score_string = format!("Score: {}", player.score);
            curs(screen, Coord::new((floor.cols - score_string.len()) as u16,
                                    floor.rows as u16));
            out(screen, score_string);
        }

        // Render Monsters
        {
            let monsters = monsters.lock().unwrap();
            for monster in monsters.iter() {
                curs(screen, monster.coord);
                color(screen, Green);
                out(screen, &monster.symbol);
                color(screen, Reset);
            }
        }

        // Game Title
        curs(screen, Coord::new(0, floor.rows as u16));
        out(screen, "Rusty Sword - Game of Infamy!");

        // Messages
        curs(screen, Coord::new(0, (floor.rows + 2) as u16));
        let mut messages = messages.lock().unwrap();
        if messages.len() > 4 {
            messages.remove(0);
        }
        for msg in messages.iter() {
            if msg.contains("spawned") {
                color(screen, Green);
            } else if msg.contains("You killed") {
                color(screen, Blue);
            } else if msg.contains("were eaten") {
                color(screen, Red);
            } else {
                color(screen, LightWhite);
            }
            out(screen, msg);
            out(screen, format!("{}\n\r", clear::UntilNewline));
            color(screen, Reset);
        }

        screen.flush().unwrap();
    }

    // Nice cleanup: Move cursor below the floor, so we can see how we finished
    {
        let floor = floor.lock().unwrap();
        curs(screen, Coord::new(0, (floor.rows+7) as u16));
    }
    out(screen, termion::cursor::Show); // Show the cursor again
    screen.flush().unwrap();
}

<commit_msg>Better coordinate translation explanation.<commit_after>use ::*;

fn out<S: ToString>(screen : &mut RawTerminal<Stdout>, output : S) {
    write!(*screen, "{}", output.to_string()).unwrap();
}

fn curs(screen : &mut RawTerminal<Stdout>, coord : Coord) {
    // (0, 0) in coord space maps to (1, 1) in terminal space
    out(screen, termion::cursor::Goto(coord.col+1, coord.row+1));
}

fn color<C: Color>(screen : &mut RawTerminal<Stdout>, clr : C) {
    out(screen, Fg(clr));
}

pub fn render_loop(
    stop         : Arc<Mutex<bool>>,
    floor        : Arc<Mutex<Floor>>,
    dirty_coords : Arc<Mutex<Vec<Coord>>>,
    messages     : Arc<Mutex<Vec<String>>>,
    player       : Arc<Mutex<Player>>,
    monsters     : Arc<Mutex<Vec<Monster>>>,
) {
    let mut screen = &mut stdout().into_raw_mode().unwrap();
    out(screen, termion::cursor::Hide); // Hide the cursor
    out(screen, termion::clear::All);   // Clear the screen

    // Draw the entire floor
    curs(screen, Coord::new(0,0));
    {
        let floor = floor.lock().unwrap();
        for row in &floor.tiles {
            for tile in row {
                if let Some(ref wall) = tile.wall {
                    out(screen, wall);
                } else {
                    out(screen, " ");
                }
            }
            out(screen, "\r\n");
        }
    }

    // Render Loop
    loop {
        sleep(Duration::from_millis(10));
        {
            if *stop.lock().unwrap() {
                break;
            }
        }

        // Lock floor first...
        let floor = floor.lock().unwrap();

        // Redraw any dirty coordinates with floor tiles
        let mut dirty_coords = dirty_coords.lock().unwrap();
        for coord in dirty_coords.drain(..) {
            curs(screen, coord);
            out(screen, floor.get_symbol(&coord));
        }

        // Render Player
        {
            let mut player = player.lock().unwrap();
            if player.dirty {
                player.dirty = false;
                // Player's sword
                curs(screen, player.sword_coord);
                color(screen, Red);
                out(screen, sword_symbol(&player.facing));
                // Player himself
                curs(screen, player.coord);
                color(screen, Blue);
                out(screen, &player.symbol);
                color(screen, Reset);
            }
            // Player Score
            let score_string = format!("Score: {}", player.score);
            curs(screen, Coord::new((floor.cols - score_string.len()) as u16,
                                    floor.rows as u16));
            out(screen, score_string);
        }

        // Render Monsters
        {
            let monsters = monsters.lock().unwrap();
            for monster in monsters.iter() {
                curs(screen, monster.coord);
                color(screen, Green);
                out(screen, &monster.symbol);
                color(screen, Reset);
            }
        }

        // Game Title
        curs(screen, Coord::new(0, floor.rows as u16));
        out(screen, "Rusty Sword - Game of Infamy!");

        // Messages
        curs(screen, Coord::new(0, (floor.rows + 2) as u16));
        let mut messages = messages.lock().unwrap();
        if messages.len() > 4 {
            messages.remove(0);
        }
        for msg in messages.iter() {
            if msg.contains("spawned") {
                color(screen, Green);
            } else if msg.contains("You killed") {
                color(screen, Blue);
            } else if msg.contains("were eaten") {
                color(screen, Red);
            } else {
                color(screen, LightWhite);
            }
            out(screen, msg);
            out(screen, format!("{}\n\r", clear::UntilNewline));
            color(screen, Reset);
        }

        screen.flush().unwrap();
    }

    // Nice cleanup: Move cursor below the floor, so we can see how we finished
    {
        let floor = floor.lock().unwrap();
        curs(screen, Coord::new(0, (floor.rows+7) as u16));
    }
    out(screen, termion::cursor::Show); // Show the cursor again
    screen.flush().unwrap();
}

<|endoftext|>


<commit_before>//!`Router` takes care of routing incoming requests to handlers.
//!
//!```rust
//!use rustful::router::Router;
//!# use std::hashmap::HashMap;
//!
//!# fn about_us(_: ~HashMap<~str, &str>) -> ~str {~""}
//!# fn show_user(_: ~HashMap<~str, &str>) -> ~str {~""}
//!# fn show_product(_: ~HashMap<~str, &str>) -> ~str {~""}
//!let routes = [
//!	("about", about_us),
//!	("user/:user", show_user),
//!	("product/:name", show_product)
//!];
//!
//!let router = Router::from_vec(routes);
//!```
use std::hashmap::HashMap;

///A handler function for routing.
pub type HandlerFn = fn(~HashMap<~str, &str>) -> ~str;

///Takes care of routing requests to handlers.
///
///Paths can be static (`"path/to/handler"`) or variable (`"users/:group/:user"`).
///Variables (starting with `:`) will match whatever word the path contains at
///that point and it will be sent as a value to the handler function.
pub struct Router {
	priv handler: Option<HandlerFn>,
	priv static_routes: HashMap<~str, ~Router>,
	priv variable_route: Option<~Router>,
	priv variable_names: ~[~str],
	priv wildcard_route: Option<~Router>
}

impl Router {
	///Creates an empty `Router`.
	pub fn new() -> Router {
		Router {
			handler: None,
			static_routes: HashMap::new(),
			variable_route: None,
			variable_names: ~[],
			wildcard_route: None
		}
	}

	///Generates a `Router` tree from a set of handlers and paths.
	pub fn from_vec(routes: &[(&str, HandlerFn)]) -> Router {
		let mut root = Router::new();

		for &(path, handler) in routes.iter() {
			root.insert_handler(path, handler);
		}

		root
	}

	///Inserts a handler into the `Router` at a given path.
	pub fn insert_handler(&mut self, path: &str, handler: HandlerFn) {
		self.insert_handler_vec(path.split('/').collect::<~[&str]>(), ~[], handler);
	}

	//Same as `insert_handler`, but internal
	fn insert_handler_vec(&mut self, path: &[&str], variable_names: ~[~str], handler: HandlerFn) {
		let mut var_names = variable_names;

		match path {
			[piece] => {
				let next = self.find_or_insert_router(piece);
				if piece.len() > 0 && piece.char_at(0) == ':' {
					var_names.push(piece.slice(1, piece.len()).to_owned());
				}
				next.variable_names = var_names;
				next.handler = Some(handler);
			},
			[piece, ..rest] => {
				let next = self.find_or_insert_router(piece);
				if piece.len() > 0 && piece.char_at(0) == ':' {
					var_names.push(piece.slice(1, piece.len()).to_owned());
				}
				next.insert_handler_vec(rest, var_names, handler);
			},
			[] => {
				self.variable_names = var_names;
				self.handler = Some(handler);
			}
		}
	}

	//Tries to find a router matching the key or inserts a new one if none exists
	fn find_or_insert_router<'a>(&'a mut self, key: &str) -> &'a mut ~Router {
		if key.len() > 0 && key.char_at(0) == ':' {
			if self.variable_route.is_none() {
				self.variable_route = Some(~Router::new());
			}
			self.variable_route.as_mut::<'a>().unwrap()
		} else {
			self.static_routes.find_or_insert_with::<'a>(key.to_owned(), |_| {
				~Router::new()
			})
		}
	}

	///Executes a matching handler function and returns the result.
	pub fn route(&self, path: &str) -> Option<~str> {
		self.find(path.split('/').collect::<~[&str]>(), &[])
	}

	//Tries to find a matching handler and run it
	fn find(&self, path: &[&str], variables: &[&str]) -> Option<~str> {
		match path {
			[piece] => {
				self.match_static(piece, variables, |next, vars| { next.exec(vars) })
			},
			[piece, ..rest] => {
				self.match_static(piece, variables, |next, vars| { next.find(rest, vars) })
			},
			[] => {
				self.exec(variables)
			}
		}
	}

	//Tries to run a handler with a given set of variable values
	fn exec(&self, variables: &[&str]) -> Option<~str> {
		match self.handler {
			Some(handler) => {
				let mut variable_map = ~HashMap::new();
				for (key, &value) in self.variable_names.iter().zip(variables.iter()) {
					variable_map.insert(key.to_owned(), value);
				}
				Some(handler(variable_map))
			},
			None => None
		}
	}

	//Checks for a static route. Runs `action` if found, runs `match_variable` otherwhise
	fn match_static(&self, key: &str, variables: &[&str], action: |&~Router, &[&str]| -> Option<~str>) -> Option<~str> {
		match self.static_routes.find(&key.to_owned()) {
			Some(next) => {
				match action(next, variables) {
					None => self.match_variable(key, variables, action),
					result => result
				}
			},
			None => {
				self.match_variable(key, variables, action)
			}
		}
	}

	//Checks for a variable route. Runs `action` if found, runs `match_wildcard` otherwhise
	fn match_variable(&self, key: &str, variables: &[&str], action: |&~Router, &[&str]| -> Option<~str>) -> Option<~str> {
		match self.variable_route {
			Some(ref next) => {
				let mut new_variables = variables.to_owned();
				new_variables.push(key.clone());
				action(next, new_variables)
			},
			None => None //TODO: Wildcard
		}
	}
}

#[cfg(test)]
mod test {
	use extra::test::BenchHarness;
	use std::hashmap::HashMap;
	use super::Router;

	fn test_1(_: ~HashMap<~str, &str>) -> ~str {
		~"test 1"
	}

	fn test_2(_: ~HashMap<~str, &str>) -> ~str {
		~"test 2"
	}

	fn test_3(_: ~HashMap<~str, &str>) -> ~str {
		~"test 3"
	}

	fn test_var(variables: ~HashMap<~str, &str>) -> ~str {
		let keys = ~[~"a", ~"b", ~"c"];
		keys.iter().filter_map(|key| {
			match variables.find(key) {
				Some(value) => Some(value.to_owned()),
				None => None
			}
		}).collect::<~[~str]>().connect(", ")
	}

	#[test]
	fn one_static_route() {
		let routes = [("path/to/test1", test_1)];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to"), None);
		assert_eq!(router.route("path/to/test1/nothing"), None);
	}

	#[test]
	fn several_static_routes() {
		let routes = [
			("path/to/test1", test_1),
			("path/to/test/no2", test_2),
			("path/to/test1/no/test3", test_3)
		];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to/test/no2"), Some(~"test 2"));
		assert_eq!(router.route("path/to/test1/no/test3"), Some(~"test 3"));
		assert_eq!(router.route("path/to/test1/no"), None);
	}

	#[test]
	fn one_variable_route() {
		let routes = [("path/:a/test1", test_var)];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"to"));
		assert_eq!(router.route("path/to"), None);
		assert_eq!(router.route("path/to/test1/nothing"), None);
	}

	#[test]
	fn several_variable_routes() {
		let routes = [
			("path/to/test1", test_var),
			("path/:a/test/no2", test_var),
			("path/to/:b/:c/:a", test_var)
		];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~""));
		assert_eq!(router.route("path/to/test/no2"), Some(~"to"));
		assert_eq!(router.route("path/to/test1/no/test3"), Some(~"test3, test1, no"));
		assert_eq!(router.route("path/to/test1/no"), None);
	}

	#[bench]
	fn search_speed(b: &mut BenchHarness) {
		let routes = [
			("path/to/test1", test_1),
			("path/to/test/no2", test_1),
			("path/to/test1/no/test3", test_1),
			("path/to/other/test1", test_1),
			("path/to/test/no2/again", test_1),
			("other/path/to/test1/no/test3", test_1),
			("path/to/test1", test_1),
			("path/:a/test/no2", test_1),
			("path/to/:b/:c/:a", test_1)
		];

		let paths = [
			"path/to/test1",
			"path/to/test/no2",
			"path/to/test1/no/test3",
			"path/to/other/test1",
			"path/to/test/no2/again",
			"other/path/to/test1/no/test3",
			"path/a/test1",
			"path/a/test/no2",
			"path/to/b/c/a",
			"path/to/test1/no",
			"path/to",
			"path/to/test1/nothing/at/all"
		];

		let router = Router::from_vec(routes);
		let mut counter = 0;

		b.iter(|| {
			router.route(paths[counter]);
			counter = (counter + 1) % paths.len()
		});
	}
}<commit_msg>Implemented wildcards in router paths<commit_after>//!`Router` takes care of routing incoming requests to handlers.
//!
//!```rust
//!# use rustful::router::Router;
//!# use std::hashmap::HashMap;
//!# fn about_us(_: ~HashMap<~str, &str>) -> ~str {~""}
//!# fn show_user(_: ~HashMap<~str, &str>) -> ~str {~""}
//!# fn show_product(_: ~HashMap<~str, &str>) -> ~str {~""}
//!# fn show_welcome(_: ~HashMap<~str, &str>) -> ~str {~""}
//!let routes = [
//!	("about", about_us),
//!	("user/:user", show_user),
//!	("product/:name", show_product),
//!	("*", show_welcome)
//!];
//!
//!let router = Router::from_vec(routes);
//!```
use std::hashmap::HashMap;

///A handler function for routing.
pub type HandlerFn = fn(~HashMap<~str, &str>) -> ~str;

///Takes care of routing requests to handlers.
///
///Paths can be static (`"path/to/handler"`) or variable (`"users/:group/:user"`)
///and contain wildcards (`"path/*/handler/*"`).
///
///Variables (starting with `:`) will match whatever word the request path contains at
///that point and it will be sent as a value to the handler function.
///
///Wildcards (a single `*`) will consume the segments until the rest of the
///path gives a match.
///
///```
///pattern = "a/*/b"
///"a/c/b" -> match
///"a/c/d/b" -> match
///"a/b" -> no match
///"a/c/b/d" -> no match
///```
///
///```
///pattern = "a/b/*"
///"a/b/c" -> match
///"a/b/c/d" -> match
///"a/b" -> no match
///```
pub struct Router {
	priv handler: Option<HandlerFn>,
	priv static_routes: HashMap<~str, ~Router>,
	priv variable_route: Option<~Router>,
	priv variable_names: ~[~str],
	priv wildcard_route: Option<~Router>
}

impl Router {
	///Creates an empty `Router`.
	pub fn new() -> Router {
		Router {
			handler: None,
			static_routes: HashMap::new(),
			variable_route: None,
			variable_names: ~[],
			wildcard_route: None
		}
	}

	///Generates a `Router` tree from a set of handlers and paths.
	pub fn from_vec(routes: &[(&str, HandlerFn)]) -> Router {
		let mut root = Router::new();

		for &(path, handler) in routes.iter() {
			root.insert_handler(path, handler);
		}

		root
	}

	///Inserts a handler into the `Router` at a given path.
	pub fn insert_handler(&mut self, path: &str, handler: HandlerFn) {
		self.insert_handler_vec(path.split('/').collect::<~[&str]>(), ~[], handler);
	}

	//Same as `insert_handler`, but internal
	fn insert_handler_vec(&mut self, path: &[&str], variable_names: ~[~str], handler: HandlerFn) {
		let mut var_names = variable_names;

		match path {
			[piece] => {
				let next = self.find_or_insert_router(piece);
				if piece.len() > 0 && piece.char_at(0) == ':' {
					var_names.push(piece.slice(1, piece.len()).to_owned());
				}
				next.variable_names = var_names;
				next.handler = Some(handler);
			},
			[piece, ..rest] => {
				let next = self.find_or_insert_router(piece);
				if piece.len() > 0 && piece.char_at(0) == ':' {
					var_names.push(piece.slice(1, piece.len()).to_owned());
				}
				next.insert_handler_vec(rest, var_names, handler);
			},
			[] => {
				self.variable_names = var_names;
				self.handler = Some(handler);
			}
		}
	}

	//Tries to find a router matching the key or inserts a new one if none exists
	fn find_or_insert_router<'a>(&'a mut self, key: &str) -> &'a mut ~Router {
		if key == "*" {
			if self.wildcard_route.is_none() {
				self.wildcard_route = Some(~Router::new());
			}
			self.wildcard_route.as_mut::<'a>().unwrap()
		} else if key.len() > 0 && key.char_at(0) == ':' {
			if self.variable_route.is_none() {
				self.variable_route = Some(~Router::new());
			}
			self.variable_route.as_mut::<'a>().unwrap()
		} else {
			self.static_routes.find_or_insert_with::<'a>(key.to_owned(), |_| {
				~Router::new()
			})
		}
	}

	///Executes a matching handler function and returns the result.
	pub fn route(&self, path: &str) -> Option<~str> {
		self.find(path.split('/').collect::<~[&str]>(), &[])
	}

	//Tries to find a matching handler and run it
	fn find(&self, path: &[&str], variables: &[&str]) -> Option<~str> {
		match path {
			[piece] => {
				self.match_static(piece, &[], variables, |next, _, vars| { next.exec(vars) })
			},
			[piece, ..rest] => {
				self.match_static(piece, rest, variables, |next, path, vars| { next.find(path, vars) })
			},
			[] => {
				self.exec(variables)
			}
		}
	}

	//Tries to run a handler with a given set of variable values
	fn exec(&self, variables: &[&str]) -> Option<~str> {
		match self.handler {
			Some(handler) => {
				let mut variable_map = ~HashMap::new();
				for (key, &value) in self.variable_names.iter().zip(variables.iter()) {
					variable_map.insert(key.to_owned(), value);
				}
				Some(handler(variable_map))
			},
			None => None
		}
	}

	//Checks for a static route. Runs `action` if found, runs `match_variable` otherwhise
	fn match_static(&self, key: &str, rest: &[&str], variables: &[&str], action: |&~Router, &[&str], &[&str]| -> Option<~str>) -> Option<~str> {
		match self.static_routes.find(&key.to_owned()) {
			Some(next) => {
				match action(next, rest, variables) {
					None => self.match_variable(key, rest, variables, action),
					result => result
				}
			},
			None => self.match_variable(key, rest, variables, action)
		}
	}

	//Checks for a variable route. Runs `action` if found, runs `match_wildcard` otherwhise
	fn match_variable(&self, key: &str, rest: &[&str], variables: &[&str], action: |&~Router, &[&str], &[&str]| -> Option<~str>) -> Option<~str> {
		match self.variable_route {
			Some(ref next) => {
				let mut new_variables = variables.to_owned();
				new_variables.push(key.clone());

				match action(next, rest, new_variables) {
					None => self.match_wildcard(rest, variables, action),
					result => result
				}
			},
			None => self.match_wildcard(rest, variables, action)
		}
	}

	//Checks for a wildcard route. Runs `action` if found, returns `None` otherwhise
	fn match_wildcard(&self, rest: &[&str], variables: &[&str], action: |&~Router, &[&str], &[&str]| -> Option<~str>) -> Option<~str> {
		match self.wildcard_route {
			Some(ref next) => {
				let mut path = rest;
				while path.len() > 0 {
					match action(next, path, variables) {
						None => path = path.slice(1, path.len()),
						result => return result
					}
				}

				action(next, path, variables)
			},
			None => None
		}
	}
}

#[cfg(test)]
mod test {
	use extra::test::BenchHarness;
	use std::hashmap::HashMap;
	use super::Router;

	fn test_1(_: ~HashMap<~str, &str>) -> ~str {
		~"test 1"
	}

	fn test_2(_: ~HashMap<~str, &str>) -> ~str {
		~"test 2"
	}

	fn test_3(_: ~HashMap<~str, &str>) -> ~str {
		~"test 3"
	}

	fn test_var(variables: ~HashMap<~str, &str>) -> ~str {
		let keys = ~[~"a", ~"b", ~"c"];
		keys.iter().filter_map(|key| {
			match variables.find(key) {
				Some(value) => Some(value.to_owned()),
				None => None
			}
		}).collect::<~[~str]>().connect(", ")
	}

	#[test]
	fn one_static_route() {
		let routes = [("path/to/test1", test_1)];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to"), None);
		assert_eq!(router.route("path/to/test1/nothing"), None);
	}

	#[test]
	fn several_static_routes() {
		let routes = [
			("path/to/test1", test_1),
			("path/to/test/no2", test_2),
			("path/to/test1/no/test3", test_3)
		];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to/test/no2"), Some(~"test 2"));
		assert_eq!(router.route("path/to/test1/no/test3"), Some(~"test 3"));
		assert_eq!(router.route("path/to/test1/no"), None);
	}

	#[test]
	fn one_variable_route() {
		let routes = [("path/:a/test1", test_var)];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"to"));
		assert_eq!(router.route("path/to"), None);
		assert_eq!(router.route("path/to/test1/nothing"), None);
	}

	#[test]
	fn several_variable_routes() {
		let routes = [
			("path/to/test1", test_var),
			("path/:a/test/no2", test_var),
			("path/to/:b/:c/:a", test_var)
		];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~""));
		assert_eq!(router.route("path/to/test/no2"), Some(~"to"));
		assert_eq!(router.route("path/to/test1/no/test3"), Some(~"test3, test1, no"));
		assert_eq!(router.route("path/to/test1/no"), None);
	}

	#[test]
	fn one_wildcard_end_route() {
		let routes = [("path/to/*", test_1)];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to/same/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to/the/same/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to"), None);
		assert_eq!(router.route("path"), None);
	}

	#[test]
	fn one_wildcard_middle_route() {
		let routes = [("path/*/test1", test_1)];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to/same/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to/the/same/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to"), None);
		assert_eq!(router.route("path"), None);
	}

	#[test]
	fn one_universal_wildcard_route() {
		let routes = [("*", test_1)];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to/same/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to/the/same/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/to"), Some(~"test 1"));
		assert_eq!(router.route("path"), Some(~"test 1"));
		assert_eq!(router.route(""), Some(~"test 1"));
	}

	#[test]
	fn several_wildcards_routes() {
		let routes = [
			("path/to/*", test_1),
			("path/*/test/no2", test_2),
			("path/to/*/*/*", test_3)
		];

		let router = Router::from_vec(routes);

		assert_eq!(router.route("path/to/test1"), Some(~"test 1"));
		assert_eq!(router.route("path/for/test/no2"), Some(~"test 2"));
		assert_eq!(router.route("path/to/test1/no/test3"), Some(~"test 3"));
		assert_eq!(router.route("path/to/test1/no/test3/again"), Some(~"test 3"));
		assert_eq!(router.route("path/to"), None);
	}

	#[bench]
	fn search_speed(b: &mut BenchHarness) {
		let routes = [
			("path/to/test1", test_1),
			("path/to/test/no2", test_1),
			("path/to/test1/no/test3", test_1),
			("path/to/other/test1", test_1),
			("path/to/test/no2/again", test_1),
			("other/path/to/test1/no/test3", test_1),
			("path/to/test1", test_1),
			("path/:a/test/no2", test_1),
			("path/to/:b/:c/:a", test_1),
			("path/to/*", test_1),
			("path/to/*/other", test_1)
		];

		let paths = [
			"path/to/test1",
			"path/to/test/no2",
			"path/to/test1/no/test3",
			"path/to/other/test1",
			"path/to/test/no2/again",
			"other/path/to/test1/no/test3",
			"path/a/test1",
			"path/a/test/no2",
			"path/to/b/c/a",
			"path/to/test1/no",
			"path/to",
			"path/to/test1/nothing/at/all"
		];

		let router = Router::from_vec(routes);
		let mut counter = 0;

		b.iter(|| {
			router.route(paths[counter]);
			counter = (counter + 1) % paths.len()
		});
	}

	#[bench]
	fn wildcard_speed(b: &mut BenchHarness) {
		let routes = [
			("*/to/*/*/a", test_1)
		];

		let paths = [
			"path/to/a",
			"path/to/test/a",
			"path/to/test1/no/a",
			"path/to/other/a",
			"path/to/test/no2/a",
			"other/path/to/test1/no/a",
			"path/a/a",
			"path/a/test/a",
			"path/to/b/c/a",
			"path/to/test1/a",
			"path/a",
			"path/to/test1/nothing/at/all/and/all/and/all/and/a"
		];

		let router = Router::from_vec(routes);
		let mut counter = 0;

		b.iter(|| {
			router.route(paths[counter]);
			counter = (counter + 1) % paths.len()
		});
	}
}<|endoftext|>


<commit_before>// We add this `extern crate` here to ensure that bindgen is up-to-date and
// rebuilt, even though we aren't using any of its types or functions here, only
// indirectly calling the executable.
#[allow(dead_code)]
extern crate bindgen;

use std::env;
use std::fs;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::process;

fn spawn_run_bindgen<P, Q, R>(run_bindgen: P, bindgen: Q, header: R) -> process::Child
    where P: AsRef<Path>,
          Q: AsRef<Path>,
          R: AsRef<Path>
{
    let run_bindgen = run_bindgen.as_ref();
    let bindgen = bindgen.as_ref();
    let header = header.as_ref();

    // Convert from "tests/headers/foo.hpp" to "tests/expectations/foo.rs" by
    // saving the filename, popping off "headers/foo.hpp", pushing
    // "expectations", pushing the saved filename, and finally modifying the
    // extension.

    let mut expected = PathBuf::from(header);
    let file_name = expected.file_name()
        .expect("Should have filename")
        .to_os_string();
    expected.pop();
    expected.pop();
    expected.push("expectations");
    expected.push(file_name);
    expected.set_extension("rs");

    let mut cmd = process::Command::new(run_bindgen);
    cmd.stdout(process::Stdio::piped())
        .stderr(process::Stdio::piped())
        .arg(bindgen)
        .arg(header)
        .arg(expected);

    if cfg!(feature = "llvm_stable") {
        cmd.arg("--feature")
            .arg("llvm_stable");
    }

    cmd.spawn()
        .expect("Should be able to spawn run-bindgen.py child process")
}

#[test]
fn run_bindgen_tests() {
    let crate_root = env::var("CARGO_MANIFEST_DIR")
        .expect("should have CARGO_MANIFEST_DIR environment variable");

    let mut run_bindgen = PathBuf::from(&crate_root);
    run_bindgen.push("tests");
    run_bindgen.push("tools");
    run_bindgen.push("run-bindgen.py");

    let mut bindgen = PathBuf::from(&crate_root);
    bindgen.push("target");
    bindgen.push("debug");
    bindgen.push("bindgen");
    if !bindgen.is_file() {
        panic!("{} is not a file! Build bindgen before running tests.",
               bindgen.display());
    }

    let mut headers_dir = PathBuf::from(&crate_root);
    headers_dir.push("tests");
    headers_dir.push("headers");

    let entries = fs::read_dir(&headers_dir)
        .expect("Should read directory")
        .map(|result| result.expect("Should read directory entry"));

    let tests = entries.filter(|entry| {
        match entry.path().extension().map(|s| s.to_str()) {
            Some(Some("h")) |
            Some(Some("hpp")) => true,
            _ => false,
        }
    });

    // Spawn one child at a time and wait on it as number of process
    // is the number of test files.

    let children = tests.map(|entry| {
        let child = spawn_run_bindgen(run_bindgen.clone(), bindgen.clone(), entry.path());
        (entry.path(), child)
    });

    let failures: Vec<_> = children
        .filter_map(|(path, mut child)| {
            let passed = child.wait()
                .expect("Should wait on child process")
                .success();

            if passed { None } else { Some((path, child)) }
        })
        .collect();

    let num_failures = failures.len();

    for (path, child) in failures {
        println!("FAIL: {}", path.display());

        let mut buf = String::new();

        child.stdout
            .expect("should have stdout piped")
            .read_to_string(&mut buf)
            .expect("should read child's stdout");
        for line in buf.lines() {
            println!("child stdout> {}", line);
        }

        child.stderr
            .expect("should have stderr piped")
            .read_to_string(&mut buf)
            .expect("should read child's stderr");
        for line in buf.lines() {
            println!("child stderr> {}", line);
        }
    }

    if num_failures > 0 {
        panic!("{} test failures!", num_failures);
    }
}
<commit_msg>Auto merge of #167 - jeanphilippeD:chunked_parallel_test, r=emilio<commit_after>// We add this `extern crate` here to ensure that bindgen is up-to-date and
// rebuilt, even though we aren't using any of its types or functions here, only
// indirectly calling the executable.
#[allow(dead_code)]
extern crate bindgen;

use std::env;
use std::fs;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::process;

const TEST_BATCH_DEFAULT_SIZE: usize = 16;

fn spawn_run_bindgen<P, Q, R>(run_bindgen: P, bindgen: Q, header: R) -> process::Child
    where P: AsRef<Path>,
          Q: AsRef<Path>,
          R: AsRef<Path>
{
    let run_bindgen = run_bindgen.as_ref();
    let bindgen = bindgen.as_ref();
    let header = header.as_ref();

    // Convert from "tests/headers/foo.hpp" to "tests/expectations/foo.rs" by
    // saving the filename, popping off "headers/foo.hpp", pushing
    // "expectations", pushing the saved filename, and finally modifying the
    // extension.

    let mut expected = PathBuf::from(header);
    let file_name = expected.file_name()
        .expect("Should have filename")
        .to_os_string();
    expected.pop();
    expected.pop();
    expected.push("expectations");
    expected.push(file_name);
    expected.set_extension("rs");

    let mut cmd = process::Command::new(run_bindgen);
    cmd.stdout(process::Stdio::piped())
        .stderr(process::Stdio::piped())
        .arg(bindgen)
        .arg(header)
        .arg(expected);

    if cfg!(feature = "llvm_stable") {
        cmd.arg("--feature")
            .arg("llvm_stable");
    }

    cmd.spawn()
        .expect("Should be able to spawn run-bindgen.py child process")
}

#[test]
fn run_bindgen_tests() {
    let crate_root = env::var("CARGO_MANIFEST_DIR")
        .expect("should have CARGO_MANIFEST_DIR environment variable");

    let mut run_bindgen = PathBuf::from(&crate_root);
    run_bindgen.push("tests");
    run_bindgen.push("tools");
    run_bindgen.push("run-bindgen.py");

    let mut bindgen = PathBuf::from(&crate_root);
    bindgen.push("target");
    bindgen.push("debug");
    bindgen.push("bindgen");
    if !bindgen.is_file() {
        panic!("{} is not a file! Build bindgen before running tests.",
               bindgen.display());
    }

    let mut headers_dir = PathBuf::from(&crate_root);
    headers_dir.push("tests");
    headers_dir.push("headers");

    let entries = fs::read_dir(&headers_dir)
        .expect("Should read directory")
        .map(|result| result.expect("Should read directory entry"));

    let tests = entries.filter(|entry| {
        match entry.path().extension().map(|s| s.to_str()) {
            Some(Some("h")) |
            Some(Some("hpp")) => true,
            _ => false,
        }
    }).collect::<Vec<_>>();

    let batch_size = env::var("BINDGEN_TEST_BATCH_SIZE")
        .ok()
        .and_then(|x| x.parse::<usize>().ok())
        .unwrap_or(TEST_BATCH_DEFAULT_SIZE);

    // Spawn batch_size child to run in parallel
    // and wait on all of them before processing the next batch

    let children = tests.chunks(batch_size).map(|x| {
        x.iter().map(|entry| {
            let child = spawn_run_bindgen(run_bindgen.clone(), bindgen.clone(), entry.path());
            (entry.path(), child)
        }).collect::<Vec<_>>()
    });

    let failures: Vec<_> = children.flat_map(|x| {
        x.into_iter().filter_map(|(path, mut child)| {
            let passed = child.wait()
                .expect("Should wait on child process")
                .success();

            if passed { None } else { Some((path, child)) }
        })
    })
    .collect();

    let num_failures = failures.len();

    for (path, child) in failures {
        println!("FAIL: {}", path.display());

        let mut buf = String::new();

        child.stdout
            .expect("should have stdout piped")
            .read_to_string(&mut buf)
            .expect("should read child's stdout");
        for line in buf.lines() {
            println!("child stdout> {}", line);
        }

        child.stderr
            .expect("should have stderr piped")
            .read_to_string(&mut buf)
            .expect("should read child's stderr");
        for line in buf.lines() {
            println!("child stderr> {}", line);
        }
    }

    if num_failures > 0 {
        panic!("{} test failures!", num_failures);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate sorted;

use sorted::*;

order_by_key!{ Key0AscOrder:
    fn (K: Ord + Copy, T)(entry: (K,T)) -> K { entry.0 }
}

order_by_key!{ KeySecondOrder:
    fn (K: Ord + Copy, T)(entry: (T,K)) -> K { entry.1 }
}

#[test]
fn sorted_array() {
    let arr = [7, 2, 9, 6];
    // Sort the array, resulting in a Sorted<[T;n], AscendingOrder>.
    let v = AscendingOrder::by_sorting(arr);
    // Retrieve a reference to the inner type with .as_inner.
    assert_eq!(v.as_inner(), &[2, 6, 7, 9]);
}

#[test]
fn ascending_order() {
    let arr = AscendingOrder::by_sorting([3, 4, 1, 2]);
    assert_eq!(arr.as_inner(), &[1, 2, 3, 4]);
}

#[test]
fn descending_order() {
    let arr = DescendingOrder::by_sorting([1, 3, 4, 2]);
    assert_eq!(arr.as_inner(), &[4, 3, 2, 1]);
}

#[test]
fn sorted_slice() {
    // You can also refer to a slice that should be sorted.
    // This leaves the ownership outside of the Sorted<> type.
    let mut arr = [3, 2, 4, 1];
    let s = AscendingOrder::by_sorting(&mut arr[..]);
    assert_eq!(s.as_inner(), &[1, 2, 3, 4]);
}

#[test]
fn sorted_vec() {
    let v = AscendingOrder::by_sorting(vec![4, 3, 1, 2]);
    assert_eq!(v.as_slice(), &[1, 2, 3, 4]);
}

#[test]
fn sort_by_first() {
    // It's easy to sort by a tuple key. Specifiy which key and in what order
    // it should be sorted.
    let s = vec![(5, 3), (2, 7), (3, 4)];
    let v = KeyOrder::<keys::Key0, AscendingOrder>::by_sorting(s);
    assert_eq!(&[(2, 7), (3, 4), (5, 3)], v.as_slice());
}

#[test]
fn sort_by_second() {
    let s = vec![(5, 3, 9), (2, 7, 2), (3, 4, 4)];
    let v = KeyOrder::<keys::Key2, DescendingOrder>::by_sorting(s);
    assert_eq!(&[(5, 3, 9), (3, 4, 4), (2, 7, 2)], v.as_slice());
}

#[test]
fn sort_with_property_key_type() {
    #[derive(Debug, Clone, Copy, PartialEq)]
    struct Record {
        stuff: i32,
        id: u32,
    };
    impl Record {
        pub fn new(n: u32) -> Self {
            Record { stuff: 0, id: n }
        }
    }

    // Sorting via an arbitrary property takes a bit more work.
    // First define a tag type to identify what property we want to sort by.
    #[derive(Debug, Clone, Copy)]
    struct IdKey;
    // Then implement how to get that key from our Record type.
    impl Key<Record> for IdKey {
        type Key = u32;
        fn key(r: &Record) -> Self::Key {
            r.id
        }
    }

    // You can now sort the Records by a property in any order.
    let v = KeyOrder::<IdKey, AscendingOrder>::by_sorting(vec![Record::new(2),
                                                               Record::new(3),
                                                               Record::new(1)]);
    assert_eq!(&v.as_slice(),
               &[Record::new(1), Record::new(2), Record::new(3)]);
}

#[test]
fn sort_by_property_string() {
    use std::cmp::Ordering;
    // Using strings as keys has some lifetime issues with the previous key
    // pattern. It's however possible, just not as flexible.
    #[derive(Debug, Clone, PartialEq)]
    struct Person {
        name: String,
        x: i32,
    };
    impl Person {
        pub fn new(n: &str) -> Self {
            Self {
                name: n.to_string(),
                x: 0,
            }
        }
    }

    // We'll implement our own SortOrder type. Starting with a tag type.
    #[derive(Debug, Clone, Copy)]
    struct OrderByName;
    // Then implement the SortOrder trait, per struct supporting it.
    impl SortOrder<Person> for OrderByName {
        fn cmp(a: &Person, b: &Person) -> Ordering {
            a.name.cmp(&b.name)
        }
    }

    // It will only provide one ordering for what you define, so if you need to
    // support multiple ways of ordering by name it will result in a bit of
    // boilerplate.
    let v = OrderByName::by_sorting(vec![Person::new("Bob"),
                                         Person::new("Cecil"),
                                         Person::new("Alice")]);
    assert_eq!(v.as_slice(),
               &[Person::new("Alice"),
                 Person::new("Bob"),
                 Person::new("Cecil")]);
}

#[test]
fn sorted_slice_from_sorted_vec() {
    // The as_ref works like Option::as_ref; it returns a Sorted type with the
    // inner type being a reference to the original. If the inner type implements
    // the correct AsRef, it'll work.
    fn take_sorted_slice<'a>(slice: Sorted<&'a [i32], AscendingOrder>) {
        assert_eq!(&[1, 2, 4, 9, 33][..], *slice);
    }
    let vec = AscendingOrder::by_sorting(vec![4, 9, 2, 33, 1]);
    take_sorted_slice(vec.as_ref());
}

#[test]
fn sorted_vec_ref() {
    fn take_sorted_vec<'a>(refvec: Sorted<&'a Vec<i32>, AscendingOrder>) {
        assert_eq!(&[1, 2, 3, 4], refvec.as_slice())
    }
    let vec = AscendingOrder::by_sorting(vec![3, 2, 4, 1]);
    take_sorted_vec(vec.as_ref());
}

#[test]
#[cfg(feature="unstable")]
fn sorted_vec_from_sorted_slice() {
    type SortedVec<T, O> = Sorted<Vec<T>, O>;
    let mut arr = [5, 3, 7, 9];
    let slice = AscendingOrder::by_sorting(&mut arr[..]);
    // This is currently unstable as it doesn't seem possible to guarantee sortedness.
    let vec = SortedVec::from(slice);
    assert_eq!([3, 5, 7, 9], vec.as_slice());
}

#[test]
fn take_sorted_iterator() {
    // Sorted types can generate SortedIterators.
    fn take_sorted<I>(sorted: I)
        where I: IntoIterator<Item = i32>,
              I::IntoIter: SortedIterator<Ordering = AscendingOrder>
    {
        let v: Vec<_> = sorted.into_iter().collect();
        assert_eq!(vec![2, 3, 8, 10], v);
    }
    let vec = AscendingOrder::by_sorting(vec![3, 8, 2, 10]);
    take_sorted(vec);
}

#[test]
fn take_sorted_ref_iterator() {
    // By-ref iterators can only be created via Sorted::iter() right now.
    // I.e there is no IntoIterator for &Sorted<>.
    fn take_sorted_ref<'a, I>(sorted: I)
        where I: IntoIterator<Item = &'a i32>,
              I::IntoIter: SortedIterator
    {
        let v: Vec<_> = sorted.into_iter().cloned().collect();
        assert_eq!([1, 2, 3, 4], v.as_slice());
    }
    let vec = AscendingOrder::by_sorting(vec![3, 4, 1, 2]);
    take_sorted_ref(vec.iter());
}

#[test]
fn sorted_insert() {
    // The Sorted type only provides mutable operations that keep the collection
    // sorted.
    let mut vec = AscendingOrder::by_sorting(vec![4, 8, 2, 0]);
    vec.insert(6);
    assert_eq!([0, 2, 4, 6, 8], vec.as_slice());
}

#[test]
fn sorted_vec_from_sorted_iterator() {
    // You can create Sorted collections from Sorted iterators.
    type SortedVec<T, O> = Sorted<Vec<T>, O>;
    let v0 = AscendingOrder::by_sorting(vec![3, 1, 4, 2]);
    let it = v0.into_iter();
    let v1 = SortedVec::from_iter(it);
    assert_eq!(&[1, 2, 3, 4], v1.as_slice());
}

#[test]
fn building_from_empty_vec() {
    let mut v: Sorted<Vec<i32>,AscendingOrder> = Default::default();
    v.insert(3);
    v.insert(1);
    v.insert(2);
    assert_eq!(&[1,2,3], v.as_slice());
}
<commit_msg>Corrected naming of test function in usage.rs.<commit_after>#[macro_use]
extern crate sorted;

use sorted::*;

order_by_key!{ Key0AscOrder:
    fn (K: Ord + Copy, T)(entry: (K,T)) -> K { entry.0 }
}

order_by_key!{ KeySecondOrder:
    fn (K: Ord + Copy, T)(entry: (T,K)) -> K { entry.1 }
}

#[test]
fn sorted_array() {
    let arr = [7, 2, 9, 6];
    // Sort the array, resulting in a Sorted<[T;n], AscendingOrder>.
    let v = AscendingOrder::by_sorting(arr);
    // Retrieve a reference to the inner type with .as_inner.
    assert_eq!(v.as_inner(), &[2, 6, 7, 9]);
}

#[test]
fn ascending_order() {
    let arr = AscendingOrder::by_sorting([3, 4, 1, 2]);
    assert_eq!(arr.as_inner(), &[1, 2, 3, 4]);
}

#[test]
fn descending_order() {
    let arr = DescendingOrder::by_sorting([1, 3, 4, 2]);
    assert_eq!(arr.as_inner(), &[4, 3, 2, 1]);
}

#[test]
fn sorted_slice() {
    // You can also refer to a slice that should be sorted.
    // This leaves the ownership outside of the Sorted<> type.
    let mut arr = [3, 2, 4, 1];
    let s = AscendingOrder::by_sorting(&mut arr[..]);
    assert_eq!(s.as_inner(), &[1, 2, 3, 4]);
}

#[test]
fn sorted_vec() {
    let v = AscendingOrder::by_sorting(vec![4, 3, 1, 2]);
    assert_eq!(v.as_slice(), &[1, 2, 3, 4]);
}

#[test]
fn sort_by_first() {
    // It's easy to sort by a tuple key. Specifiy which key and in what order
    // it should be sorted.
    let s = vec![(5, 3), (2, 7), (3, 4)];
    let v = KeyOrder::<keys::Key0, AscendingOrder>::by_sorting(s);
    assert_eq!(&[(2, 7), (3, 4), (5, 3)], v.as_slice());
}

#[test]
fn sort_by_third() {
    let s = vec![(5, 3, 9), (2, 7, 2), (3, 4, 4)];
    let v = KeyOrder::<keys::Key2, DescendingOrder>::by_sorting(s);
    assert_eq!(&[(5, 3, 9), (3, 4, 4), (2, 7, 2)], v.as_slice());
}

#[test]
fn sort_with_property_key_type() {
    #[derive(Debug, Clone, Copy, PartialEq)]
    struct Record {
        stuff: i32,
        id: u32,
    };
    impl Record {
        pub fn new(n: u32) -> Self {
            Record { stuff: 0, id: n }
        }
    }

    // Sorting via an arbitrary property takes a bit more work.
    // First define a tag type to identify what property we want to sort by.
    #[derive(Debug, Clone, Copy)]
    struct IdKey;
    // Then implement how to get that key from our Record type.
    impl Key<Record> for IdKey {
        type Key = u32;
        fn key(r: &Record) -> Self::Key {
            r.id
        }
    }

    // You can now sort the Records by a property in any order.
    let v = KeyOrder::<IdKey, AscendingOrder>::by_sorting(vec![Record::new(2),
                                                               Record::new(3),
                                                               Record::new(1)]);
    assert_eq!(&v.as_slice(),
               &[Record::new(1), Record::new(2), Record::new(3)]);
}

#[test]
fn sort_by_property_string() {
    use std::cmp::Ordering;
    // Using strings as keys has some lifetime issues with the previous key
    // pattern. It's however possible, just not as flexible.
    #[derive(Debug, Clone, PartialEq)]
    struct Person {
        name: String,
        x: i32,
    };
    impl Person {
        pub fn new(n: &str) -> Self {
            Self {
                name: n.to_string(),
                x: 0,
            }
        }
    }

    // We'll implement our own SortOrder type. Starting with a tag type.
    #[derive(Debug, Clone, Copy)]
    struct OrderByName;
    // Then implement the SortOrder trait, per struct supporting it.
    impl SortOrder<Person> for OrderByName {
        fn cmp(a: &Person, b: &Person) -> Ordering {
            a.name.cmp(&b.name)
        }
    }

    // It will only provide one ordering for what you define, so if you need to
    // support multiple ways of ordering by name it will result in a bit of
    // boilerplate.
    let v = OrderByName::by_sorting(vec![Person::new("Bob"),
                                         Person::new("Cecil"),
                                         Person::new("Alice")]);
    assert_eq!(v.as_slice(),
               &[Person::new("Alice"),
                 Person::new("Bob"),
                 Person::new("Cecil")]);
}

#[test]
fn sorted_slice_from_sorted_vec() {
    // The as_ref works like Option::as_ref; it returns a Sorted type with the
    // inner type being a reference to the original. If the inner type implements
    // the correct AsRef, it'll work.
    fn take_sorted_slice<'a>(slice: Sorted<&'a [i32], AscendingOrder>) {
        assert_eq!(&[1, 2, 4, 9, 33][..], *slice);
    }
    let vec = AscendingOrder::by_sorting(vec![4, 9, 2, 33, 1]);
    take_sorted_slice(vec.as_ref());
}

#[test]
fn sorted_vec_ref() {
    fn take_sorted_vec<'a>(refvec: Sorted<&'a Vec<i32>, AscendingOrder>) {
        assert_eq!(&[1, 2, 3, 4], refvec.as_slice())
    }
    let vec = AscendingOrder::by_sorting(vec![3, 2, 4, 1]);
    take_sorted_vec(vec.as_ref());
}

#[test]
#[cfg(feature="unstable")]
fn sorted_vec_from_sorted_slice() {
    type SortedVec<T, O> = Sorted<Vec<T>, O>;
    let mut arr = [5, 3, 7, 9];
    let slice = AscendingOrder::by_sorting(&mut arr[..]);
    // This is currently unstable as it doesn't seem possible to guarantee sortedness.
    let vec = SortedVec::from(slice);
    assert_eq!([3, 5, 7, 9], vec.as_slice());
}

#[test]
fn take_sorted_iterator() {
    // Sorted types can generate SortedIterators.
    fn take_sorted<I>(sorted: I)
        where I: IntoIterator<Item = i32>,
              I::IntoIter: SortedIterator<Ordering = AscendingOrder>
    {
        let v: Vec<_> = sorted.into_iter().collect();
        assert_eq!(vec![2, 3, 8, 10], v);
    }
    let vec = AscendingOrder::by_sorting(vec![3, 8, 2, 10]);
    take_sorted(vec);
}

#[test]
fn take_sorted_ref_iterator() {
    // By-ref iterators can only be created via Sorted::iter() right now.
    // I.e there is no IntoIterator for &Sorted<>.
    fn take_sorted_ref<'a, I>(sorted: I)
        where I: IntoIterator<Item = &'a i32>,
              I::IntoIter: SortedIterator
    {
        let v: Vec<_> = sorted.into_iter().cloned().collect();
        assert_eq!([1, 2, 3, 4], v.as_slice());
    }
    let vec = AscendingOrder::by_sorting(vec![3, 4, 1, 2]);
    take_sorted_ref(vec.iter());
}

#[test]
fn sorted_insert() {
    // The Sorted type only provides mutable operations that keep the collection
    // sorted.
    let mut vec = AscendingOrder::by_sorting(vec![4, 8, 2, 0]);
    vec.insert(6);
    assert_eq!([0, 2, 4, 6, 8], vec.as_slice());
}

#[test]
fn sorted_vec_from_sorted_iterator() {
    // You can create Sorted collections from Sorted iterators.
    type SortedVec<T, O> = Sorted<Vec<T>, O>;
    let v0 = AscendingOrder::by_sorting(vec![3, 1, 4, 2]);
    let it = v0.into_iter();
    let v1 = SortedVec::from_iter(it);
    assert_eq!(&[1, 2, 3, 4], v1.as_slice());
}

#[test]
fn building_from_empty_vec() {
    let mut v: Sorted<Vec<i32>,AscendingOrder> = Default::default();
    v.insert(3);
    v.insert(1);
    v.insert(2);
    assert_eq!(&[1,2,3], v.as_slice());
}
<|endoftext|>