<commit_before>//! Implements talking to the Github API

use bytes::buf::BufExt as _;
use hyper;
use hyper::{Body, Request, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json;
use snafu::{ResultExt, Snafu};
use url::Url;

use crate::HttpClient;

/// Used to talk to the Github API.
///
/// Can safely be cloned.
#[derive(Debug, Clone)]
pub struct GithubApi {
    pub http_client: HttpClient,
}

/// An error occured talking to Github.
#[derive(Debug, Snafu)]
pub enum HttpError {
    /// Failed to parse response as expected JSON object/
    #[snafu(display("Failed to parse JSON response from GitHub: {}", source))]
    DeserializeError { source: serde_json::Error },
    /// HTTP request failed/
    #[snafu(display("Failed to send request to GitHub: {}", source))]
    Http { source: hyper::Error },
    /// Got non-2xx response.
    #[snafu(display("Got non-200 response from GitHub: {}", code))]
    Status { code: StatusCode },
}

impl GithubApi {
    /// Exchange received OAuth code with Github.
    pub async fn exchange_oauth_code(
        &self,
        client_id: &str,
        client_secret: &str,
        code: &str,
    ) -> Result<GithubCallbackAuthResponse, HttpError> {
        let mut gh = Url::parse("https://github.com/login/oauth/access_token").unwrap();

        gh.query_pairs_mut()
            .append_pair("client_id", client_id)
            .append_pair("client_secret", client_secret)
            .append_pair("code", code);

        let req = Request::post(gh.to_string()).header(hyper::header::ACCEPT, "application/json");

        let resp = self
            .http_client
            .request(req.body(Body::empty()).unwrap())
            .await
            .map_err(|e| HttpError::Http { source: e })?;

        Ok(parse_resp_as_json(resp).await?)
    }

    /// Given a user access token from Github get the user's Github ID and
    /// display name.
    pub async fn get_authenticated_user(
        &self,
        token: &str,
    ) -> Result<GithubUserResponse, HttpError> {
        let url = "https://api.github.com/user";

        let req = Request::get(url)
            .header(hyper::header::ACCEPT, "application/json")
            .header(hyper::header::USER_AGENT, "rust shaft")
            .header(hyper::header::AUTHORIZATION, format!("token {}", token));

        let resp = self
            .http_client
            .request(req.body(Body::empty()).unwrap())
            .await
            .map_err(|e| HttpError::Http { source: e })?;

        Ok(parse_resp_as_json(resp).await?)
    }

    /// Check if the Github user with given access token is a member of the org.
    pub async fn get_if_member_of_org(
        &self,
        token: &str,
        org: &str,
    ) -> Result<Option<GithubOrganizationMembership>, HttpError> {
        let url = format!("https://api.github.com/user/memberships/orgs/{}", org);

        let req = Request::get(url)
            .header(hyper::header::ACCEPT, "application/json")
            .header(hyper::header::USER_AGENT, "rust shaft")
            .header(hyper::header::AUTHORIZATION, format!("token {}", token));

        let resp = self
            .http_client
            .request(req.body(Body::empty()).unwrap())
            .await
            .map_err(|e| HttpError::Http { source: e })?;

        match parse_resp_as_json(resp).await {
            Ok(r) => Ok(Some(r)),
            Err(HttpError::Status { code }) if code == StatusCode::FORBIDDEN => Ok(None),
            Err(err) => Err(err),
        }
    }
}

/// Parse HTTP response into JSON object.
async fn parse_resp_as_json<C>(resp: hyper::Response<Body>) -> Result<C, HttpError>
where
    C: DeserializeOwned + 'static,
{
    if !resp.status().is_success() {
        return Err(HttpError::Status {
            code: resp.status(),
        });
    }

    let body = hyper::body::aggregate(resp)
        .await
        .map_err(|e| HttpError::Http { source: e })?;

    let res = serde_json::from_reader(body.reader()).context(DeserializeError)?;

    Ok(res)
}

/// Github API response to `/login/oauth/access_token`
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GithubCallbackAuthResponse {
    /// An access token for the user we're authed against.
    pub access_token: String,
    /// The permissions scope the token has.
    pub scope: String,
}

/// Github API repsonse to `/user`
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GithubUserResponse {
    /// The user's Github login ID
    pub login: String,
    /// The user's Github display name (if any)
    pub name: Option<String>,
}

/// Github API response to `/user/memberships/orgs/{org}`
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GithubOrganizationMembership {
    /// The user's membership state in the org
    state: String,
    /// The user's role in the org
    role: String,
}
<commit_msg>Add a generic http client for testing<commit_after>//! Implements talking to the Github API

use bytes::buf::BufExt as _;
use hyper;
use hyper::{Body, Request, Response, StatusCode};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json;
use snafu::{ResultExt, Snafu};
use url::Url;

use std::fmt::Debug;
use std::future::Future;

use crate::HttpClient;

pub trait GenericHttpClient: Debug + Clone {
    type ResponseFuture: Future<Output = hyper::Result<Response<hyper::Body>>>;

    fn request(&self, request: Request<hyper::Body>) -> Self::ResponseFuture;
}

impl GenericHttpClient for HttpClient {
    type ResponseFuture = hyper::client::ResponseFuture;

    fn request(&self, request: Request<hyper::Body>) -> Self::ResponseFuture {
        self.request(request)
    }
}

/// Used to talk to the Github API.
///
/// Can safely be cloned.
#[derive(Debug, Clone)]
pub struct GithubApi<G: GenericHttpClient> {
    pub http_client: G,
}

/// An error occured talking to Github.
#[derive(Debug, Snafu)]
pub enum HttpError {
    /// Failed to parse response as expected JSON object/
    #[snafu(display("Failed to parse JSON response from GitHub: {}", source))]
    DeserializeError { source: serde_json::Error },
    /// HTTP request failed/
    #[snafu(display("Failed to send request to GitHub: {}", source))]
    Http { source: hyper::Error },
    /// Got non-2xx response.
    #[snafu(display("Got non-200 response from GitHub: {}", code))]
    Status { code: StatusCode },
}

impl<G> GithubApi<G>
where
    G: GenericHttpClient,
{
    /// Exchange received OAuth code with Github.
    pub async fn exchange_oauth_code(
        &self,
        client_id: &str,
        client_secret: &str,
        code: &str,
    ) -> Result<GithubCallbackAuthResponse, HttpError> {
        let mut gh = Url::parse("https://github.com/login/oauth/access_token").unwrap();

        gh.query_pairs_mut()
            .append_pair("client_id", client_id)
            .append_pair("client_secret", client_secret)
            .append_pair("code", code);

        let req = Request::post(gh.to_string()).header(hyper::header::ACCEPT, "application/json");

        let resp = self
            .http_client
            .request(req.body(Body::empty()).unwrap())
            .await
            .map_err(|e| HttpError::Http { source: e })?;

        Ok(parse_resp_as_json(resp).await?)
    }

    /// Given a user access token from Github get the user's Github ID and
    /// display name.
    pub async fn get_authenticated_user(
        &self,
        token: &str,
    ) -> Result<GithubUserResponse, HttpError> {
        let url = "https://api.github.com/user";

        let req = Request::get(url)
            .header(hyper::header::ACCEPT, "application/json")
            .header(hyper::header::USER_AGENT, "rust shaft")
            .header(hyper::header::AUTHORIZATION, format!("token {}", token));

        let resp = self
            .http_client
            .request(req.body(Body::empty()).unwrap())
            .await
            .map_err(|e| HttpError::Http { source: e })?;

        Ok(parse_resp_as_json(resp).await?)
    }

    /// Check if the Github user with given access token is a member of the org.
    pub async fn get_if_member_of_org(
        &self,
        token: &str,
        org: &str,
    ) -> Result<Option<GithubOrganizationMembership>, HttpError> {
        let url = format!("https://api.github.com/user/memberships/orgs/{}", org);

        let req = Request::get(url)
            .header(hyper::header::ACCEPT, "application/json")
            .header(hyper::header::USER_AGENT, "rust shaft")
            .header(hyper::header::AUTHORIZATION, format!("token {}", token));

        let resp = self
            .http_client
            .request(req.body(Body::empty()).unwrap())
            .await
            .map_err(|e| HttpError::Http { source: e })?;

        match parse_resp_as_json(resp).await {
            Ok(r) => Ok(Some(r)),
            Err(HttpError::Status { code }) if code == StatusCode::FORBIDDEN => Ok(None),
            Err(err) => Err(err),
        }
    }
}

/// Parse HTTP response into JSON object.
async fn parse_resp_as_json<C>(resp: hyper::Response<Body>) -> Result<C, HttpError>
where
    C: DeserializeOwned + 'static,
{
    if !resp.status().is_success() {
        return Err(HttpError::Status {
            code: resp.status(),
        });
    }

    let body = hyper::body::aggregate(resp)
        .await
        .map_err(|e| HttpError::Http { source: e })?;

    let res = serde_json::from_reader(body.reader()).context(DeserializeError)?;

    Ok(res)
}

/// Github API response to `/login/oauth/access_token`
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GithubCallbackAuthResponse {
    /// An access token for the user we're authed against.
    pub access_token: String,
    /// The permissions scope the token has.
    pub scope: String,
}

/// Github API repsonse to `/user`
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GithubUserResponse {
    /// The user's Github login ID
    pub login: String,
    /// The user's Github display name (if any)
    pub name: Option<String>,
}

/// Github API response to `/user/memberships/orgs/{org}`
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GithubOrganizationMembership {
    /// The user's membership state in the org
    state: String,
    /// The user's role in the org
    role: String,
}
<|endoftext|>


<commit_before>use renderer::html_handlebars::helpers;
use renderer::Renderer;
use book::MDBook;
use book::bookitem::BookItem;
use {utils, theme};
use regex::{Regex, Captures};

use std::ascii::AsciiExt;
use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::error::Error;
use std::io::{self, Read};
use std::collections::BTreeMap;

use handlebars::Handlebars;

use serde_json;

#[derive(Default)]
pub struct HtmlHandlebars;

impl HtmlHandlebars {
    pub fn new() -> Self {
        HtmlHandlebars
    }
}

impl Renderer for HtmlHandlebars {
    fn render(&self, book: &MDBook) -> Result<(), Box<Error>> {
        debug!("[fn]: render");
        let mut handlebars = Handlebars::new();

        // Load theme
        let theme = theme::Theme::new(book.get_theme_path());

        // Register template
        debug!("[*]: Register handlebars template");
        try!(handlebars.register_template_string("index", try!(String::from_utf8(theme.index))));

        // Register helpers
        debug!("[*]: Register handlebars helpers");
        handlebars.register_helper("toc", Box::new(helpers::toc::RenderToc));
        handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
        handlebars.register_helper("next", Box::new(helpers::navigation::next));

        let mut data = try!(make_data(book));

        // Print version
        let mut print_content: String = String::new();

        // Check if dest directory exists
        debug!("[*]: Check if destination directory exists");
        if fs::create_dir_all(book.get_dest()).is_err() {
            return Err(Box::new(io::Error::new(io::ErrorKind::Other,
                                               "Unexpected error when constructing destination path")));
        }

        // Render a file for every entry in the book
        let mut index = true;
        for item in book.iter() {

            match *item {
                BookItem::Chapter(_, ref ch) |
                BookItem::Affix(ref ch) => {
                    if ch.path != PathBuf::new() {

                        let path = book.get_src().join(&ch.path);

                        debug!("[*]: Opening file: {:?}", path);
                        let mut f = try!(File::open(&path));
                        let mut content: String = String::new();

                        debug!("[*]: Reading file");
                        try!(f.read_to_string(&mut content));

                        // Parse for playpen links
                        if let Some(p) = path.parent() {
                            content = helpers::playpen::render_playpen(&content, p);
                        }

                        // Render markdown using the pulldown-cmark crate
                        content = utils::render_markdown(&content);
                        print_content.push_str(&content);

                        // Update the context with data for this file
                        let path = ch.path.to_str().ok_or_else(||
                            io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
                        data.insert("path".to_owned(), json!(path));
                        data.insert("content".to_owned(), json!(content));
                        data.insert("chapter_title".to_owned(), json!(ch.name));
                        data.insert("path_to_root".to_owned(), json!(utils::fs::path_to_root(&ch.path)));

                        // Render the handlebars template with the data
                        debug!("[*]: Render template");
                        let rendered = try!(handlebars.render("index", &data));

                        let filename = Path::new(&ch.path).with_extension("html");

                        // Do several kinds of post-processing
                        let rendered = build_header_links(rendered, filename.to_str().unwrap_or(""));
                        let rendered = fix_anchor_links(rendered, filename.to_str().unwrap_or(""));
                        let rendered = fix_code_blocks(rendered);
                        let rendered = add_playpen_pre(rendered);

                        // Write to file
                        info!("[*] Creating {:?} ✓", filename.display());
                        try!(book.write_file(filename, &rendered.into_bytes()));

                        // Create an index.html from the first element in SUMMARY.md
                        if index {
                            debug!("[*]: index.html");

                            let mut content = String::new();
                            let _source = try!(File::open(book.get_dest().join(&ch.path.with_extension("html"))))
                                .read_to_string(&mut content);

                            // This could cause a problem when someone displays code containing <base href=...>
                            // on the front page, however this case should be very very rare...
                            content = content.lines()
                                .filter(|line| !line.contains("<base href="))
                                .collect::<Vec<&str>>()
                                .join("\n");

                            try!(book.write_file("index.html", content.as_bytes()));

                            info!("[*] Creating index.html from {:?} ✓",
                                  book.get_dest().join(&ch.path.with_extension("html")));
                            index = false;
                        }
                    }
                },
                _ => {},
            }
        }

        // Print version

        // Update the context with data for this file
        data.insert("path".to_owned(), json!("print.md"));
        data.insert("content".to_owned(), json!(print_content));
        data.insert("path_to_root".to_owned(), json!(utils::fs::path_to_root(Path::new("print.md"))));

        // Render the handlebars template with the data
        debug!("[*]: Render template");

        let rendered = try!(handlebars.render("index", &data));

        // do several kinds of post-processing
        let rendered = build_header_links(rendered, "print.html");
        let rendered = fix_anchor_links(rendered, "print.html");
        let rendered = fix_code_blocks(rendered);
        let rendered = add_playpen_pre(rendered);

        try!(book.write_file(Path::new("print").with_extension("html"), &rendered.into_bytes()));
        info!("[*] Creating print.html ✓");

        // Copy static files (js, css, images, ...)

        debug!("[*] Copy static files");
        try!(book.write_file("book.js", &theme.js));
        try!(book.write_file("book.css", &theme.css));
        try!(book.write_file("favicon.png", &theme.favicon));
        try!(book.write_file("jquery.js", &theme.jquery));
        try!(book.write_file("highlight.css", &theme.highlight_css));
        try!(book.write_file("tomorrow-night.css", &theme.tomorrow_night_css));
        try!(book.write_file("highlight.js", &theme.highlight_js));
        try!(book.write_file("_FontAwesome/css/font-awesome.css", theme::FONT_AWESOME));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", theme::FONT_AWESOME_EOT));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", theme::FONT_AWESOME_SVG));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", theme::FONT_AWESOME_TTF));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", theme::FONT_AWESOME_WOFF));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2));
        try!(book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF));

        // Copy all remaining files
        try!(utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"]));

        Ok(())
    }
}

fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>, Box<Error>> {
    debug!("[fn]: make_data");

    let mut data = serde_json::Map::new();
    data.insert("language".to_owned(), json!("en"));
    data.insert("title".to_owned(), json!(book.get_title()));
    data.insert("description".to_owned(), json!(book.get_description()));
    data.insert("favicon".to_owned(), json!("favicon.png"));
    if let Some(livereload) = book.get_livereload() {
        data.insert("livereload".to_owned(), json!(livereload));
    }

    let mut chapters = vec![];

    for item in book.iter() {
        // Create the data to inject in the template
        let mut chapter = BTreeMap::new();

        match *item {
            BookItem::Affix(ref ch) => {
                chapter.insert("name".to_owned(), json!(ch.name));
                let path = ch.path.to_str().ok_or_else(||
                    io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
                chapter.insert("path".to_owned(), json!(path));
            },
            BookItem::Chapter(ref s, ref ch) => {
                chapter.insert("section".to_owned(), json!(s));
                chapter.insert("name".to_owned(), json!(ch.name));
                let path = ch.path.to_str().ok_or_else(||
                    io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
                chapter.insert("path".to_owned(), json!(path));
            },
            BookItem::Spacer => {
                chapter.insert("spacer".to_owned(), json!("_spacer_"));
            },

        }

        chapters.push(chapter);
    }

    data.insert("chapters".to_owned(), json!(chapters));

    debug!("[*]: JSON constructed");
    Ok(data)
}

fn build_header_links(html: String, filename: &str) -> String {
    let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();

    regex.replace_all(&html, |caps: &Captures| {
        let level = &caps[1];
        let text = &caps[2];
        let mut id = text.to_string();
        let repl_sub = vec!["", "", "", "",
                            "", "",
                            "<", ">", "&", "'", """];
        for sub in repl_sub {
            id = id.replace(sub, "");
        }
        let id = id.chars().filter_map(|c| {
            if c.is_alphanumeric() || c == '-' || c == '_' {
                if c.is_ascii() {
                    Some(c.to_ascii_lowercase())
                } else {
                    Some(c)
                }
            } else if c.is_whitespace() && c.is_ascii() {
                Some('-')
            } else {
                None
            }
        }).collect::<String>();

        format!("<h{level}>{text}</h{level}>",
            level=level, id=id, text=text, filename=filename)
    }).into_owned()
}

// anchors to the same page (href="#anchor") do not work because of
// <base href="../"> pointing to the root folder. This function *fixes*
// that in a very inelegant way
fn fix_anchor_links(html: String, filename: &str) -> String {
    let regex = Regex::new(r##"]+)href="#([^"]+)"([^>]*)>"##).unwrap();
    regex.replace_all(&html, |caps: &Captures| {
        let before = &caps[1];
        let anchor = &caps[2];
        let after = &caps[3];

        format!("",
            before=before, filename=filename, anchor=anchor, after=after)
    }).into_owned()
}

// The rust book uses annotations for rustdoc to test code snippets, like the following:
// ```rust,should_panic
// fn main() {
//     // Code here
// }
// ```
// This function replaces all commas by spaces in the code block classes
fn fix_code_blocks(html: String) -> String {
    let regex = Regex::new(r##"]+)class="([^"]+)"([^>]*)>"##).unwrap();
    regex.replace_all(&html, |caps: &Captures| {
        let before = &caps[1];
        let classes = &caps[2].replace(",", " ");
        let after = &caps[3];

        format!("", before=before, classes=classes, after=after)
    }).into_owned()
}

fn add_playpen_pre(html: String) -> String {
    let regex = Regex::new(r##"((?s)]?class="([^"]+)".*?>.*?)"##).unwrap();
    regex.replace_all(&html, |caps: &Captures| {
        let text = &caps[1];
        let classes = &caps[2];

        if classes.contains("language-rust") && !classes.contains("ignore") {
            // wrap the contents in an external pre block
            format!("
{}
", text)
        } else {
            // not language-rust, so no-op
            format!("{}", text)
        }
    }).into_owned()
}
<commit_msg>inject main<commit_after>use renderer::html_handlebars::helpers;
use renderer::Renderer;
use book::MDBook;
use book::bookitem::BookItem;
use {utils, theme};
use regex::{Regex, Captures};

use std::ascii::AsciiExt;
use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::error::Error;
use std::io::{self, Read};
use std::collections::BTreeMap;

use handlebars::Handlebars;

use serde_json;

#[derive(Default)]
pub struct HtmlHandlebars;

impl HtmlHandlebars {
    pub fn new() -> Self {
        HtmlHandlebars
    }
}

impl Renderer for HtmlHandlebars {
    fn render(&self, book: &MDBook) -> Result<(), Box<Error>> {
        debug!("[fn]: render");
        let mut handlebars = Handlebars::new();

        // Load theme
        let theme = theme::Theme::new(book.get_theme_path());

        // Register template
        debug!("[*]: Register handlebars template");
        try!(handlebars.register_template_string("index", try!(String::from_utf8(theme.index))));

        // Register helpers
        debug!("[*]: Register handlebars helpers");
        handlebars.register_helper("toc", Box::new(helpers::toc::RenderToc));
        handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
        handlebars.register_helper("next", Box::new(helpers::navigation::next));

        let mut data = try!(make_data(book));

        // Print version
        let mut print_content: String = String::new();

        // Check if dest directory exists
        debug!("[*]: Check if destination directory exists");
        if fs::create_dir_all(book.get_dest()).is_err() {
            return Err(Box::new(io::Error::new(io::ErrorKind::Other,
                                               "Unexpected error when constructing destination path")));
        }

        // Render a file for every entry in the book
        let mut index = true;
        for item in book.iter() {

            match *item {
                BookItem::Chapter(_, ref ch) |
                BookItem::Affix(ref ch) => {
                    if ch.path != PathBuf::new() {

                        let path = book.get_src().join(&ch.path);

                        debug!("[*]: Opening file: {:?}", path);
                        let mut f = try!(File::open(&path));
                        let mut content: String = String::new();

                        debug!("[*]: Reading file");
                        try!(f.read_to_string(&mut content));

                        // Parse for playpen links
                        if let Some(p) = path.parent() {
                            content = helpers::playpen::render_playpen(&content, p);
                        }

                        // Render markdown using the pulldown-cmark crate
                        content = utils::render_markdown(&content);
                        print_content.push_str(&content);

                        // Update the context with data for this file
                        let path = ch.path.to_str().ok_or_else(||
                            io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
                        data.insert("path".to_owned(), json!(path));
                        data.insert("content".to_owned(), json!(content));
                        data.insert("chapter_title".to_owned(), json!(ch.name));
                        data.insert("path_to_root".to_owned(), json!(utils::fs::path_to_root(&ch.path)));

                        // Render the handlebars template with the data
                        debug!("[*]: Render template");
                        let rendered = try!(handlebars.render("index", &data));

                        let filename = Path::new(&ch.path).with_extension("html");

                        // Do several kinds of post-processing
                        let rendered = build_header_links(rendered, filename.to_str().unwrap_or(""));
                        let rendered = fix_anchor_links(rendered, filename.to_str().unwrap_or(""));
                        let rendered = fix_code_blocks(rendered);
                        let rendered = add_playpen_pre(rendered);

                        // Write to file
                        info!("[*] Creating {:?} ✓", filename.display());
                        try!(book.write_file(filename, &rendered.into_bytes()));

                        // Create an index.html from the first element in SUMMARY.md
                        if index {
                            debug!("[*]: index.html");

                            let mut content = String::new();
                            let _source = try!(File::open(book.get_dest().join(&ch.path.with_extension("html"))))
                                .read_to_string(&mut content);

                            // This could cause a problem when someone displays code containing <base href=...>
                            // on the front page, however this case should be very very rare...
                            content = content.lines()
                                .filter(|line| !line.contains("<base href="))
                                .collect::<Vec<&str>>()
                                .join("\n");

                            try!(book.write_file("index.html", content.as_bytes()));

                            info!("[*] Creating index.html from {:?} ✓",
                                  book.get_dest().join(&ch.path.with_extension("html")));
                            index = false;
                        }
                    }
                },
                _ => {},
            }
        }

        // Print version

        // Update the context with data for this file
        data.insert("path".to_owned(), json!("print.md"));
        data.insert("content".to_owned(), json!(print_content));
        data.insert("path_to_root".to_owned(), json!(utils::fs::path_to_root(Path::new("print.md"))));

        // Render the handlebars template with the data
        debug!("[*]: Render template");

        let rendered = try!(handlebars.render("index", &data));

        // do several kinds of post-processing
        let rendered = build_header_links(rendered, "print.html");
        let rendered = fix_anchor_links(rendered, "print.html");
        let rendered = fix_code_blocks(rendered);
        let rendered = add_playpen_pre(rendered);

        try!(book.write_file(Path::new("print").with_extension("html"), &rendered.into_bytes()));
        info!("[*] Creating print.html ✓");

        // Copy static files (js, css, images, ...)

        debug!("[*] Copy static files");
        try!(book.write_file("book.js", &theme.js));
        try!(book.write_file("book.css", &theme.css));
        try!(book.write_file("favicon.png", &theme.favicon));
        try!(book.write_file("jquery.js", &theme.jquery));
        try!(book.write_file("highlight.css", &theme.highlight_css));
        try!(book.write_file("tomorrow-night.css", &theme.tomorrow_night_css));
        try!(book.write_file("highlight.js", &theme.highlight_js));
        try!(book.write_file("_FontAwesome/css/font-awesome.css", theme::FONT_AWESOME));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", theme::FONT_AWESOME_EOT));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", theme::FONT_AWESOME_SVG));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", theme::FONT_AWESOME_TTF));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", theme::FONT_AWESOME_WOFF));
        try!(book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2));
        try!(book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF));

        // Copy all remaining files
        try!(utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"]));

        Ok(())
    }
}

fn make_data(book: &MDBook) -> Result<serde_json::Map<String, serde_json::Value>, Box<Error>> {
    debug!("[fn]: make_data");

    let mut data = serde_json::Map::new();
    data.insert("language".to_owned(), json!("en"));
    data.insert("title".to_owned(), json!(book.get_title()));
    data.insert("description".to_owned(), json!(book.get_description()));
    data.insert("favicon".to_owned(), json!("favicon.png"));
    if let Some(livereload) = book.get_livereload() {
        data.insert("livereload".to_owned(), json!(livereload));
    }

    let mut chapters = vec![];

    for item in book.iter() {
        // Create the data to inject in the template
        let mut chapter = BTreeMap::new();

        match *item {
            BookItem::Affix(ref ch) => {
                chapter.insert("name".to_owned(), json!(ch.name));
                let path = ch.path.to_str().ok_or_else(||
                    io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
                chapter.insert("path".to_owned(), json!(path));
            },
            BookItem::Chapter(ref s, ref ch) => {
                chapter.insert("section".to_owned(), json!(s));
                chapter.insert("name".to_owned(), json!(ch.name));
                let path = ch.path.to_str().ok_or_else(||
                    io::Error::new(io::ErrorKind::Other, "Could not convert path to str"))?;
                chapter.insert("path".to_owned(), json!(path));
            },
            BookItem::Spacer => {
                chapter.insert("spacer".to_owned(), json!("_spacer_"));
            },

        }

        chapters.push(chapter);
    }

    data.insert("chapters".to_owned(), json!(chapters));

    debug!("[*]: JSON constructed");
    Ok(data)
}

fn build_header_links(html: String, filename: &str) -> String {
    let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();

    regex.replace_all(&html, |caps: &Captures| {
        let level = &caps[1];
        let text = &caps[2];
        let mut id = text.to_string();
        let repl_sub = vec!["", "", "", "",
                            "", "",
                            "<", ">", "&", "'", """];
        for sub in repl_sub {
            id = id.replace(sub, "");
        }
        let id = id.chars().filter_map(|c| {
            if c.is_alphanumeric() || c == '-' || c == '_' {
                if c.is_ascii() {
                    Some(c.to_ascii_lowercase())
                } else {
                    Some(c)
                }
            } else if c.is_whitespace() && c.is_ascii() {
                Some('-')
            } else {
                None
            }
        }).collect::<String>();

        format!("<h{level}>{text}</h{level}>",
            level=level, id=id, text=text, filename=filename)
    }).into_owned()
}

// anchors to the same page (href="#anchor") do not work because of
// <base href="../"> pointing to the root folder. This function *fixes*
// that in a very inelegant way
fn fix_anchor_links(html: String, filename: &str) -> String {
    let regex = Regex::new(r##"]+)href="#([^"]+)"([^>]*)>"##).unwrap();
    regex.replace_all(&html, |caps: &Captures| {
        let before = &caps[1];
        let anchor = &caps[2];
        let after = &caps[3];

        format!("",
            before=before, filename=filename, anchor=anchor, after=after)
    }).into_owned()
}

// The rust book uses annotations for rustdoc to test code snippets, like the following:
// ```rust,should_panic
// fn main() {
//     // Code here
// }
// ```
// This function replaces all commas by spaces in the code block classes
fn fix_code_blocks(html: String) -> String {
    let regex = Regex::new(r##"]+)class="([^"]+)"([^>]*)>"##).unwrap();
    regex.replace_all(&html, |caps: &Captures| {
        let before = &caps[1];
        let classes = &caps[2].replace(",", " ");
        let after = &caps[3];

        format!("", before=before, classes=classes, after=after)
    }).into_owned()
}

fn add_playpen_pre(html: String) -> String {
    let regex = Regex::new(r##"((?s)]?class="([^"]+)".*?>(.*?))"##).unwrap();
    regex.replace_all(&html, |caps: &Captures| {
        let text = &caps[1];
        let classes = &caps[2];
        let code = &caps[3];

        if classes.contains("language-rust") && !classes.contains("ignore") {
            // wrap the contents in an external pre block

            if text.contains("fn main") {
                format!("
{}
", text)
            } else {
                // we need to inject our own main
                format!("
#fn main() {{
{}
#}}
", classes, code)
            }
        } else {
            // not language-rust, so no-op
            format!("{}", text)
        }
    }).into_owned()
}
<|endoftext|>


<commit_before>use words::{Word, Words};

use tens::*;
use hundreds::*;

pub struct Groups(Sign, Vec<Group>);

impl Groups
{
    pub fn new(val: i64) -> Groups
    {
        let sign = Sign::new(val);

        let val = i64::abs(val);

        let groups = if val < i64::pow(10, 6 * 3) // if it's greater than 10 ^ 18, the numerical approach won't work
        {
            (0..)
                .map(|step| (val / (i64::pow(10, step * 3))) % 1000)
                .take_while(|n| *n != 0)
                .map(|n| Group::new(n as usize))
                .collect::<Vec<_>>()
        }
        else
        {
            let mut val_string = val.to_string();

            while val_string.len() % 3 != 0
            {
                val_string = format!("0{}", val_string);
            }

            let mut parsed_chunks = val_string.chars().collect::<Vec<_>>().chunks(3)
                .map(|chunk| chunk.into_iter()
                    .map(|c| *c)
                    .collect::<String>())
                .map(|s| s.parse::<usize>().unwrap())
                .collect::<Vec<_>>();

            parsed_chunks.reverse();

            parsed_chunks.into_iter()
                .map(|n| Group::new(n))
                .collect::<Vec<_>>()
        };

        Groups(sign, groups)
    }

    pub fn build(&self) -> Words
    {
        let mut words = Words::new(vec![]);

        // i64::max is nine quintillion, so this should cover all the possible values
        let places = vec!["", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion"];

        for i in 0..self.1.len()
        {
            let mut group = self.1[i].build();

            if places[i] != ""
            {
                group.add(Words::new(vec![
                    Word::Space,
                    Word::Number(places[i].to_owned())]))
            }

            if i != 0
            {
                group.add(Words::new(vec![Word::Comma]));
            }

            group.add(words);

            words = group;
        }

        if self.0 == Sign::Negative
        {
            let mut neg = Words::new(vec![
                Word::Number("negative".to_owned()),
                Word::Space
            ]);

            neg.add(words);

            return neg;
        }

        words
    }
}

pub struct Group(Hundreds, Tens);

impl Group
{
    pub fn new(val: usize) -> Group
    {
        assert!(val < 1000);

        let hundreds = Hundreds::new(val / 100);
        let tens = Tens::new(val % 100);

        Group(hundreds, tens)
    }

    pub fn build(&self) -> Words
    {
        if self.0.is_zero()
        {
            return self.1.build()
        }

        let mut words = self.0.build();

        words.add(Words::new(vec![Word::And]));

        words.add(self.1.build());

        words
    }
}

#[derive(PartialEq)]
enum Sign
{
    Positive,
    Negative
}

impl Sign
{
    pub fn new(val: i64) -> Sign
    {
        match val >= 0
        {
            true => Sign::Positive,
            false => Sign::Negative
        }
    }
}<commit_msg>removed numerical group-splitting, because it's bugged<commit_after>use words::{Word, Words};

use tens::*;
use hundreds::*;

pub struct Groups(Sign, Vec<Group>);

impl Groups
{
    pub fn new(val: i64) -> Groups
    {
        let sign = Sign::new(val);

        let val = i64::abs(val);

        let groups =
        {
            let mut val_string = val.to_string();

            while val_string.len() % 3 != 0
            {
                val_string = format!("0{}", val_string);
            }

            let mut parsed_chunks = val_string.chars().collect::<Vec<_>>().chunks(3)
                .map(|chunk| chunk.into_iter()
                    .map(|c| *c)
                    .collect::<String>())
                .map(|s| s.parse::<usize>().unwrap())
                .collect::<Vec<_>>();

            parsed_chunks.reverse();

            parsed_chunks.into_iter()
                .map(|n| Group::new(n))
                .collect::<Vec<_>>()
        };

        Groups(sign, groups)
    }

    pub fn build(&self) -> Words
    {
        let mut words = Words::new(vec![]);

        // i64::max is nine quintillion, so this should cover all the possible values
        let places = vec!["", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion"];

        for i in 0..self.1.len()
        {
            let mut group = self.1[i].build();

            if places[i] != ""
            {
                group.add(Words::new(vec![
                    Word::Space,
                    Word::Number(places[i].to_owned())]))
            }

            if i != 0
            {
                group.add(Words::new(vec![Word::Comma]));
            }

            group.add(words);

            words = group;
        }

        if self.0 == Sign::Negative
        {
            let mut neg = Words::new(vec![
                Word::Number("negative".to_owned()),
                Word::Space
            ]);

            neg.add(words);

            return neg;
        }

        words
    }
}

pub struct Group(Hundreds, Tens);

impl Group
{
    pub fn new(val: usize) -> Group
    {
        assert!(val < 1000);

        let hundreds = Hundreds::new(val / 100);
        let tens = Tens::new(val % 100);

        Group(hundreds, tens)
    }

    pub fn build(&self) -> Words
    {
        if self.0.is_zero()
        {
            return self.1.build()
        }

        let mut words = self.0.build();

        words.add(Words::new(vec![Word::And]));

        words.add(self.1.build());

        words
    }
}

#[derive(PartialEq)]
enum Sign
{
    Positive,
    Negative
}

impl Sign
{
    pub fn new(val: i64) -> Sign
    {
        match val >= 0
        {
            true => Sign::Positive,
            false => Sign::Negative
        }
    }
}<|endoftext|>


<commit_before>//! Module containing the `import()` function

use std::io::BufRead;
use std::io::Read;

use serde_json;

use result::Result;
use task::Task;
use error::{TaskError, TaskErrorKind};

/// Import taskwarrior-exported JSON. This expects an JSON Array of objects, as exported by
/// taskwarrior.
pub fn import<R: Read>(r: R) -> Result<Vec<Task>> {
    serde_json::from_reader(r)
        .map_err(|e| {
            TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e)))
        })
}

/// Import a single JSON-formatted Task
pub fn import_task(s : &str) -> Result<Task> {
    serde_json::from_str(s)
        .map_err(|e| {
            TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e)))
        })
}

/// Reads line by line and tries to parse a task-object per line.
pub fn import_tasks<BR: BufRead>(r : BR) -> Vec<Result<Task>> {
    let mut vt = Vec::new();
    for line in r.lines() {
        if line.is_err() {
            vt.push(Err(TaskError::new(TaskErrorKind::ReaderError, Some(Box::new(line.unwrap_err())))));
            continue;
        }
        // Unwrap is safe because of continue above
        if line.as_ref().unwrap().len() <= 0 {
            // Empty strings are not usable, and shall be silently ignored
            continue;
        }
        vt.push(import_task(line.unwrap().as_str()));
    }
    vt
}

#[test]
fn test_one() {
    let s = r#"
[
    {
        "id": 1,
        "description": "some description",
        "entry": "20150619T165438Z",
        "modified": "20160327T164007Z",
        "project": "someproject",
        "status": "waiting",
        "tags": ["some", "tags", "are", "here"],
        "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
        "wait": "20160508T164007Z",
        "urgency": 0.583562
    }
]
"#;

    let imported = import(s.as_bytes());
    assert!(imported.is_ok());
    let imported = imported.unwrap();
    assert!(imported.len() == 1);
}

#[test]
fn test_two() {
    let s = r#"
[
    {
        "id"          : 1,
        "description" : "test",
        "entry"       : "20150619T165438Z",
        "modified"    : "20160327T164007Z",
        "project"     : "self.software",
        "status"      : "waiting",
        "tags"        : ["check", "this", "crate", "out"],
        "uuid"        : "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
        "wait"        : "20160508T164007Z",
        "urgency"     : 0.583562
    },
    {
        "id"          : 2,
        "description" : "another test",
        "entry"       : "20150623T181011Z",
        "modified"    : "20160327T163718Z",
        "priority"    : "L",
        "project"     : "studying",
        "status"      : "waiting",
        "tags"        : ["I", "have", "awesome", "kittens"],
        "uuid"        : "54d49ffc-a06b-4dd8-b7d1-db5f50594312",
        "wait"        : "20160508T163718Z",
        "annotations" : [
            {
                "entry"       : "20150623T181018Z",
                "description" : "fooooooobar"
            }
        ],
        "urgency"     : 3.16164
    },
    {
        "id"          : 3,
        "description" : "I love kittens, really!",
        "entry"       : "20150919T222323Z",
        "modified"    : "20160327T163718Z",
        "project"     : "getkittens",
        "status"      : "waiting",
        "tags"        : ["kittens", "are", "so", "damn", "awesome"],
        "uuid"        : "08ee8dce-cb97-4c8c-9940-c9a440e90119",
        "wait"        : "20160508T163718Z",
        "urgency"     : 1.07397
    }
]

"#;

    let imported = import(s.as_bytes());
    assert!(imported.is_ok());
    let imported = imported.unwrap();
    assert!(imported.len() == 3);
}

#[test]
fn test_one_single() {
    use status::TaskStatus;
    let s = r#"
{
    "id": 1,
    "description": "some description",
    "entry": "20150619T165438Z",
    "modified": "20160327T164007Z",
    "project": "someproject",
    "status": "waiting",
    "tags": ["some", "tags", "are", "here"],
    "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
    "wait": "20160508T164007Z",
    "urgency": 0.583562
}
"#;
    let imported = import_task(&s);
    assert!(imported.is_ok());
    let imported = imported.unwrap();
    assert!(imported.status() == &TaskStatus::Waiting);
}

#[test]
fn test_two_single() {
    use std::io::BufReader;
    use status::TaskStatus;
    let s = r#"
{"id":1,"description":"some description","entry":"20150619T165438Z","modified":"20160327T164007Z","project":"someproject","status":"waiting","tags":["some","tags","are","here"],"uuid":"8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","wait":"20160508T164007Z","urgency":0.583562}
{"id":1,"description":"some description","entry":"20150619T165438Z","modified":"20160327T164007Z","project":"someproject","status":"waiting","tags":["some","tags","are","here"],"uuid":"8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","wait":"20160508T164007Z","urgency":0.583562}"#;
    let imported = import_tasks(BufReader::new(s.as_bytes()));
    assert!(imported.len() == 2);
    assert!(imported[0].is_ok());
    assert!(imported[1].is_ok());
    let import0 = imported[0].as_ref().unwrap();
    let import1 = imported[1].as_ref().unwrap();
    assert!(import0.status() == &TaskStatus::Waiting);
    assert!(import1.status() == &TaskStatus::Waiting);
}

<commit_msg>expanded the test for a single task from a string. import_tasks uses the tested function import_task, so we should only need to check if the Vector has the correct length and the items are ok.<commit_after>//! Module containing the `import()` function

use std::io::BufRead;
use std::io::Read;

use serde_json;

use result::Result;
use task::Task;
use error::{TaskError, TaskErrorKind};

/// Import taskwarrior-exported JSON. This expects an JSON Array of objects, as exported by
/// taskwarrior.
pub fn import<R: Read>(r: R) -> Result<Vec<Task>> {
    serde_json::from_reader(r)
        .map_err(|e| {
            TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e)))
        })
}

/// Import a single JSON-formatted Task
pub fn import_task(s : &str) -> Result<Task> {
    serde_json::from_str(s)
        .map_err(|e| {
            TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e)))
        })
}

/// Reads line by line and tries to parse a task-object per line.
pub fn import_tasks<BR: BufRead>(r : BR) -> Vec<Result<Task>> {
    let mut vt = Vec::new();
    for line in r.lines() {
        if line.is_err() {
            vt.push(Err(TaskError::new(TaskErrorKind::ReaderError, Some(Box::new(line.unwrap_err())))));
            continue;
        }
        // Unwrap is safe because of continue above
        if line.as_ref().unwrap().len() <= 0 {
            // Empty strings are not usable, and shall be silently ignored
            continue;
        }
        vt.push(import_task(line.unwrap().as_str()));
    }
    vt
}

#[test]
fn test_one() {
    let s = r#"
[
    {
        "id": 1,
        "description": "some description",
        "entry": "20150619T165438Z",
        "modified": "20160327T164007Z",
        "project": "someproject",
        "status": "waiting",
        "tags": ["some", "tags", "are", "here"],
        "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
        "wait": "20160508T164007Z",
        "urgency": 0.583562
    }
]
"#;

    let imported = import(s.as_bytes());
    assert!(imported.is_ok());
    let imported = imported.unwrap();
    assert!(imported.len() == 1);
}

#[test]
fn test_two() {
    let s = r#"
[
    {
        "id"          : 1,
        "description" : "test",
        "entry"       : "20150619T165438Z",
        "modified"    : "20160327T164007Z",
        "project"     : "self.software",
        "status"      : "waiting",
        "tags"        : ["check", "this", "crate", "out"],
        "uuid"        : "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
        "wait"        : "20160508T164007Z",
        "urgency"     : 0.583562
    },
    {
        "id"          : 2,
        "description" : "another test",
        "entry"       : "20150623T181011Z",
        "modified"    : "20160327T163718Z",
        "priority"    : "L",
        "project"     : "studying",
        "status"      : "waiting",
        "tags"        : ["I", "have", "awesome", "kittens"],
        "uuid"        : "54d49ffc-a06b-4dd8-b7d1-db5f50594312",
        "wait"        : "20160508T163718Z",
        "annotations" : [
            {
                "entry"       : "20150623T181018Z",
                "description" : "fooooooobar"
            }
        ],
        "urgency"     : 3.16164
    },
    {
        "id"          : 3,
        "description" : "I love kittens, really!",
        "entry"       : "20150919T222323Z",
        "modified"    : "20160327T163718Z",
        "project"     : "getkittens",
        "status"      : "waiting",
        "tags"        : ["kittens", "are", "so", "damn", "awesome"],
        "uuid"        : "08ee8dce-cb97-4c8c-9940-c9a440e90119",
        "wait"        : "20160508T163718Z",
        "urgency"     : 1.07397
    }
]

"#;

    let imported = import(s.as_bytes());
    assert!(imported.is_ok());
    let imported = imported.unwrap();
    assert!(imported.len() == 3);
}

#[test]
fn test_one_single() {
    use status::TaskStatus;
    use date::Date;
    use date::TASKWARRIOR_DATETIME_TEMPLATE;
    use uuid::Uuid;
    use chrono::naive::datetime::NaiveDateTime;
    fn mkdate(s: &str) -> Date {
        let n = NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE);
        Date::from(n.unwrap())
    }
    let s = r#"
{
    "id": 1,
    "description": "some description",
    "entry": "20150619T165438Z",
    "modified": "20160327T164007Z",
    "project": "someproject",
    "status": "waiting",
    "tags": ["some", "tags", "are", "here"],
    "uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
    "wait": "20160508T164007Z",
    "urgency": 0.583562
}
"#;
    let imported = import_task(&s);
    assert!(imported.is_ok());

    // Check for every information
    let task = imported.unwrap();
    assert!(task.status() == &TaskStatus::Waiting);
    assert!(task.description() == "some description");
    assert!(task.entry().clone() == mkdate("20150619T165438Z"));
    assert!(task.uuid().clone() == Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap());
    assert!(task.modified() == Some(&mkdate("20160327T164007Z")));
    assert!(task.project() == Some(&String::from("someproject")));
    if let Some(tags) = task.tags() {
        for tag in tags {
            let any_tag = [ "some", "tags", "are", "here", ]
                .into_iter().any(|t| tag == *t);
            assert!(any_tag, "Tag {} missing", tag);
        }
    } else {
        assert!(false, "Tags completely missing");
    }

    assert!(task.wait() == Some(&mkdate("20160508T164007Z")));
}

#[test]
fn test_two_single() {
    use std::io::BufReader;
    use status::TaskStatus;
    let s = r#"
{"id":1,"description":"some description","entry":"20150619T165438Z","modified":"20160327T164007Z","project":"someproject","status":"waiting","tags":["some","tags","are","here"],"uuid":"8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","wait":"20160508T164007Z","urgency":0.583562}
{"id":1,"description":"some description","entry":"20150619T165438Z","modified":"20160327T164007Z","project":"someproject","status":"waiting","tags":["some","tags","are","here"],"uuid":"8ca953d5-18b4-4eb9-bd56-18f2e5b752f0","wait":"20160508T164007Z","urgency":0.583562}"#;
    let imported = import_tasks(BufReader::new(s.as_bytes()));
    assert!(imported.len() == 2);
    assert!(imported[0].is_ok());
    assert!(imported[1].is_ok());
    let import0 = imported[0].as_ref().unwrap();
    let import1 = imported[1].as_ref().unwrap();
    assert!(import0.status() == &TaskStatus::Waiting);
    assert!(import1.status() == &TaskStatus::Waiting);
}

<|endoftext|>


<commit_before>// Copyright (c) 2016 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use BoxFuture;
use errors::*;
use std::fmt;
#[cfg(feature = "ssl")]
use std::fs::File;
use lib_futures::Async::NotReady;
use lib_futures::Async::Ready;
#[cfg(feature = "ssl")]
use lib_futures::{Future, IntoFuture};
use lib_futures::Poll;
use lib_futures::stream;
use io::futures::ConnectingStream;
use io::futures::new_connecting_stream;
use io::futures::new_write_packet;
use io::futures::WritePacket;
#[cfg(feature = "ssl")]
use native_tls::{Certificate, Pkcs12, TlsConnector};
use myc::packets::{PacketParser, ParseResult, RawPacket};
use opts::SslOpts;
use std::cmp;
use std::io;
use std::io::Read;
use std::net::ToSocketAddrs;
use std::time::Duration;
use tokio::net::TcpStream;
use tokio::reactor::Handle;
use tokio_io::AsyncRead;
use tokio_io::AsyncWrite;
#[cfg(feature = "ssl")]
use tokio_tls::{TlsConnectorExt, TlsStream};

pub mod futures;

#[derive(Debug)]
enum Endpoint {
    Plain(TcpStream),
    #[cfg(feature = "ssl")]
    Secure(TlsStream<TcpStream>),
}

impl Endpoint {
    pub fn set_keepalive_ms(&self, ms: Option<u32>) -> Result<()> {
        let ms = ms.map(|val| Duration::from_millis(val as u64));
        match *self {
            Endpoint::Plain(ref stream) => stream.set_keepalive(ms)?,
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref stream) => stream.get_ref().get_ref().set_keepalive(ms)?,
        }
        Ok(())
    }

    #[cfg(feature = "ssl")]
    pub fn make_secure(self, domain: String, ssl_opts: SslOpts) -> BoxFuture<Self> {
        let fut = ::std::fs::File::open(ssl_opts.pkcs12_path())
            .map_err(Error::from)
            .and_then(|mut file| {
                let mut der = vec![];
                file.read_to_end(&mut der)?;
                let identity = Pkcs12::from_der(&*der, ssl_opts.password().unwrap_or(""))
                    .chain_err(|| "Can't parse der")?;
                let mut builder = TlsConnector::builder().chain_err(
                    || "Can't create TlsConnectorBuilder)",
                )?;
                match ssl_opts.root_cert_path() {
                    Some(root_cert_path) => {
                        let mut root_cert_der = vec![];
                        let mut root_cert_file = File::open(root_cert_path)?;
                        root_cert_file.read_to_end(&mut root_cert_der)?;
                        let root_cert = Certificate::from_der(&*root_cert_der)
                            .chain_err(|| "Can't parse root certificate")?;
                        builder.add_root_certificate(root_cert)
                            .chain_err(|| "Can't add root certificate")?;
                    },
                    None => (),
                }
                builder.identity(identity).chain_err(
                    || "Can't set identity for TlsConnectorBuilder",
                )?;
                builder.build().chain_err(
                    || "Can't build TlsConnectorBuilder",
                )
            })
            .into_future()
            .and_then(move |tls_connector| match self {
                Endpoint::Plain(stream) => {
                    let fut = if ssl_opts.skip_domain_validation() {
                        TlsConnectorExt::danger_connect_async_without_providing_domain_for_certificate_verification_and_server_name_indication(&tls_connector, stream)
                    } else {
                        TlsConnectorExt::connect_async(&tls_connector, &*domain, stream)
                    };
                    fut.then(|result| result.chain_err(|| "Can't connect TlsConnector"))
                }
                Endpoint::Secure(_) => unreachable!(),
            })
            .map(|tls_stream| Endpoint::Secure(tls_stream));
        Box::new(fut)
    }
}

impl From<TcpStream> for Endpoint {
    fn from(stream: TcpStream) -> Self {
        Endpoint::Plain(stream)
    }
}

#[cfg(feature = "ssl")]
impl From<TlsStream<TcpStream>> for Endpoint {
    fn from(stream: TlsStream<TcpStream>) -> Self {
        Endpoint::Secure(stream)
    }
}

impl io::Read for Endpoint {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Endpoint::Plain(ref mut stream) => stream.read(buf),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref mut stream) => stream.read(buf),
        }
    }
}

impl io::Write for Endpoint {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            Endpoint::Plain(ref mut stream) => stream.write(buf),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref mut stream) => stream.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            Endpoint::Plain(ref mut stream) => stream.flush(),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref mut stream) => stream.flush(),
        }
    }
}

impl AsyncRead for Endpoint {
    unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [u8]) -> bool {
        match *self {
            Endpoint::Plain(ref stream) => stream.prepare_uninitialized_buffer(_buf),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref stream) => stream.prepare_uninitialized_buffer(_buf),
        }
    }
}

impl AsyncWrite for Endpoint {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        match *self {
            Endpoint::Plain(ref mut stream) => stream.shutdown(),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref mut stream) => stream.shutdown(),
        }
    }
}

/// Stream connected to MySql server.
pub struct Stream {
    endpoint: Option<Endpoint>,
    closed: bool,
    next_packet: Option<ParseResult>,
    buf: Option<Vec<u8>>,
}

impl fmt::Debug for Stream {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Stream (endpoint={:?})", self.endpoint)
    }
}

impl Stream {
    pub fn connect<S>(addr: S, handle: &Handle) -> ConnectingStream
    where
        S: ToSocketAddrs,
    {
        new_connecting_stream(addr, handle)
    }

    pub fn write_packet(self, data: Vec<u8>, seq_id: u8) -> WritePacket {
        new_write_packet(self, data, seq_id)
    }

    pub fn set_keepalive_ms(&self, ms: Option<u32>) -> Result<()> {
        match self.endpoint {
            Some(ref endpoint) => endpoint.set_keepalive_ms(ms),
            None => unreachable!(),
        }
    }

    #[cfg(not(feature = "ssl"))]
    #[allow(unused)]
    pub fn make_secure(self, domain: String, ssl_opts: SslOpts) -> BoxFuture<Self> {
        panic!("Ssl connection requires `ssl` feature");
    }

    #[cfg(feature = "ssl")]
    pub fn make_secure(mut self, domain: String, ssl_opts: SslOpts) -> BoxFuture<Self> {
        match self.endpoint.take() {
            Some(endpoint) => {
                let fut = endpoint.make_secure(domain, ssl_opts).map(|endpoint| {
                    self.endpoint = Some(endpoint);
                    self
                });
                Box::new(fut)
            }
            None => unreachable!(),
        }
    }
}

impl io::Read for Stream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.endpoint {
            Some(ref mut endpoint) => endpoint.read(buf),
            None => unreachable!(),
        }
    }
}

impl io::Write for Stream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self.endpoint {
            Some(ref mut endpoint) => endpoint.write(buf),
            None => unreachable!(),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.endpoint {
            Some(ref mut endpoint) => endpoint.flush(),
            None => unreachable!(),
        }
    }
}

impl AsyncRead for Stream {
    unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [u8]) -> bool {
        match self.endpoint {
            Some(ref endpoint) => endpoint.prepare_uninitialized_buffer(_buf),
            None => unreachable!(),
        }
    }
}

impl AsyncWrite for Stream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        match self.endpoint {
            Some(ref mut endpoint) => endpoint.shutdown(),
            None => unreachable!(),
        }
    }
}

impl stream::Stream for Stream {
    type Item = (RawPacket, u8);
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<(RawPacket, u8)>, Error> {
        loop {
            // should read everything from self.endpoint
            let mut would_block = false;
            if !self.closed {
                let mut buf = [0u8; 4096];
                loop {
                    match self.read(&mut buf[..]) {
                        Ok(0) => {
                            break;
                        }
                        Ok(size) => {
                            let buf_handle = self.buf.as_mut().unwrap();
                            buf_handle.extend_from_slice(&buf[..size]);
                        }
                        Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                            would_block = true;
                            break;
                        }
                        Err(error) => {
                            self.closed = true;
                            return Err(Error::from(error));
                        }
                    };
                }
            } else {
                return Ok(Ready(None));
            }

            // need to call again if there is a data in self.buf
            // or data was written to packet parser
            let mut should_poll = false;

            let next_packet = self.next_packet.take().expect(
                "Stream.next_packet should not be None",
                );
            let next_packet = match next_packet {
                ParseResult::Done(packet, seq_id) => {
                    self.next_packet = Some(PacketParser::empty().parse());
                    return Ok(Ready(Some((packet, seq_id))));
                }
                ParseResult::Incomplete(mut new_packet, needed) => {
                    let buf_handle = self.buf.as_mut().unwrap();
                    let buf_len = buf_handle.len();

                    let to = cmp::min(needed, buf_len);
                    new_packet.extend_from_slice(&buf_handle[..to]);
                    unsafe {
                        let src = buf_handle.as_ptr().offset(to as isize);
                        let dst = buf_handle.as_mut_ptr();
                        let len = buf_handle.len() - to;
                        ::std::ptr::copy(src, dst, len);
                        buf_handle.set_len(len);
                    }

                    if buf_len != 0 {
                        should_poll = true;
                    }

                    new_packet
                }
            };

            self.next_packet = Some(next_packet.parse());

            if !should_poll && would_block {
                return Ok(NotReady)
            }
        }
    }
}
<commit_msg>Reduce unsafe block in io::Stream::poll<commit_after>// Copyright (c) 2016 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use BoxFuture;
use errors::*;
use std::fmt;
#[cfg(feature = "ssl")]
use std::fs::File;
use lib_futures::Async::NotReady;
use lib_futures::Async::Ready;
#[cfg(feature = "ssl")]
use lib_futures::{Future, IntoFuture};
use lib_futures::Poll;
use lib_futures::stream;
use io::futures::ConnectingStream;
use io::futures::new_connecting_stream;
use io::futures::new_write_packet;
use io::futures::WritePacket;
#[cfg(feature = "ssl")]
use native_tls::{Certificate, Pkcs12, TlsConnector};
use myc::packets::{PacketParser, ParseResult, RawPacket};
use opts::SslOpts;
use std::cmp;
use std::io;
use std::io::Read;
use std::net::ToSocketAddrs;
use std::time::Duration;
use tokio::net::TcpStream;
use tokio::reactor::Handle;
use tokio_io::AsyncRead;
use tokio_io::AsyncWrite;
#[cfg(feature = "ssl")]
use tokio_tls::{TlsConnectorExt, TlsStream};

pub mod futures;

#[derive(Debug)]
enum Endpoint {
    Plain(TcpStream),
    #[cfg(feature = "ssl")]
    Secure(TlsStream<TcpStream>),
}

impl Endpoint {
    pub fn set_keepalive_ms(&self, ms: Option<u32>) -> Result<()> {
        let ms = ms.map(|val| Duration::from_millis(val as u64));
        match *self {
            Endpoint::Plain(ref stream) => stream.set_keepalive(ms)?,
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref stream) => stream.get_ref().get_ref().set_keepalive(ms)?,
        }
        Ok(())
    }

    #[cfg(feature = "ssl")]
    pub fn make_secure(self, domain: String, ssl_opts: SslOpts) -> BoxFuture<Self> {
        let fut = ::std::fs::File::open(ssl_opts.pkcs12_path())
            .map_err(Error::from)
            .and_then(|mut file| {
                let mut der = vec![];
                file.read_to_end(&mut der)?;
                let identity = Pkcs12::from_der(&*der, ssl_opts.password().unwrap_or(""))
                    .chain_err(|| "Can't parse der")?;
                let mut builder = TlsConnector::builder().chain_err(
                    || "Can't create TlsConnectorBuilder)",
                )?;
                match ssl_opts.root_cert_path() {
                    Some(root_cert_path) => {
                        let mut root_cert_der = vec![];
                        let mut root_cert_file = File::open(root_cert_path)?;
                        root_cert_file.read_to_end(&mut root_cert_der)?;
                        let root_cert = Certificate::from_der(&*root_cert_der)
                            .chain_err(|| "Can't parse root certificate")?;
                        builder.add_root_certificate(root_cert)
                            .chain_err(|| "Can't add root certificate")?;
                    },
                    None => (),
                }
                builder.identity(identity).chain_err(
                    || "Can't set identity for TlsConnectorBuilder",
                )?;
                builder.build().chain_err(
                    || "Can't build TlsConnectorBuilder",
                )
            })
            .into_future()
            .and_then(move |tls_connector| match self {
                Endpoint::Plain(stream) => {
                    let fut = if ssl_opts.skip_domain_validation() {
                        TlsConnectorExt::danger_connect_async_without_providing_domain_for_certificate_verification_and_server_name_indication(&tls_connector, stream)
                    } else {
                        TlsConnectorExt::connect_async(&tls_connector, &*domain, stream)
                    };
                    fut.then(|result| result.chain_err(|| "Can't connect TlsConnector"))
                }
                Endpoint::Secure(_) => unreachable!(),
            })
            .map(|tls_stream| Endpoint::Secure(tls_stream));
        Box::new(fut)
    }
}

impl From<TcpStream> for Endpoint {
    fn from(stream: TcpStream) -> Self {
        Endpoint::Plain(stream)
    }
}

#[cfg(feature = "ssl")]
impl From<TlsStream<TcpStream>> for Endpoint {
    fn from(stream: TlsStream<TcpStream>) -> Self {
        Endpoint::Secure(stream)
    }
}

impl io::Read for Endpoint {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Endpoint::Plain(ref mut stream) => stream.read(buf),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref mut stream) => stream.read(buf),
        }
    }
}

impl io::Write for Endpoint {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            Endpoint::Plain(ref mut stream) => stream.write(buf),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref mut stream) => stream.write(buf),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match *self {
            Endpoint::Plain(ref mut stream) => stream.flush(),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref mut stream) => stream.flush(),
        }
    }
}

impl AsyncRead for Endpoint {
    unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [u8]) -> bool {
        match *self {
            Endpoint::Plain(ref stream) => stream.prepare_uninitialized_buffer(_buf),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref stream) => stream.prepare_uninitialized_buffer(_buf),
        }
    }
}

impl AsyncWrite for Endpoint {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        match *self {
            Endpoint::Plain(ref mut stream) => stream.shutdown(),
            #[cfg(feature = "ssl")]
            Endpoint::Secure(ref mut stream) => stream.shutdown(),
        }
    }
}

/// Stream connected to MySql server.
pub struct Stream {
    endpoint: Option<Endpoint>,
    closed: bool,
    next_packet: Option<ParseResult>,
    buf: Option<Vec<u8>>,
}

impl fmt::Debug for Stream {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Stream (endpoint={:?})", self.endpoint)
    }
}

impl Stream {
    pub fn connect<S>(addr: S, handle: &Handle) -> ConnectingStream
    where
        S: ToSocketAddrs,
    {
        new_connecting_stream(addr, handle)
    }

    pub fn write_packet(self, data: Vec<u8>, seq_id: u8) -> WritePacket {
        new_write_packet(self, data, seq_id)
    }

    pub fn set_keepalive_ms(&self, ms: Option<u32>) -> Result<()> {
        match self.endpoint {
            Some(ref endpoint) => endpoint.set_keepalive_ms(ms),
            None => unreachable!(),
        }
    }

    #[cfg(not(feature = "ssl"))]
    #[allow(unused)]
    pub fn make_secure(self, domain: String, ssl_opts: SslOpts) -> BoxFuture<Self> {
        panic!("Ssl connection requires `ssl` feature");
    }

    #[cfg(feature = "ssl")]
    pub fn make_secure(mut self, domain: String, ssl_opts: SslOpts) -> BoxFuture<Self> {
        match self.endpoint.take() {
            Some(endpoint) => {
                let fut = endpoint.make_secure(domain, ssl_opts).map(|endpoint| {
                    self.endpoint = Some(endpoint);
                    self
                });
                Box::new(fut)
            }
            None => unreachable!(),
        }
    }
}

impl io::Read for Stream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.endpoint {
            Some(ref mut endpoint) => endpoint.read(buf),
            None => unreachable!(),
        }
    }
}

impl io::Write for Stream {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self.endpoint {
            Some(ref mut endpoint) => endpoint.write(buf),
            None => unreachable!(),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.endpoint {
            Some(ref mut endpoint) => endpoint.flush(),
            None => unreachable!(),
        }
    }
}

impl AsyncRead for Stream {
    unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [u8]) -> bool {
        match self.endpoint {
            Some(ref endpoint) => endpoint.prepare_uninitialized_buffer(_buf),
            None => unreachable!(),
        }
    }
}

impl AsyncWrite for Stream {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        match self.endpoint {
            Some(ref mut endpoint) => endpoint.shutdown(),
            None => unreachable!(),
        }
    }
}

impl stream::Stream for Stream {
    type Item = (RawPacket, u8);
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<(RawPacket, u8)>, Error> {
        loop {
            // should read everything from self.endpoint
            let mut would_block = false;
            if !self.closed {
                let mut buf = [0u8; 4096];
                loop {
                    match self.read(&mut buf[..]) {
                        Ok(0) => {
                            break;
                        }
                        Ok(size) => {
                            let buf_handle = self.buf.as_mut().unwrap();
                            buf_handle.extend_from_slice(&buf[..size]);
                        }
                        Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                            would_block = true;
                            break;
                        }
                        Err(error) => {
                            self.closed = true;
                            return Err(Error::from(error));
                        }
                    };
                }
            } else {
                return Ok(Ready(None));
            }

            // need to call again if there is a data in self.buf
            // or data was written to packet parser
            let mut should_poll = false;

            let next_packet = self.next_packet.take().expect(
                "Stream.next_packet should not be None",
                );
            let next_packet = match next_packet {
                ParseResult::Done(packet, seq_id) => {
                    self.next_packet = Some(PacketParser::empty().parse());
                    return Ok(Ready(Some((packet, seq_id))));
                }
                ParseResult::Incomplete(mut new_packet, needed) => {
                    let buf_handle = self.buf.as_mut().unwrap();
                    let buf_len = buf_handle.len();

                    let to = cmp::min(needed, buf_len);
                    new_packet.extend_from_slice(&buf_handle[..to]);
                    {
                        let src = unsafe { buf_handle.as_ptr().offset(to as isize) };
                        let dst = buf_handle.as_mut_ptr();
                        let len = buf_handle.len() - to;
                        unsafe {
                            ::std::ptr::copy(src, dst, len);
                            buf_handle.set_len(len);
                        }
                    }

                    if buf_len != 0 {
                        should_poll = true;
                    }

                    new_packet
                }
            };

            self.next_packet = Some(next_packet.parse());

            if !should_poll && would_block {
                return Ok(NotReady)
            }
        }
    }
}
<|endoftext|>


<commit_before>#![feature(iter_arith)]

pub use self::types::{Type,TypeTrait};
pub use self::value::{Value,Expression,ExpressionTrait,Register};
pub use lang::Name;
pub use self::instruction::{Instruction,InstructionTrait,Unary,Binary};
pub use self::print::printable;
pub use self::users::Users;
pub use self::cond::Condition;

/// Where types are implemented.
pub mod types;
/// Contains the different value kinds.
pub mod value;
/// Where instruction definitions are located.
pub mod instruction;
/// The IR verifier.
pub mod verifier;
/// Routines for printing modules.
pub mod print;
/// IR reading routines.
pub mod read;
/// Expression user information.
pub mod users;

pub mod cond;

#[macro_use]
extern crate compiler_util as util;
extern crate compiler_lang as lang;

extern crate num;
extern crate bit_vec;

pub type Module = ::lang::Module<Value>;
pub type Global = ::lang::Global<Value>;
pub type Function = ::lang::Function<Value>;
pub type Block = ::lang::Block<Value>;
pub type Signature = ::lang::Signature<Value>;
pub type Parameter = ::lang::Parameter<Value>;

<commit_msg>Removed unused #[macro_use]<commit_after>#![feature(iter_arith)]

pub use self::types::{Type,TypeTrait};
pub use self::value::{Value,Expression,ExpressionTrait,Register};
pub use lang::Name;
pub use self::instruction::{Instruction,InstructionTrait,Unary,Binary};
pub use self::print::printable;
pub use self::users::Users;
pub use self::cond::Condition;

/// Where types are implemented.
pub mod types;
/// Contains the different value kinds.
pub mod value;
/// Where instruction definitions are located.
pub mod instruction;
/// The IR verifier.
pub mod verifier;
/// Routines for printing modules.
pub mod print;
/// IR reading routines.
pub mod read;
/// Expression user information.
pub mod users;
/// Condition codes.
pub mod cond;

extern crate compiler_util as util;
extern crate compiler_lang as lang;

extern crate num;
extern crate bit_vec;

pub type Module = ::lang::Module<Value>;
pub type Global = ::lang::Global<Value>;
pub type Function = ::lang::Function<Value>;
pub type Block = ::lang::Block<Value>;
pub type Signature = ::lang::Signature<Value>;
pub type Parameter = ::lang::Parameter<Value>;

<|endoftext|>


<commit_before>//xfail-stage0
//xfail-stage1
use std;

fn main() {

    obj a() {
        fn foo() -> int {
            ret 2;
        }
        fn bar() -> int {
            ret self.foo();
        }
    }

    auto my_a = a();

    // Extending an object with a new method that contains a simple
    // self-call.  Adding support for this is issue #540.

    // Right now, this fails with a failed lookup in a hashmap; not
    // sure where, but I think it might be during typeck.
    auto my_b = obj {
        fn baz() -> int {
            ret self.foo();
        }
        with my_a
    };

    assert my_b.baz() == 2);

}
<commit_msg>Typo fix in test case.<commit_after>//xfail-stage0
//xfail-stage1
use std;

fn main() {

    obj a() {
        fn foo() -> int {
            ret 2;
        }
        fn bar() -> int {
            ret self.foo();
        }
    }

    auto my_a = a();

    // Extending an object with a new method that contains a simple
    // self-call.  Adding support for this is issue #540.

    // Right now, this fails with a failed lookup in a hashmap; not
    // sure where, but I think it might be during typeck.
    auto my_b = obj {
        fn baz() -> int {
            ret self.foo();
        }
        with my_a
    };

    assert (my_b.baz() == 2);

}
<|endoftext|>


<commit_before>extern crate walkdir;

use self::walkdir::{DirEntry as WalkDirEntry, WalkDir, WalkDirIterator};

use std::collections::BTreeSet;
use std::ffi::OsString;
use std::fs;
use std::io::Error as IoError;
use std::path::{Path, PathBuf};
use std::result::Result;

macro_rules! path_buf {
    ($($x: expr),+) => {
        PathBuf::new()
            $(.join($x))+
    };
}

pub fn ls<P: AsRef<Path>>(path: P) -> Result<Vec<String>, IoError> {
    let dirs = try!(fs::read_dir(path));
    let dirs = dirs
        .filter_map(Result::ok)
        .map(|d| d.file_name())
        .map(OsString::into_string)
        .filter_map(Result::ok)
        .collect::<Vec<String>>();
    Ok(dirs)
}

pub fn is_empty_dir<P: AsRef<Path>>(path: P) -> bool {
    match fs::read_dir(path) {
        Ok(rd) => rd.count() == 0,
        Err(_) => false,
    }
}

fn is_hidden_name(file_name: &str) -> bool {
    file_name.starts_with(".") && file_name.len() > 1 && file_name != ".."
}
#[test]
fn is_hidden_name_should_return_true() {
    let file_names = [
        ".a",
        ".ab",
        ".abc",
        "..a",
        "...",
    ];
    for file_name in &file_names {
        assert!(is_hidden_name(file_name));
    }
}
#[test]
fn is_hidden_name_should_return_false() {
    let file_names = [
        "",

        ".",
        "..",
        "a",
        "ab",
        "abc",
    ];
    for file_name in &file_names {
        assert!(!is_hidden_name(file_name));
    }
}

fn is_hidden(entry: &WalkDirEntry) -> bool {
    entry
        .file_name()
        .to_str()
        .map_or(false, is_hidden_name)
}

pub fn walk_dir<P: AsRef<Path>>(root: P) -> BTreeSet<String> {
    let walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !is_hidden(e))
        .filter_map(Result::ok)
        .filter(|e| !e.file_type().is_dir())
        .collect::<Vec<WalkDirEntry>>();

    walker
        .into_iter()
        .filter_map(|e| e.path().to_str().map(|s| s.to_string()))
        .collect::<BTreeSet<String>>()
}

pub fn dirs_ordered_by_descending_depth<P: AsRef<Path>>(root: P) -> Vec<PathBuf> {
    let mut walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !is_hidden(e))
        .filter_entry(|e| e.file_type().is_dir())
        .filter_map(Result::ok)
        .collect::<Vec<WalkDirEntry>>();

    walker.sort_by(|l, r| {
        let l_depth = &l.depth();
        let r_depth = &r.depth();
        r_depth.cmp(l_depth)
    });

    walker
        .into_iter()
        .map(|e| e.path().to_path_buf())
        .collect::<Vec<PathBuf>>()
}

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

    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    const D1: &'static str = "directory1";
    const D2: &'static str = "directory2";
    const D3: &'static str = "directory3";
    const F1: &'static str = "file1";
    const F2: &'static str = "file2";

    struct TestHelper;
    impl TestHelper {
        fn path_to_d1() -> PathBuf {
            PathBuf::new().join(D1)
        }
        fn path_to_d2() -> PathBuf {
            Self::path_to_d1().join(D2)
        }
        fn path_to_d3() -> PathBuf {
            Self::path_to_d2().join(D3)
        }

        fn path_to_f1() -> PathBuf {
            Self::path_to_d1().join(F1)
        }
        fn path_to_f2() -> PathBuf {
            Self::path_to_d2().join(F2)
        }

        fn remove_dirs_and_files() {
            fs::remove_dir_all(Self::path_to_d1()).ok();
        }

        fn create_dirs_and_files() {
            Self::remove_dirs_and_files();

            fs::create_dir_all(Self::path_to_d3()).ok();

            let mut f = File::create(Self::path_to_f1()).unwrap();
            f.write("\n".as_ref()).ok();
            fs::copy(Self::path_to_f1(), Self::path_to_f2()).ok();
        }
    }

    #[test]
    fn path_buf_macro_should_create_path_buf() {
        let paths = [
            (Path::new("path").to_path_buf()          , path_buf!["path"              ]),
            (Path::new("path").join("to")             , path_buf!["path", "to"        ]),
            (Path::new("path").join("to").join("file"), path_buf!["path", "to", "file"]),
        ];
        for &(ref correct, ref calculated) in &paths {
            assert_eq!(correct, calculated);
        }
    }

    #[test]
    fn ls_should_return_ok() {
        TestHelper::create_dirs_and_files();

        let empty_vec: Vec<String> = Vec::new();

        assert_eq!(vec![D2.to_string(), F1.to_string()], ls(TestHelper::path_to_d1()).unwrap());
        assert_eq!(vec![D3.to_string(), F2.to_string()], ls(TestHelper::path_to_d2()).unwrap());
        assert_eq!(empty_vec                           , ls(TestHelper::path_to_d3()).unwrap());

        TestHelper::remove_dirs_and_files();
    }
    #[test]
    fn ls_should_return_err() {
        // need to cause permission error
    }

    #[test]
    fn is_empty_dir_should_return_true() {
    }
    #[test]
    fn is_empty_dir_should_return_false() {
    }

    #[test]
    fn walk_dir_should_return_b_tree_set() {
    }

    #[test]
    fn dirs_ordered_by_descending_depth_should_return_vec() {
    }
}
<commit_msg>[Test] Rename "TestHelper" => "Helper"<commit_after>extern crate walkdir;

use self::walkdir::{DirEntry as WalkDirEntry, WalkDir, WalkDirIterator};

use std::collections::BTreeSet;
use std::ffi::OsString;
use std::fs;
use std::io::Error as IoError;
use std::path::{Path, PathBuf};
use std::result::Result;

macro_rules! path_buf {
    ($($x: expr),+) => {
        PathBuf::new()
            $(.join($x))+
    };
}

pub fn ls<P: AsRef<Path>>(path: P) -> Result<Vec<String>, IoError> {
    let dirs = try!(fs::read_dir(path));
    let dirs = dirs
        .filter_map(Result::ok)
        .map(|d| d.file_name())
        .map(OsString::into_string)
        .filter_map(Result::ok)
        .collect::<Vec<String>>();
    Ok(dirs)
}

pub fn is_empty_dir<P: AsRef<Path>>(path: P) -> bool {
    match fs::read_dir(path) {
        Ok(rd) => rd.count() == 0,
        Err(_) => false,
    }
}

fn is_hidden_name(file_name: &str) -> bool {
    file_name.starts_with(".") && file_name.len() > 1 && file_name != ".."
}
#[test]
fn is_hidden_name_should_return_true() {
    let file_names = [
        ".a",
        ".ab",
        ".abc",
        "..a",
        "...",
    ];
    for file_name in &file_names {
        assert!(is_hidden_name(file_name));
    }
}
#[test]
fn is_hidden_name_should_return_false() {
    let file_names = [
        "",

        ".",
        "..",
        "a",
        "ab",
        "abc",
    ];
    for file_name in &file_names {
        assert!(!is_hidden_name(file_name));
    }
}

fn is_hidden(entry: &WalkDirEntry) -> bool {
    entry
        .file_name()
        .to_str()
        .map_or(false, is_hidden_name)
}

pub fn walk_dir<P: AsRef<Path>>(root: P) -> BTreeSet<String> {
    let walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !is_hidden(e))
        .filter_map(Result::ok)
        .filter(|e| !e.file_type().is_dir())
        .collect::<Vec<WalkDirEntry>>();

    walker
        .into_iter()
        .filter_map(|e| e.path().to_str().map(|s| s.to_string()))
        .collect::<BTreeSet<String>>()
}

pub fn dirs_ordered_by_descending_depth<P: AsRef<Path>>(root: P) -> Vec<PathBuf> {
    let mut walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !is_hidden(e))
        .filter_entry(|e| e.file_type().is_dir())
        .filter_map(Result::ok)
        .collect::<Vec<WalkDirEntry>>();

    walker.sort_by(|l, r| {
        let l_depth = &l.depth();
        let r_depth = &r.depth();
        r_depth.cmp(l_depth)
    });

    walker
        .into_iter()
        .map(|e| e.path().to_path_buf())
        .collect::<Vec<PathBuf>>()
}

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

    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    struct Helper;
    impl Helper {
        fn d1() -> String { "directory1".to_string() }
        fn d2() -> String { "directory2".to_string() }
        fn d3() -> String { "directory3".to_string() }

        fn f1() -> String { "file1".to_string() }
        fn f2() -> String { "file2".to_string() }

        fn path_to_d1() -> PathBuf {
            PathBuf::new().join(Self::d1())
        }
        fn path_to_d2() -> PathBuf {
            Self::path_to_d1().join(Self::d2())
        }
        fn path_to_d3() -> PathBuf {
            Self::path_to_d2().join(Self::d3())
        }

        fn path_to_f1() -> PathBuf {
            Self::path_to_d1().join(Self::f1())
        }
        fn path_to_f2() -> PathBuf {
            Self::path_to_d2().join(Self::f2())
        }

        fn remove_dirs_and_files() {
            fs::remove_dir_all(Self::path_to_d1()).ok();
        }

        fn create_dirs_and_files() {
            Self::remove_dirs_and_files();

            fs::create_dir_all(Self::path_to_d3()).ok();

            let mut f = File::create(Self::path_to_f1()).unwrap();
            f.write("\n".as_ref()).ok();
            fs::copy(Self::path_to_f1(), Self::path_to_f2()).ok();
        }
    }

    #[test]
    fn path_buf_macro_should_create_path_buf() {
        let paths = [
            (Path::new("path").to_path_buf()          , path_buf!["path"              ]),
            (Path::new("path").join("to")             , path_buf!["path", "to"        ]),
            (Path::new("path").join("to").join("file"), path_buf!["path", "to", "file"]),
        ];
        for &(ref correct, ref calculated) in &paths {
            assert_eq!(correct, calculated);
        }
    }

    #[test]
    fn ls_should_return_ok() {
        Helper::create_dirs_and_files();

        let empty_vec: Vec<String> = Vec::new();

        assert_eq!(vec![Helper::d2(), Helper::f1()], ls(Helper::path_to_d1()).unwrap());
        assert_eq!(vec![Helper::d3(), Helper::f2()], ls(Helper::path_to_d2()).unwrap());
        assert_eq!(empty_vec                       , ls(Helper::path_to_d3()).unwrap());

        Helper::remove_dirs_and_files();
    }

    #[test]
    fn is_empty_dir_should_return_true() {
    }
    #[test]
    fn is_empty_dir_should_return_false() {
    }

    #[test]
    fn walk_dir_should_return_b_tree_set() {
    }

    #[test]
    fn dirs_ordered_by_descending_depth_should_return_vec() {
    }
}
<|endoftext|>