<commit_before>#![feature(plugin)]
#![plugin(regex_macros)]

extern crate argparse;
extern crate html5ever;
extern crate html5ever_dom_sink;
extern crate hyper;
extern crate iron;
extern crate regex;
extern crate rustc_serialize;
extern crate tempfile;
extern crate tendril;
extern crate url;

mod json;

use argparse::ArgumentParser;
use argparse::Store;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
use std::process::Command;
use std::process::Stdio;
use std::str::FromStr;

use html5ever::Attribute;
use html5ever::tree_builder::interface::TreeSink;
use html5ever_dom_sink::common::NodeEnum;
use html5ever_dom_sink::rcdom::Handle;
use html5ever_dom_sink::rcdom::RcDom;
use hyper::Client;
use hyper::header::Connection;
use iron::Iron;
use iron::IronResult;
use iron::Request;
use iron::Response;
use iron::headers::ContentType;
use iron::mime::Mime;
use iron::mime::SubLevel;
use iron::mime::TopLevel;
use rustc_serialize::json::Json;
use tempfile::NamedTempFile;
use url::percent_encoding;

use json::JsonPathElement::{Key, Only};

// TODO: consider doing s/en.wikipedia.org/this app's url/ on the HTML before serving it. This
// currently works fine, but might not over HTTPS.
// TODO: Do nested functions work? If so, use those where appropriate.
// TODO: there are some places where I've handrolled try!() equivalents. Fix those.
// TODO: make sure I'm returning Results everywhere, and propagating errors correctly. Remove all
// uses of unwrap() that might panic.
// TODO: The page "Battle_of_Palo_Alto" is truncated. Figure out the best way to debug.

// TODO: return a Result
fn call_wikimedia_api(parameters: Vec<(&str, &str)>) -> String {
    let post_body =
        parameters.into_iter().map(|p| format!("{}={}", p.0, p.1))
        .collect::<Vec<_>>().join("&") + "&format=json";

    let client = Client::new();
    let mut response = client.post("https://en.wikipedia.org/w/api.php")
        .body(&post_body)
        .header(Connection::close())
        .send().unwrap();

    let mut body = String::new();
    response.read_to_string(&mut body).unwrap();
    body
}

// TODO: this name is terrible.
// Returns pairs of (revid, parentid)
fn get_revert_revision_ids(page: &str) -> Result<Vec<(u64, u64)>, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", page),
             ("rvprop", "comment|ids"), ("rvlimit", "60")]);

    let json = Json::from_str(&json_str).unwrap();
    let revisions = try!(
        json::get_json_array(&json, &[Key("query"), Key("pages"), Only, Key("revisions")]));

    // Filter for revisions that mention vandalism, and that have revid and parentid (which they all
    // should, but merits checking).
    Ok(revisions.into_iter().filter(|revision| {
        match (json::get_json_string(revision, &[Key("comment")]),
               json::get_json_number(revision, &[Key("revid")]),
               json::get_json_number(revision, &[Key("parentid")])) {
            (Ok(ref comment), Ok(_), Ok(_)) => comment.contains("vandal"),
            _ => false,
        }})
       .map(|revision| {
           (json::get_json_number(revision, &[Key("revid")]).unwrap(),
            json::get_json_number(revision, &[Key("parentid")]).unwrap())
       }).collect())
}

fn get_latest_revision_id(page: &str) -> Result<u64, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", page), ("rvprop", "ids"),
             ("rvlimit", "1")]);
    let json = Json::from_str(&json_str).unwrap();
    json::get_json_number(
        &json, &[Key("query"), Key("pages"), Only, Key("revisions"), Only, Key("revid")])
}

fn get_revision_content(page: &str, id: u64) -> Result<String, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", page), ("rvprop", "content"),
             ("rvlimit", "1"), ("rvstartid", &id.to_string())]);
    let json = Json::from_str(&json_str).unwrap();
    match json::get_json_string(
        &json, &[Key("query"), Key("pages"), Only, Key("revisions"), Only, Key("*")]) {
        Ok(content) => Ok(content.to_string()),
        Err(msg) => Err(msg),
    }
}

fn get_canonical_page_title(page: &str) -> Result<String, String> {
    let latest_revision_id = get_latest_revision_id(page).unwrap();
    let page_contents = get_revision_content(page, latest_revision_id).unwrap();

    let regex = regex!(r"#REDIRECT \[\[([^]]+)\]\].*");
    match regex.captures(&page_contents) {
        Some(captures) => get_canonical_page_title(captures.at(1).unwrap()),
        None => {
            println!("Canonical page title is \"{}\"", page);
            Ok(page.to_string())
        },
    }
}

fn write_to_temp_file(contents: &str) -> NamedTempFile {
    let tempfile = NamedTempFile::new().unwrap();
    let mut file = OpenOptions::new().write(true).open(tempfile.path()).unwrap();
    file.write_all(contents.as_bytes()).unwrap();
    file.flush().unwrap();
    tempfile
}

// TODO: I'm not so sure these parameter names aren't terrible.
fn merge(old: &str, new1: &str, new2: &str) -> Option<String> {
    let old_tempfile = write_to_temp_file(old);
    let new1_tempfile = write_to_temp_file(new1);
    let new2_tempfile = write_to_temp_file(new2);

    let mut process = Command::new("diff3");
    process.arg("-m").args(&[new1_tempfile.path(), old_tempfile.path(), new2_tempfile.path()])
        .stdout(Stdio::piped()).stderr(Stdio::null());
    let output = process.output().unwrap();
    if output.status.success() {
        Some(String::from_utf8(output.stdout).unwrap())
    } else {
        None
    }
}

fn render(page: &str, wikitext: &str) -> Result<String, String> {
    let encoded_wikitext =
        percent_encoding::percent_encode(wikitext.as_bytes(), percent_encoding::QUERY_ENCODE_SET);
    let html = call_wikimedia_api(
        vec![("action", "parse"), ("prop", "text"), ("disablepp", ""), ("contentmodel", "wikitext"),
             ("title", page), ("text", &encoded_wikitext)]);
    // TODO: check return value
    let json = Json::from_str(&html).unwrap();
    match json::get_json_string(&json, &[Key("parse"), Key("text"), Key("*")]) {
        Ok(contents) => Ok(contents.to_string()),
        Err(message) => Err(message),
    }
}

fn has_matching_id(attributes: &Vec<Attribute>, id: &str) -> bool {
    // TODO: could also do this with filter() and is_empty(). Not sure if that would be better.
    for attribute in attributes {
        // TODO: do I seriously have to construct a StrTendril here?
        // There has to be a better way.
        if attribute.name.local.as_slice() == "id" &&
            attribute.value == tendril::StrTendril::from_str(id).unwrap() {
                return true;
            }
    }
    return false;
}

fn find_node_by_id(handle: &Handle, id: &str) -> Result<Handle, String> {
    let node = handle.borrow();
    match node.node {
        NodeEnum::Element(_, ref attributes) if has_matching_id(attributes, id) => Ok(handle.clone()),
        _ => {
            for child in &node.children {
                match find_node_by_id(child, id) {
                    // TODO: this looks weird. Is this an abnormal way to do things?
                    Ok(node) => return Ok(node),
                    _ => continue,
                }
            }
            Err(format!("No node with ID {} found", id))
        },
    }
}

fn replace_node_with_placeholder(original_html: &str, div_id: &str, placeholder: &str) -> Result<String, String> {
    // TODO: check errors
    let html = tendril::StrTendril::from_str(original_html).unwrap();
    let mut dom: RcDom = html5ever::parse(html5ever::one_input(html), Default::default());

    let handle = try!(find_node_by_id(&dom.get_document(), div_id));
    let child_handles =
        (&handle.borrow().children).into_iter().map(|child| child.clone()).collect::<Vec<_>>();
    for child_handle in child_handles {
        dom.remove_from_parent(child_handle);
    }
    dom.append(handle,
               html5ever::tree_builder::interface::NodeOrText::AppendText(
                   tendril::StrTendril::from_str(placeholder).unwrap()));
    let mut serialized: Vec<u8> = vec![];
    html5ever::serialize::serialize(&mut serialized, &dom.document, Default::default());
    // TODO: error handling
    Ok(String::from_utf8(serialized).unwrap())
}

fn get_current_page(page: &str) -> String {
    let client = Client::new();
    let mut res = client.get(
        &format!("https://en.wikipedia.org/wiki/{}", page))
        .header(Connection::close())
        .send().unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    body
}

// TODO: rename "page" to "title" everywhere
// TODO: this name is terrible.
fn get_page_with_vandalism_restored(page: &str) -> Result<String, String> {
    let canonicalized_page = get_canonical_page_title(page).unwrap();

    let latest_revid = get_latest_revision_id(&canonicalized_page).unwrap();
    let mut accumulated_contents = get_revision_content(&canonicalized_page, latest_revid).unwrap();
    let mut revisions = vec![];
    for (revert_revid, vandalism_revid) in get_revert_revision_ids(&canonicalized_page).ok().unwrap() {
        let reverted_contents = get_revision_content(&canonicalized_page, revert_revid).unwrap();
        let vandalized_contents = get_revision_content(&canonicalized_page, vandalism_revid).unwrap();
        match merge(&reverted_contents, &vandalized_contents, &accumulated_contents) {
            Some(merged_contents) => {
                // TODO: replace this with a log statement, if there's a good logging framework.
                println!(
                    "For page \"{}\", restored vandalism https://en.wikipedia.org/w/index.php?title={}&diff=prev&oldid={}",
                         &page, &canonicalized_page, revert_revid);
                accumulated_contents = merged_contents;
                revisions.push(revert_revid);
            }
            None => (),
        }
    }

    // TODO: replace this with a log statement, if there's a good logging framework.
    println!("For page \"{}\", restored vandalisms reverted in: {:?}", &page, revisions);

    let rendered_body = render(page, &accumulated_contents).unwrap();
    let current_page_contents = get_current_page(&page); // Note: "page" rather than "canonicalized_page"
    // TODO: randomize the placeholder string per-request
    let page_contents_with_placeholder =
        replace_node_with_placeholder(
            &current_page_contents, "mw-content-text", "WMW_PLACEHOLDER_TEXT").unwrap();
    Ok(page_contents_with_placeholder.replace("WMW_PLACEHOLDER_TEXT", &rendered_body))
}

fn serve_request(request: &mut Request) -> IronResult<Response> {
    if request.url.path.len() == 2 && request.url.path[0] == "wiki" {
        let mut response = Response::with(
            (iron::status::Ok, get_page_with_vandalism_restored(&request.url.path[1]).unwrap()));
        response.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
        Ok(response)
    } else {
        let client = Client::new();
        // TODO: error handling
        let mut wikipedia_response =
            // TODO: not good enough. Needs to include query string.
            client.get(&format!("https://en.wikipedia.org/{}", request.url.path.join("/")))
            .header(Connection::close())
            .send().unwrap();
        let mut wikipedia_body: Vec<u8> = Vec::new();
        wikipedia_response.read_to_end(&mut wikipedia_body);

        let mut response = Response::with(wikipedia_body);
        response.status = Some(wikipedia_response.status);
        response.headers = wikipedia_response.headers.clone();
        //response.body = wikipedia_body;
        println!("Forwarded request for {} to en.wikipedia.org", request.url.path.join("/"));
        Ok(response)
    }
}

fn main() {
    let mut port = 3000;
    {
        let mut parser = ArgumentParser::new();
        parser.set_description("TODO: Usage description");
        parser.refer(&mut port).add_option(&["-p", "--port"], Store, "The port to serve HTTP on.");
        parser.parse_args_or_exit();
    }
    Iron::new(serve_request).http(("localhost", port)).unwrap();
}

#[cfg(test)]
mod tests {
    use super::{merge, replace_node_with_placeholder};

    #[test]
    fn test_merge_clean() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed.\n";
        let new2 = "First line changed.\n\nSecond line.\n";
        assert_eq!("First line changed.\n\nSecond line changed.\n", merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_merge_conflicting() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed one way.\n";
        let new2 = "First line changed.\n\nSecond line changed a different way.\n";
        assert_eq!(None, merge(old, new1, new2));
    }

    #[test]
    fn test_merge_special_characters() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line 𐅃.\n";
        let new2 = "First line さようなら.\n\nSecond line.\n";
        assert_eq!("First line さようなら.\n\nSecond line 𐅃.\n", merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_replace_html_content() {
        let original_html = "

original text

Other text

";
        let expected_html = "

replaced text

Other text

";
        let processed_html = replace_node_with_placeholder(original_html, "mw-content-text", "replaced text").unwrap();
        assert_eq!(expected_html, processed_html);
    }
}
<commit_msg>Satisfy a TODO: Rename "page" parameters to "title".<commit_after>#![feature(plugin)]
#![plugin(regex_macros)]

extern crate argparse;
extern crate html5ever;
extern crate html5ever_dom_sink;
extern crate hyper;
extern crate iron;
extern crate regex;
extern crate rustc_serialize;
extern crate tempfile;
extern crate tendril;
extern crate url;

mod json;

use argparse::ArgumentParser;
use argparse::Store;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
use std::process::Command;
use std::process::Stdio;
use std::str::FromStr;

use html5ever::Attribute;
use html5ever::tree_builder::interface::TreeSink;
use html5ever_dom_sink::common::NodeEnum;
use html5ever_dom_sink::rcdom::Handle;
use html5ever_dom_sink::rcdom::RcDom;
use hyper::Client;
use hyper::header::Connection;
use iron::Iron;
use iron::IronResult;
use iron::Request;
use iron::Response;
use iron::headers::ContentType;
use iron::mime::Mime;
use iron::mime::SubLevel;
use iron::mime::TopLevel;
use rustc_serialize::json::Json;
use tempfile::NamedTempFile;
use url::percent_encoding;

use json::JsonPathElement::{Key, Only};

// TODO: consider doing s/en.wikipedia.org/this app's url/ on the HTML before serving it. This
// currently works fine, but might not over HTTPS.
// TODO: Do nested functions work? If so, use those where appropriate.
// TODO: there are some places where I've handrolled try!() equivalents. Fix those.
// TODO: make sure I'm returning Results everywhere, and propagating errors correctly. Remove all
// uses of unwrap() that might panic.
// TODO: The page "Battle_of_Palo_Alto" is truncated. Figure out the best way to debug.

// TODO: return a Result
fn call_wikimedia_api(parameters: Vec<(&str, &str)>) -> String {
    let post_body =
        parameters.into_iter().map(|p| format!("{}={}", p.0, p.1))
        .collect::<Vec<_>>().join("&") + "&format=json";

    let client = Client::new();
    let mut response = client.post("https://en.wikipedia.org/w/api.php")
        .body(&post_body)
        .header(Connection::close())
        .send().unwrap();

    let mut body = String::new();
    response.read_to_string(&mut body).unwrap();
    body
}

// TODO: this name is terrible.
// Returns pairs of (revid, parentid)
fn get_revert_revision_ids(title: &str) -> Result<Vec<(u64, u64)>, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", title),
             ("rvprop", "comment|ids"), ("rvlimit", "60")]);

    let json = Json::from_str(&json_str).unwrap();
    let revisions = try!(
        json::get_json_array(&json, &[Key("query"), Key("pages"), Only, Key("revisions")]));

    // Filter for revisions that mention vandalism, and that have revid and parentid (which they all
    // should, but merits checking).
    Ok(revisions.into_iter().filter(|revision| {
        match (json::get_json_string(revision, &[Key("comment")]),
               json::get_json_number(revision, &[Key("revid")]),
               json::get_json_number(revision, &[Key("parentid")])) {
            (Ok(ref comment), Ok(_), Ok(_)) => comment.contains("vandal"),
            _ => false,
        }})
       .map(|revision| {
           (json::get_json_number(revision, &[Key("revid")]).unwrap(),
            json::get_json_number(revision, &[Key("parentid")]).unwrap())
       }).collect())
}

fn get_latest_revision_id(title: &str) -> Result<u64, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", title), ("rvprop", "ids"),
             ("rvlimit", "1")]);
    let json = Json::from_str(&json_str).unwrap();
    json::get_json_number(
        &json, &[Key("query"), Key("pages"), Only, Key("revisions"), Only, Key("revid")])
}

fn get_revision_content(title: &str, id: u64) -> Result<String, String> {
    let json_str = call_wikimedia_api(
        vec![("action", "query"), ("prop", "revisions"), ("titles", title), ("rvprop", "content"),
             ("rvlimit", "1"), ("rvstartid", &id.to_string())]);
    let json = Json::from_str(&json_str).unwrap();
    match json::get_json_string(
        &json, &[Key("query"), Key("pages"), Only, Key("revisions"), Only, Key("*")]) {
        Ok(content) => Ok(content.to_string()),
        Err(msg) => Err(msg),
    }
}

fn get_canonical_title(title: &str) -> Result<String, String> {
    let latest_revision_id = get_latest_revision_id(title).unwrap();
    let page_contents = get_revision_content(title, latest_revision_id).unwrap();

    let regex = regex!(r"#REDIRECT \[\[([^]]+)\]\].*");
    match regex.captures(&page_contents) {
        Some(captures) => get_canonical_title(captures.at(1).unwrap()),
        None => {
            println!("Canonical page title is \"{}\"", title);
            Ok(title.to_string())
        },
    }
}

fn write_to_temp_file(contents: &str) -> NamedTempFile {
    let tempfile = NamedTempFile::new().unwrap();
    let mut file = OpenOptions::new().write(true).open(tempfile.path()).unwrap();
    file.write_all(contents.as_bytes()).unwrap();
    file.flush().unwrap();
    tempfile
}

// TODO: I'm not so sure these parameter names aren't terrible.
fn merge(old: &str, new1: &str, new2: &str) -> Option<String> {
    let old_tempfile = write_to_temp_file(old);
    let new1_tempfile = write_to_temp_file(new1);
    let new2_tempfile = write_to_temp_file(new2);

    let mut process = Command::new("diff3");
    process.arg("-m").args(&[new1_tempfile.path(), old_tempfile.path(), new2_tempfile.path()])
        .stdout(Stdio::piped()).stderr(Stdio::null());
    let output = process.output().unwrap();
    if output.status.success() {
        Some(String::from_utf8(output.stdout).unwrap())
    } else {
        None
    }
}

fn render(title: &str, wikitext: &str) -> Result<String, String> {
    let encoded_wikitext =
        percent_encoding::percent_encode(wikitext.as_bytes(), percent_encoding::QUERY_ENCODE_SET);
    let html = call_wikimedia_api(
        vec![("action", "parse"), ("prop", "text"), ("disablepp", ""), ("contentmodel", "wikitext"),
             ("title", title), ("text", &encoded_wikitext)]);
    // TODO: check return value
    let json = Json::from_str(&html).unwrap();
    match json::get_json_string(&json, &[Key("parse"), Key("text"), Key("*")]) {
        Ok(contents) => Ok(contents.to_string()),
        Err(message) => Err(message),
    }
}

fn has_matching_id(attributes: &Vec<Attribute>, id: &str) -> bool {
    // TODO: could also do this with filter() and is_empty(). Not sure if that would be better.
    for attribute in attributes {
        // TODO: do I seriously have to construct a StrTendril here?
        // There has to be a better way.
        if attribute.name.local.as_slice() == "id" &&
            attribute.value == tendril::StrTendril::from_str(id).unwrap() {
                return true;
            }
    }
    return false;
}

fn find_node_by_id(handle: &Handle, id: &str) -> Result<Handle, String> {
    let node = handle.borrow();
    match node.node {
        NodeEnum::Element(_, ref attributes) if has_matching_id(attributes, id) => Ok(handle.clone()),
        _ => {
            for child in &node.children {
                match find_node_by_id(child, id) {
                    // TODO: this looks weird. Is this an abnormal way to do things?
                    Ok(node) => return Ok(node),
                    _ => continue,
                }
            }
            Err(format!("No node with ID {} found", id))
        },
    }
}

fn replace_node_with_placeholder(original_html: &str, div_id: &str, placeholder: &str) -> Result<String, String> {
    // TODO: check errors
    let html = tendril::StrTendril::from_str(original_html).unwrap();
    let mut dom: RcDom = html5ever::parse(html5ever::one_input(html), Default::default());

    let handle = try!(find_node_by_id(&dom.get_document(), div_id));
    let child_handles =
        (&handle.borrow().children).into_iter().map(|child| child.clone()).collect::<Vec<_>>();
    for child_handle in child_handles {
        dom.remove_from_parent(child_handle);
    }
    dom.append(handle,
               html5ever::tree_builder::interface::NodeOrText::AppendText(
                   tendril::StrTendril::from_str(placeholder).unwrap()));
    let mut serialized: Vec<u8> = vec![];
    html5ever::serialize::serialize(&mut serialized, &dom.document, Default::default());
    // TODO: error handling
    Ok(String::from_utf8(serialized).unwrap())
}

fn get_current_page(title: &str) -> String {
    let client = Client::new();
    let mut res = client.get(
        &format!("https://en.wikipedia.org/wiki/{}", title))
        .header(Connection::close())
        .send().unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    body
}

// TODO: this name is terrible.
fn get_page_with_vandalism_restored(title: &str) -> Result<String, String> {
    let canonicalized_title = get_canonical_title(title).unwrap();

    let latest_revid = get_latest_revision_id(&canonicalized_title).unwrap();
    let mut accumulated_contents = get_revision_content(&canonicalized_title, latest_revid).unwrap();
    let mut revisions = vec![];
    for (revert_revid, vandalism_revid) in get_revert_revision_ids(&canonicalized_title).ok().unwrap() {
        let reverted_contents = get_revision_content(&canonicalized_title, revert_revid).unwrap();
        let vandalized_contents = get_revision_content(&canonicalized_title, vandalism_revid).unwrap();
        match merge(&reverted_contents, &vandalized_contents, &accumulated_contents) {
            Some(merged_contents) => {
                // TODO: replace this with a log statement, if there's a good logging framework.
                println!(
                    "For page \"{}\", restored vandalism https://en.wikipedia.org/w/index.php?title={}&diff=prev&oldid={}",
                         &title, &canonicalized_title, revert_revid);
                accumulated_contents = merged_contents;
                revisions.push(revert_revid);
            }
            None => (),
        }
    }

    // TODO: replace this with a log statement, if there's a good logging framework.
    println!("For page \"{}\", restored vandalisms reverted in: {:?}", &title, revisions);

    let rendered_body = render(title, &accumulated_contents).unwrap();
    // Note: "title" rather than "canonicalized_title", so that redirects look right.
    let current_page_contents = get_current_page(&title);
    // TODO: randomize the placeholder string per-request
    let page_contents_with_placeholder =
        replace_node_with_placeholder(
            &current_page_contents, "mw-content-text", "WMW_PLACEHOLDER_TEXT").unwrap();
    Ok(page_contents_with_placeholder.replace("WMW_PLACEHOLDER_TEXT", &rendered_body))
}

fn serve_request(request: &mut Request) -> IronResult<Response> {
    if request.url.path.len() == 2 && request.url.path[0] == "wiki" {
        let mut response = Response::with(
            (iron::status::Ok, get_page_with_vandalism_restored(&request.url.path[1]).unwrap()));
        response.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
        Ok(response)
    } else {
        let client = Client::new();
        // TODO: error handling
        let mut wikipedia_response =
            // TODO: not good enough. Needs to include query string.
            client.get(&format!("https://en.wikipedia.org/{}", request.url.path.join("/")))
            .header(Connection::close())
            .send().unwrap();
        let mut wikipedia_body: Vec<u8> = Vec::new();
        wikipedia_response.read_to_end(&mut wikipedia_body);

        let mut response = Response::with(wikipedia_body);
        response.status = Some(wikipedia_response.status);
        response.headers = wikipedia_response.headers.clone();
        //response.body = wikipedia_body;
        println!("Forwarded request for {} to en.wikipedia.org", request.url.path.join("/"));
        Ok(response)
    }
}

fn main() {
    let mut port = 3000;
    {
        let mut parser = ArgumentParser::new();
        parser.set_description("TODO: Usage description");
        parser.refer(&mut port).add_option(&["-p", "--port"], Store, "The port to serve HTTP on.");
        parser.parse_args_or_exit();
    }
    Iron::new(serve_request).http(("localhost", port)).unwrap();
}

#[cfg(test)]
mod tests {
    use super::{merge, replace_node_with_placeholder};

    #[test]
    fn test_merge_clean() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed.\n";
        let new2 = "First line changed.\n\nSecond line.\n";
        assert_eq!("First line changed.\n\nSecond line changed.\n", merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_merge_conflicting() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed one way.\n";
        let new2 = "First line changed.\n\nSecond line changed a different way.\n";
        assert_eq!(None, merge(old, new1, new2));
    }

    #[test]
    fn test_merge_special_characters() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line 𐅃.\n";
        let new2 = "First line さようなら.\n\nSecond line.\n";
        assert_eq!("First line さようなら.\n\nSecond line 𐅃.\n", merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_replace_html_content() {
        let original_html = "

original text

Other text

";
        let expected_html = "

replaced text

Other text

";
        let processed_html = replace_node_with_placeholder(original_html, "mw-content-text", "replaced text").unwrap();
        assert_eq!(expected_html, processed_html);
    }
}
<|endoftext|>


<commit_before>#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use]
extern crate serde_derive;

extern crate chrono;
extern crate ureq;
extern crate serde_json;

#[macro_use]
extern crate rocket;
extern crate rocket_contrib;

use chrono::Datelike;
use chrono::{NaiveDate as Date, Utc};
use rocket::Outcome;
use rocket_contrib::templates::Template;
use std::collections::BTreeSet;
use std::env;
use std::vec::Vec;

static DEBITOOR_TOKEN: &'static str = "DEBITOOR_TOKEN";

fn default_description() -> String {
    "<empty>".to_string()
}

#[derive(Serialize, Deserialize, Debug)]
struct Expense {
    date: String,
    lines: Vec<Line>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Line {
    #[serde(rename = "categoryType")]
    category_type: Option<String>,
    #[serde(rename = "netAmount")]
    net_amount: f64,
    #[serde(default = "default_description")]
    description: String,
    #[serde(rename = "assetDepreciation")]
    #[serde(default = "Vec::new")]
    asset_depreciation: Vec<AssetDepreciation>,
}

#[derive(Serialize, Deserialize, Debug)]
struct AssetDepreciation {
    #[serde(rename = "depreciationCost")]
    depreciation_cost: f64,
    #[serde(rename = "depreciationDate")]
    depreciation_date: Date,
    #[serde(rename = "bookValue")]
    book_value: f64,
}

#[derive(Deserialize, Debug)]
struct AccessToken {
    access_token: String,
}

impl<'a, 'r> rocket::request::FromRequest<'a, 'r> for AccessToken {
    type Error = ();

    fn from_request(
        request: &'a rocket::request::Request<'r>,
    ) -> rocket::request::Outcome<Self, Self::Error> {
        let token = request
            .cookies()
            .get(DEBITOOR_TOKEN)
            .map(|c| c.value().to_owned());

        match token {
            None => Outcome::Forward(()),
            Some(token) => Outcome::Success(AccessToken {
                access_token: token,
            }),
        }
    }
}

#[derive(Deserialize, Debug)]
struct BaseURL {
    base_url: String,
}

impl<'a, 'r> rocket::request::FromRequest<'a, 'r> for BaseURL {
    type Error = ();

    fn from_request(
        request: &'a rocket::request::Request<'r>,
    ) -> rocket::request::Outcome<Self, Self::Error> {
        println!("Header: {:?}", request.headers());

        let schema = request
            .headers()
            .get_one("X-Forwarded-Proto")
            .unwrap_or("http");
        let host = request.headers().get_one("Host").unwrap();

        Outcome::Success(BaseURL {
            base_url: format!("{}://{}", schema, host),
        })
    }
}

#[get("/?<code>", rank = 1)]
fn check_code(
    mut cookies: rocket::http::Cookies,
    code: String,
    base_url: BaseURL,
) -> rocket::response::Redirect {
    let client_secret = env::var("CLIENT_SECRET").unwrap();

    println!("got code {:?}", code);

    let body = format!(
        "code={}&client_secret={}&redirect_uri={}",
        code, client_secret, base_url.base_url
    );

    println!("body {}", body);

    let res = ureq::post("https://app.debitoor.com/login/oauth2/access_token").
        //if we keep the connection open the parsing will wait for a minute in between for a timeout
        //don't know why this is, so just disable keep alive for now
        set("Connection", "close").
        set("Content-Type", "application/x-www-form-urlencoded").
        send_string(&body);

    assert_eq!(res.ok(), true);

    let access_token: AccessToken = serde_json::from_reader(res.into_reader()).unwrap();

    println!("{:?}", access_token);

    //set cookie and redirect
    cookies.add(rocket::http::Cookie::new(
        DEBITOOR_TOKEN.to_owned(),
        access_token.access_token.to_owned(),
    ));
    rocket::response::Redirect::temporary("/")
}

#[allow(unused_variables)]
#[get("/", rank = 2)]
fn index(token: AccessToken) -> rocket::response::Redirect {
    rocket::response::Redirect::temporary(uri!(asset_list: year = Utc::now().year()))
}

#[get("/", rank = 3)]
fn redirect_auth() -> rocket::response::Redirect {
    let client_id = env::var("CLIENT_ID").unwrap();

    rocket::response::Redirect::temporary(format!(
        "https://app.debitoor.com/login/oauth2/authorize?client_id={}&response_type=code",
        client_id
    ))
}

#[get("/assets/<year>")]
fn asset_list(token: AccessToken, year: i32) -> Template {
    println!("send request for token {:?}", token);
    let res = ureq::get("https://api.debitoor.com/api/expenses/v4").
        //if we keep the connection open the parsing will wait for a minute in between for a timeout
        //don't know why this is, so just disable keep alive for now
        set("Connection", "close").
        //the access token to authenticate with
        set("x-token", &token.access_token).
        call();
    assert_eq!(res.ok(), true);

    println!("create parser");

    let expenses: Vec<Expense> = serde_json::from_reader(res.into_reader()).unwrap();

    #[derive(Serialize, Deserialize, Debug)]
    struct AssetInformation {
        description: String,
        #[serde(rename = "netAmount")]
        net_amount: f64,
        #[serde(rename = "purchasingDate")]
        purchasing_date: Date,
        #[serde(rename = "depreciationCost")]
        depreciation_cost: f64,
        #[serde(rename = "depreciationDate")]
        depreciation_date: Date,
        #[serde(rename = "bookValuePeriodBegin")]
        book_value_period_begin: f64,
        #[serde(rename = "bookValuePeriodEnd")]
        book_value_period_end: f64,
    }

    #[derive(Serialize, Deserialize, Debug)]
    struct Context {
        year: i32,
        asset_information: Vec<AssetInformation>,
        available_years: BTreeSet<i32>,
        total_depreciation_cost: f64,
        total_book_value: f64,
    }

    let mut asset_information: Vec<AssetInformation> = Vec::new();
    let mut available_years: BTreeSet<i32> = BTreeSet::new();

    println!("printing value");

    let mut total_depreciation_cost = 0.0f64;
    let mut total_book_value = 0.0f64;

    for expense in expenses {
        for line in expense.lines {
            let description = line.description.as_str();

            let asset_deprecations = &line.asset_depreciation;

            if asset_deprecations.is_empty() {
                continue;
            }

            //find the first depreciation data, which is the purchase date
            let purchase_info = asset_deprecations
                .iter()
                .fold(None, |min, x| match min {
                    None => Some(x),
                    Some(y) => Some(if x.depreciation_date < y.depreciation_date {
                        x
                    } else {
                        y
                    }),
                })
                .unwrap();

            for asset_depreciation in asset_deprecations {
                available_years.insert(asset_depreciation.depreciation_date.year());
                if asset_depreciation.depreciation_date.year() == year {
                    total_depreciation_cost += asset_depreciation.depreciation_cost;
                    total_book_value += asset_depreciation.book_value;
                    asset_information.push(AssetInformation {
                        description: description.to_string(),
                        net_amount: line.net_amount,
                        purchasing_date: purchase_info.depreciation_date,
                        depreciation_cost: asset_depreciation.depreciation_cost,
                        depreciation_date: asset_depreciation.depreciation_date,
                        book_value_period_begin: asset_depreciation.book_value
                            + asset_depreciation.depreciation_cost,
                        book_value_period_end: asset_depreciation.book_value,
                    });
                }
            }
        }
    }

    println!("Rendering template");

    Template::render(
        "asset_list",
        Context {
            year,
            asset_information,
            available_years,
            total_depreciation_cost,
            total_book_value,
        },
    )
}

fn main() {
    rocket::ignite()
        .attach(Template::fairing())
        .mount("/", routes![index, asset_list, check_code, redirect_auth])
        .launch();
}
<commit_msg>Access Token exchange may not use Connection Close<commit_after>#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use]
extern crate serde_derive;

extern crate chrono;
extern crate ureq;
extern crate serde_json;

#[macro_use]
extern crate rocket;
extern crate rocket_contrib;

use chrono::Datelike;
use chrono::{NaiveDate as Date, Utc};
use rocket::Outcome;
use rocket_contrib::templates::Template;
use std::collections::BTreeSet;
use std::env;
use std::vec::Vec;

static DEBITOOR_TOKEN: &'static str = "DEBITOOR_TOKEN";

fn default_description() -> String {
    "<empty>".to_string()
}

#[derive(Serialize, Deserialize, Debug)]
struct Expense {
    date: String,
    lines: Vec<Line>,
}

#[derive(Serialize, Deserialize, Debug)]
struct Line {
    #[serde(rename = "categoryType")]
    category_type: Option<String>,
    #[serde(rename = "netAmount")]
    net_amount: f64,
    #[serde(default = "default_description")]
    description: String,
    #[serde(rename = "assetDepreciation")]
    #[serde(default = "Vec::new")]
    asset_depreciation: Vec<AssetDepreciation>,
}

#[derive(Serialize, Deserialize, Debug)]
struct AssetDepreciation {
    #[serde(rename = "depreciationCost")]
    depreciation_cost: f64,
    #[serde(rename = "depreciationDate")]
    depreciation_date: Date,
    #[serde(rename = "bookValue")]
    book_value: f64,
}

#[derive(Deserialize, Debug)]
struct AccessToken {
    access_token: String,
}

impl<'a, 'r> rocket::request::FromRequest<'a, 'r> for AccessToken {
    type Error = ();

    fn from_request(
        request: &'a rocket::request::Request<'r>,
    ) -> rocket::request::Outcome<Self, Self::Error> {
        let token = request
            .cookies()
            .get(DEBITOOR_TOKEN)
            .map(|c| c.value().to_owned());

        match token {
            None => Outcome::Forward(()),
            Some(token) => Outcome::Success(AccessToken {
                access_token: token,
            }),
        }
    }
}

#[derive(Deserialize, Debug)]
struct BaseURL {
    base_url: String,
}

impl<'a, 'r> rocket::request::FromRequest<'a, 'r> for BaseURL {
    type Error = ();

    fn from_request(
        request: &'a rocket::request::Request<'r>,
    ) -> rocket::request::Outcome<Self, Self::Error> {
        println!("Header: {:?}", request.headers());

        let schema = request
            .headers()
            .get_one("X-Forwarded-Proto")
            .unwrap_or("http");
        let host = request.headers().get_one("Host").unwrap();

        Outcome::Success(BaseURL {
            base_url: format!("{}://{}", schema, host),
        })
    }
}

#[get("/?<code>", rank = 1)]
fn check_code(
    mut cookies: rocket::http::Cookies,
    code: String,
    base_url: BaseURL,
) -> rocket::response::Redirect {
    let client_secret = env::var("CLIENT_SECRET").unwrap();

    println!("got code {:?}", code);

    let body = format!(
        "code={}&client_secret={}&redirect_uri={}",
        code, client_secret, base_url.base_url
    );

    println!("body {}", body);

    let res = ureq::post("https://app.debitoor.com/login/oauth2/access_token").
        set("Content-Type", "application/x-www-form-urlencoded").
        send_string(&body);

    assert_eq!(res.ok(), true);

    let access_token: AccessToken = serde_json::from_reader(res.into_reader()).unwrap();

    println!("{:?}", access_token);

    //set cookie and redirect
    cookies.add(rocket::http::Cookie::new(
        DEBITOOR_TOKEN.to_owned(),
        access_token.access_token.to_owned(),
    ));
    rocket::response::Redirect::temporary("/")
}

#[allow(unused_variables)]
#[get("/", rank = 2)]
fn index(token: AccessToken) -> rocket::response::Redirect {
    rocket::response::Redirect::temporary(uri!(asset_list: year = Utc::now().year()))
}

#[get("/", rank = 3)]
fn redirect_auth() -> rocket::response::Redirect {
    let client_id = env::var("CLIENT_ID").unwrap();

    rocket::response::Redirect::temporary(format!(
        "https://app.debitoor.com/login/oauth2/authorize?client_id={}&response_type=code",
        client_id
    ))
}

#[get("/assets/<year>")]
fn asset_list(token: AccessToken, year: i32) -> Template {
    println!("send request for token {:?}", token);
    let res = ureq::get("https://api.debitoor.com/api/expenses/v4").
        //if we keep the connection open the parsing will wait for a minute in between for a timeout
        //don't know why this is, so just disable keep alive for now
        set("Connection", "close").
        //the access token to authenticate with
        set("x-token", &token.access_token).
        call();
    assert_eq!(res.ok(), true);

    println!("create parser");

    let expenses: Vec<Expense> = serde_json::from_reader(res.into_reader()).unwrap();

    #[derive(Serialize, Deserialize, Debug)]
    struct AssetInformation {
        description: String,
        #[serde(rename = "netAmount")]
        net_amount: f64,
        #[serde(rename = "purchasingDate")]
        purchasing_date: Date,
        #[serde(rename = "depreciationCost")]
        depreciation_cost: f64,
        #[serde(rename = "depreciationDate")]
        depreciation_date: Date,
        #[serde(rename = "bookValuePeriodBegin")]
        book_value_period_begin: f64,
        #[serde(rename = "bookValuePeriodEnd")]
        book_value_period_end: f64,
    }

    #[derive(Serialize, Deserialize, Debug)]
    struct Context {
        year: i32,
        asset_information: Vec<AssetInformation>,
        available_years: BTreeSet<i32>,
        total_depreciation_cost: f64,
        total_book_value: f64,
    }

    let mut asset_information: Vec<AssetInformation> = Vec::new();
    let mut available_years: BTreeSet<i32> = BTreeSet::new();

    println!("printing value");

    let mut total_depreciation_cost = 0.0f64;
    let mut total_book_value = 0.0f64;

    for expense in expenses {
        for line in expense.lines {
            let description = line.description.as_str();

            let asset_deprecations = &line.asset_depreciation;

            if asset_deprecations.is_empty() {
                continue;
            }

            //find the first depreciation data, which is the purchase date
            let purchase_info = asset_deprecations
                .iter()
                .fold(None, |min, x| match min {
                    None => Some(x),
                    Some(y) => Some(if x.depreciation_date < y.depreciation_date {
                        x
                    } else {
                        y
                    }),
                })
                .unwrap();

            for asset_depreciation in asset_deprecations {
                available_years.insert(asset_depreciation.depreciation_date.year());
                if asset_depreciation.depreciation_date.year() == year {
                    total_depreciation_cost += asset_depreciation.depreciation_cost;
                    total_book_value += asset_depreciation.book_value;
                    asset_information.push(AssetInformation {
                        description: description.to_string(),
                        net_amount: line.net_amount,
                        purchasing_date: purchase_info.depreciation_date,
                        depreciation_cost: asset_depreciation.depreciation_cost,
                        depreciation_date: asset_depreciation.depreciation_date,
                        book_value_period_begin: asset_depreciation.book_value
                            + asset_depreciation.depreciation_cost,
                        book_value_period_end: asset_depreciation.book_value,
                    });
                }
            }
        }
    }

    println!("Rendering template");

    Template::render(
        "asset_list",
        Context {
            year,
            asset_information,
            available_years,
            total_depreciation_cost,
            total_book_value,
        },
    )
}

fn main() {
    rocket::ignite()
        .attach(Template::fairing())
        .mount("/", routes![index, asset_list, check_code, redirect_auth])
        .launch();
}
<|endoftext|>


<commit_before>#![allow(unstable, dead_code)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;

fn main() {
  let args = match args::parse_args() {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let choices = read_choices();

  let mut screen = Screen::open_screen();
  let mut index = 0;

  let visible_choices = min(20, screen.height - 1);

  let mut search = args.initial_search.clone();
  let start_line = screen.height - visible_choices - 1;
  loop {
    screen.blank_screen(start_line);
    screen.move_cursor(start_line, 0);
    let matches = matching::compute_matches(&choices, search.as_slice());
    let mut i = 1;
    screen.write(format!("> {} ({} choices)\n", search.as_slice(), choices.len()).as_slice());
    for choice in matches.iter() {
      if i == index + 1 {
        screen.write_inverted(choice.as_slice());
      } else {
        screen.write(choice.as_slice());
      }
      if i >= visible_choices as usize {
        break;
      } else {
        screen.write("\n");
      }
      i += 1;
    }

    screen.move_cursor(start_line, 2 + search.len() as u16);

    match screen.tty.getchar() {
      Char(x) => search.push(x),
      Backspace => { search.pop(); }
      Control('h') => { search.pop(); }
      Control('u') => { search.clear(); }
      Control('c') => { return; }
      Control('n') => { index += 1; }
      Control('p') => { index -= 1; }
      Enter => {
        screen.move_cursor(start_line + visible_choices, 0);
        screen.write("\n");
        println!("{}", matches[index]);
        break;
      }
      _ => panic!("Unexpected input"),
    }
  }
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  loop {
    match stdin.read_line() {
      Ok(x) => lines.push(String::from_str(x.as_slice().trim())),
      Err(_) => break,
    }
  }

  lines
}
<commit_msg>Keep the index within bounds<commit_after>#![allow(unstable, dead_code)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;

fn main() {
  let args = match args::parse_args() {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let choices = read_choices();

  let mut screen = Screen::open_screen();
  let mut index = 0;

  let visible_choices = min(20, screen.height - 1);

  let mut search = args.initial_search.clone();
  let start_line = screen.height - visible_choices - 1;
  loop {
    screen.blank_screen(start_line);
    screen.move_cursor(start_line, 0);
    let matches = matching::compute_matches(&choices, search.as_slice());
    let mut i = 1;
    screen.write(format!("> {} ({} choices)\n", search.as_slice(), choices.len()).as_slice());
    for choice in matches.iter() {
      if i == index + 1 {
        screen.write_inverted(choice.as_slice());
      } else {
        screen.write(choice.as_slice());
      }
      if i >= visible_choices as usize {
        break;
      } else {
        screen.write("\n");
      }
      i += 1;
    }

    screen.move_cursor(start_line, 2 + search.len() as u16);

    match screen.tty.getchar() {
      Char(x) => search.push(x),
      Backspace => { search.pop(); }
      Control('h') => { search.pop(); }
      Control('u') => { search.clear(); }
      Control('c') => { return; }
      Control('n') => { index = min(index + 1, matches.len() - 1); }
      Control('p') => { index = if index == 0 { 0 } else { index - 1 }; }
      Enter => {
        screen.move_cursor(start_line + visible_choices, 0);
        screen.write("\n");
        println!("{}", matches[index]);
        break;
      }
      _ => panic!("Unexpected input"),
    }
  }
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  loop {
    match stdin.read_line() {
      Ok(x) => lines.push(String::from_str(x.as_slice().trim())),
      Err(_) => break,
    }
  }

  lines
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate toml;

use std::io::{Read, Write};
use std::thread;

use hyper::Client;

use hyper::Server;
use hyper::server::Request;
use hyper::server::Response;
use hyper::net::Fresh;

fn main() {
    let address = "0.0.0.0:8080";

    println!("Listening on {}", address);
    Server::http(server).listen(address).unwrap();
}

fn server(mut req: Request, res: Response<Fresh>) {
    let mut body = String::new();
    match req.method {
        hyper::Get => {
            let _ = req.read_to_string(&mut body);
            println!("debug1: {:?}", &body);
        },
        _ => { panic!("unhandled") },
    }
    let mut res = res.start().unwrap();
    let symbol_url = get_config("symbol_urls.public");
    let symbol = client(symbol_url);
    let _ = res.write_all(symbol.as_bytes());
    res.end().unwrap();
}

fn client(url: String) -> String {
    let mut handles = vec![];
    for i in 0..5 {
        let this_url = url.clone();
        handles.push(thread::spawn(move || {
            let mut c = Client::new();
            let mut res = c.get(&this_url).send().unwrap();
            let mut body = String::new();
            let _ = res.read_to_string(&mut body);

            (i, body)
        }));
    }

    let mut result = String::new();

    for handle in handles {
        let (i, body) = handle.join().unwrap();
        for c in format!("{:?} {:?}\n", i, body).chars() {
            result.push(c);
        }
    }

    result
}

fn get_config(value_name: &str) -> String {
    let toml = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1/"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<commit_msg>add TODO to adjust threads later<commit_after>extern crate hyper;
extern crate toml;

use std::io::{Read, Write};
use std::thread;

use hyper::Client;

use hyper::Server;
use hyper::server::Request;
use hyper::server::Response;
use hyper::net::Fresh;

fn main() {
    let address = "0.0.0.0:8080";

    println!("Listening on {}", address);
    Server::http(server).listen(address).unwrap();
}

fn server(mut req: Request, res: Response<Fresh>) {
    let mut body = String::new();
    match req.method {
        hyper::Get => {
            let _ = req.read_to_string(&mut body);
            println!("debug1: {:?}", &body);
        },
        _ => { panic!("unhandled") },
    }
    let mut res = res.start().unwrap();
    let symbol_url = get_config("symbol_urls.public");
    let symbol = client(symbol_url);
    let _ = res.write_all(symbol.as_bytes());
    res.end().unwrap();
}

fn client(url: String) -> String {
    let mut handles = vec![];
    // TODO decide smart min/max possible threads
    for i in 0..5 {
        let this_url = url.clone();
        handles.push(thread::spawn(move || {
            let mut c = Client::new();
            let mut res = c.get(&this_url).send().unwrap();
            let mut body = String::new();
            let _ = res.read_to_string(&mut body);

            (i, body)
        }));
    }

    let mut result = String::new();

    for handle in handles {
        let (i, body) = handle.join().unwrap();
        for c in format!("{:?} {:?}\n", i, body).chars() {
            result.push(c);
        }
    }

    result
}

fn get_config(value_name: &str) -> String {
    let toml = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1/"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate term_painter as term;
extern crate byteorder;
extern crate rustc_serialize;
extern crate bincode;
extern crate docopt;

use rustc_serialize::json;
use std::fs::File;
use std::io::Read;
use docopt::Docopt;
use std::net::{Ipv4Addr, SocketAddrV4};

pub mod auth;
pub mod conn;
pub mod logger;
pub mod net;
pub mod parse;
pub mod query;
pub mod storage;

/// For console input, manages flags and arguments
const USAGE: &'static str = "
Usage: uosql-server [--cfg=<file>] [--ip=<address>] [--port=<port>]
[--dir=<directory>]

Options:
    --cfg=<file>        Enter a configuration file.
    --ip=<address>      Change the IP address.
    --port=<port>       Change the port.
    --dir=<directory>   Change the path of the database.
";

#[derive(Debug, RustcDecodable)]
struct Args {
   flag_cfg: Option<String>,
   flag_ip: Option<String>,
   flag_port: Option<u16>,
   flag_dir: Option<String>
}

/// Entry point for server. Allow dead_code to supress warnings when
/// compiled as a library.
#[allow(dead_code)]
fn main() {
    // Configure and enable the logger. We may `unwrap` here, because a panic
    // would happen right after starting the program
    logger::with_loglevel(log::LogLevelFilter::Trace)
        .with_logfile(std::path::Path::new("log.txt"))
        .enable().unwrap();
    info!("Starting uoSQL server...");

    // Getting the information for a possible configuration
    let args : Args = Docopt::new(USAGE).and_then(|d| d.decode())
                                        .unwrap_or_else(|e| e.exit());

    // If a cfg is entered, use this file name to set configurations
    let mut config = read_conf_from_json(args.flag_cfg
                                .unwrap_or("src/config.json".into()));

    // Change the IP address if flag is set
    if let Some(s) = args.flag_ip {
        config.address = string_to_ipv4(s);
    }

    // Change port if flag is set
    if let Some(s) = args.flag_port {
        config.port = s;
    }

    // Change directory is flag is set
    if let Some(s) = args.flag_dir {
        config.dir = s;
    }

    info!("IP: {}  Port: {}  Directory: {}",
                        config.address, config.port, config.dir);

    // Start listening for incoming Tcp connections
    listen(config);
}

/// Listens for incoming TCP streams
fn listen(config: Config) {
    use std::net::TcpListener;
    use std::thread;

    // Converting configurations to a valid socket address
    let sock_addr = SocketAddrV4::new(config.address, config.port);
    let listener = TcpListener::bind(sock_addr).unwrap();

    // Accept connections and process them
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                // Connection succeeded: Spawn thread and handle
                thread::spawn(move|| {
                    conn::handle(stream)
                });
            },
            Err(e) => {
                // Something went wrong...
                warn!("Failed to accept incoming connection: {:?}", e);
            },
        }
    }
}

/// Creates a Config struct out of a config file
/// returns default values for everything that is
/// not entered manually
fn read_conf_from_json(name: String) -> Config {

    #[derive(Debug, RustcDecodable, Default)]
    struct CfgFile {
        address: Option<String>,
        port: Option<u16>,
        dir: Option<String>
    }

    // Read from JSON file and decode to CfgFile
    let mut config = CfgFile::default();
    if let Ok(mut f) = File::open(name) {
        let mut s = String::new();
        if let Err(e) = f.read_to_string(&mut s) {
            error!("Could not read JSON-file: {:?}", e)
        } else {
            config = json::decode(&s).unwrap();
        }
    }

    // Return configuration, all None datafields set to default
    Config {
        address: string_to_ipv4(config.address.unwrap_or("127.0.0.1".into())),
        port: config.port.unwrap_or(4242),
        dir: config.dir.unwrap_or("data".into())
    }
}

/// Converts String into Ipv4Addr
fn string_to_ipv4(s : String) -> Ipv4Addr {
    let ip_parts : Vec<&str> = s.split(".").collect();

    let mut part_convert : Vec<u8> = Vec::default();
    for s in ip_parts {
        match s.parse::<u8>() {
            Ok(n) => part_convert.push(n),
            Err(e) => error!("Parsing error: {:?}", e)
        };
    }
    Ipv4Addr::new(part_convert[0], part_convert[1],
                  part_convert[2], part_convert[3])
}

/// A struct for managing configurations
#[derive(Debug)]
pub struct Config {
    address: Ipv4Addr,
    port: u16,
    dir: String
}
<commit_msg>Change minor problems<commit_after>#[macro_use]
extern crate log;
extern crate term_painter as term;
extern crate byteorder;
extern crate rustc_serialize;
extern crate bincode;
extern crate docopt;

use rustc_serialize::json;
use std::fs::File;
use std::io::Read;
use docopt::Docopt;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::str::FromStr;

pub mod auth;
pub mod conn;
pub mod logger;
pub mod net;
pub mod parse;
pub mod query;
pub mod storage;

/// For console input, manages flags and arguments
const USAGE: &'static str = "
Usage: uosql-server [--cfg=<file>] [--bind=<address>] [--port=<port>]
[--dir=<directory>]

Options:
    --cfg=<file>        Enter a configuration file.
    --bind=<address>    Change the bind address.
    --port=<port>       Change the port.
    --dir=<directory>   Change the path of the database.
";

#[derive(Debug, RustcDecodable)]
struct Args {
   flag_cfg: Option<String>,
   flag_bind: Option<String>,
   flag_port: Option<u16>,
   flag_dir: Option<String>
}

/// Entry point for server. Allow dead_code to supress warnings when
/// compiled as a library.
#[allow(dead_code)]
fn main() {
    // Configure and enable the logger. We may `unwrap` here, because a panic
    // would happen right after starting the program
    logger::with_loglevel(log::LogLevelFilter::Trace)
        .with_logfile(std::path::Path::new("log.txt"))
        .enable().unwrap();
    info!("Starting uoSQL server...");

    // Getting the information for a possible configuration
    let args : Args = Docopt::new(USAGE).and_then(|d| d.decode())
                                        .unwrap_or_else(|e| e.exit());

    // If a cfg is entered, use this file name to set configurations
    let mut config = read_conf_from_json(args.flag_cfg
                                .unwrap_or("src/config.json".into()));

    // Change the bind address if flag is set
    config.address = args.flag_bind.and_then(|b| Ipv4Addr::from_str(&b).ok()).unwrap_or(config.address);

    // Change port if flag is set
    config.port = args.flag_port.unwrap_or(config.port);

    // Change directory is flag is set
    config.dir = args.flag_dir.unwrap_or(config.dir);

    info!("Bind: {}  Port: {}  Directory: {}",
                        config.address, config.port, config.dir);

    // Start listening for incoming Tcp connections
    listen(config);
}

/// Listens for incoming TCP streams
fn listen(config: Config) {
    use std::net::TcpListener;
    use std::thread;

    // Converting configurations to a valid socket address
    let sock_addr = SocketAddrV4::new(config.address, config.port);
    let listener = TcpListener::bind(sock_addr).unwrap();

    // Accept connections and process them
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                // Connection succeeded: Spawn thread and handle
                thread::spawn(move|| {
                    conn::handle(stream)
                });
            },
            Err(e) => {
                // Something went wrong...
                warn!("Failed to accept incoming connection: {:?}", e);
            },
        }
    }
}

/// Creates a Config struct out of a config file
/// returns default values for everything that is
/// not entered manually
fn read_conf_from_json(name: String) -> Config {

    #[derive(Debug, RustcDecodable, Default)]
    struct CfgFile {
        address: Option<String>,
        port: Option<u16>,
        dir: Option<String>
    }

    // Read from JSON file and decode to CfgFile
    let mut config = CfgFile::default();
    if let Ok(mut f) = File::open(name) {
        let mut s = String::new();
        if let Err(e) = f.read_to_string(&mut s) {
            error!("Could not read JSON-file: {:?}", e)
        } else {
            config = json::decode(&s).unwrap();
        }
    }

    let s = config.address.unwrap_or("127.0.0.1".into());
    let bind = match Ipv4Addr::from_str(&s) {
        Ok(n) => n,
        Err(_) => {
            warn!("Invalid bind address, set to default");
            Ipv4Addr::new(127,0,0,1)
        }
    };

    // Return configuration, all None datafields set to default
    Config {
        address: bind,
        port: config.port.unwrap_or(4242),
        dir: config.dir.unwrap_or("data".into())
    }
}

/// A struct for managing configurations
#[derive(Debug)]
pub struct Config {
    address: Ipv4Addr,
    port: u16,
    dir: String
}
<|endoftext|>


<commit_before>#![feature(globs)]
extern crate libc;

use xlib::{XOpenDisplay, XDisplayName, XQueryExtension, XInitExtension, XSynchronize, XKeysymToString, XKeycodeToKeysym};
use libc::{c_int};
use std::ptr;
use xtst::{XRecordCreateContext, XRecordAllClients, XRecordAllocRange, XRecordRange, XRecordQueryVersion, XRecordEnableContext, XRecordEnableContextAsync,XRecordInterceptData, XRecordProcessReplies, XRecordFreeData};
use std::mem;
use std::c_str::CString;
use std::c_vec;
use std::time::Duration;
use std::io::Timer;
mod xtst;
mod xlib;
mod xlibint;
mod xutil;

struct XRecordDatum {
  xtype: ::libc::c_uchar,
  event: ::xlib::XEvent,
  req:   ::xlibint::xResourceReq,
  reply: ::xlibint::xGenericReply,
  error: ::xlibint::xError,
  setup: ::xlibint::xConnSetupPrefix,
}

// let mut display_control: *mut xlib::Display  = std::mem::transmute(0);
// let mut display_data: *mut xlib::Display  = std::mem::transmute(0);
// static mut display_control: *mut xlib::Display = 0 as *mut xlib::Struct__XDisplay;
// static mut display_data: *mut xlib::Display = 0 as *mut xlib::Struct__XDisplay;
static mut display_control: Display<'static> = Display {display: 0 as *mut xlib::Display};
static mut display_data: Display<'static> = Display {display: 0 as *mut xlib::Display};
static mut event_count:u32 = 0;
fn main() {
	unsafe {
		let mut a:  i8 = 0;
		display_control = Display::new();
		display_data = Display::new();

		XSynchronize(display_control.display, 1);

		let display_name = unsafe {XDisplayName(&a)};
		let ext_name = "RECORD";
		// Check presence of Record extension
		let arg2:*mut c_int = &mut 1;
		let arg3:*mut c_int = &mut 1;
		let arg4:*mut c_int = &mut 1;
		let has_record = XQueryExtension(display_control.display, ext_name.to_c_str().as_ptr() as *const i8,arg2,arg3,arg4);
		let extension = XInitExtension(display_control.display, ext_name.to_c_str().as_ptr() as *const i8);
		if extension.is_null() {
			panic!("XInitExtension() failed!");
		}

		// Get version
		let mut versionMajor: c_int = 0;
		let mut versionMinor: c_int = 0;
		XRecordQueryVersion(display_control.display, &mut versionMajor, &mut versionMinor);
		println!("RECORD extension version {}.{}", versionMajor, versionMinor);

		// Prepare record range
		let mut recordRange: XRecordRange = *XRecordAllocRange();
		let mut recordRangePtr: *mut *mut XRecordRange = std::mem::transmute(&mut &mut recordRange);
		recordRange.device_events.first = 2; // KeyPress
		recordRange.device_events.last = 6; // MotionNotify

		// Create context
		let context = XRecordCreateContext(
			display_control.display,
			0,
			&mut XRecordAllClients,
			1,
			recordRangePtr,
			1
		);
		if context == 0 {
			panic!("Fail create Record context\n");
		}

		// Run
		let res = XRecordEnableContextAsync(display_data.display, context, Some(recordCallback), &mut 0);
		if res == 0 {
			panic!("Cound not enable the Record context!\n");
		}
		xtst::XRecordFreeContext(display_data.display, context);

		// without this timer process consume 100% CPU
		let mut timer = Timer::new().unwrap();
		let periodic = timer.periodic(Duration::milliseconds(1000));
		loop {
			periodic.recv();
			XRecordProcessReplies(display_data.display);
		}
	}
}

extern "C" fn recordCallback(pointer:*mut i8, raw_data: *mut XRecordInterceptData) {

	unsafe {

		let data = &*raw_data;
		// println!("Category {}", data.category);
		if data.category != xtst::XRecordFromServer {
			return;
		}
		println!("Event count: {}", event_count);
		event_count += 1;
		println!("Time {}", data.server_time);
		println!("Datalen {}", data.data_len);
		let mut xdatum_ptr: *mut XRecordDatum = data.data as *mut XRecordDatum;
		let mut xdatum = &*xdatum_ptr;

		let mut event = xdatum.event;
		println!("Type {}", xdatum.xtype);

		// Catch key event
		if xdatum.xtype == xtst::KeyPress || xdatum.xtype == xtst::KeyRelease {
			// extract key code
			let raw_bytes: &mut [u8,..4] = std::mem::transmute(data.data);
			let c_char  = XKeysymToString(XKeycodeToKeysym(display_control.display, raw_bytes[1], 0));
			let c_string = CString::new(std::mem::transmute(c_char), false);
			let char: &str = c_string.as_str().unwrap();
			println!("Keycode: {}, Character {}", raw_bytes[1], char);
		}

		let mut current_window = display_control.get_input_focus();
		let mut parent_window: Option<Window> = None;
		let mut wm_name_str: Option<String> = None;

		let mut i = 0u;
		while i < 10 {
			if current_window.id == 0 {
				break;
			}

			wm_name_str = current_window.get_wm_name();
			match wm_name_str {
				None => {
					let tree = current_window.get_tree();
					parent_window = match tree {
						Some(WindowTree{parent: parent, children: _}) => {
							Some(parent)
						},
						_ => None
					}

				},
				Some(ref wmname) => {
					// wm_name_str = Some(wmname);
					break;
				}
			}

			current_window = match parent_window {
				Some(win) => win,
				_ => current_window
			};

			i += 1;
		}
		match wm_name_str {
			Some(ref name) => {
				println!("WM_NAME: {}", *name);
			},
			None => {
				println!("WM_NAME: none");
			}
		}

		XRecordFreeData(raw_data);
	}
	println!("\n");
}

// ============================================================================
// Simple naive wrappers around X stuff
// ============================================================================
struct Display<'a> {
    display: *mut xlib::Display,
}

impl<'a> Display<'a> {
	fn new() -> Display<'a> {
		Display {display: unsafe {
			let mut a:  i8 = 0;
			let dpy = XOpenDisplay(&a);
			if dpy.is_null() {
				panic!("XOpenDisplay() failed!");
			}
			dpy
		}}
	}
	fn get_input_focus(&self) -> Window{
		let current_window: *mut xlib::Window = &mut 0;
		let revert_to_return: *mut i32 = &mut 0;
		unsafe{xlib::XGetInputFocus(self.display, current_window, revert_to_return)};
		Window {id: unsafe{*current_window}, display: self.display}
	}
}

struct Window<'a> {
    id: u64, // XID
    display: *mut xlib::Display
}

impl<'a> Window<'a> {
	fn get_wm_name(&self) -> Option<String> {
		// let mut a:String = String::new();
		// let wmname = unsafe {
			// let mut window_name: *mut i8 = 0 as *mut i8;
			// let res = xlib::XFetchName(self.display, self.id, &mut window_name);
			// if res != 0 {
			// 	let c_wm_name = CString::new(std::mem::transmute(window_name), false);
			// 	// xlib::XFree(&mut window_name);
			// 	Some(String::from_str(c_wm_name.as_str().unwrap()))
			// } else {
			// 	// Try get _NET_WM_NAME
			// 	None
			// }
			let wmname_c = self.get_property("_NET_WM_NAME", "UTF8_STRING");
			match wmname_c {
				Some(bytes) => match String::from_utf8(bytes){
					Ok(value) => Some(value),
					Err(err) => {println!("Error: {}", err); None}
				},
				None => None
			}
		// };
		// wmname
	}
	fn get_property(&self, property_name: &str, property_type: &str) -> Option<Vec<u8>>{
		unsafe {
			let xa_property_type: xlibint::Atom = xlib::XInternAtom(self.display, property_type.to_c_str().as_ptr(), 0);
			let xa_property_name: xlibint::Atom = xlib::XInternAtom(self.display, property_name.to_c_str().as_ptr(), 0);
			let mut actual_type_return  : xlibint::Atom     = 0;
			let mut actual_format_return: libc::c_int       = 0;
			let mut nitems_return       : libc::c_ulong     = 0;
			let mut bytes_after_return  : libc::c_ulong     = 0;
			let mut tmp                 : libc::c_uchar     = 0u8;
			let mut prop_return         : *mut libc::c_uchar = mem::transmute(&mut tmp);
			let res = xlib::XGetWindowProperty(
				self.display,
				self.id,
				xa_property_name,
				0,
				4096 / 4,
				0,
				xa_property_type,
				&mut actual_type_return,
				&mut actual_format_return,
				&mut nitems_return,
				&mut bytes_after_return,
				&mut prop_return
				);
			if (xa_property_type != actual_type_return) {
				println!("Invalid type of {} property", property_name);
				return None;
			}
			let tmp_size = ((actual_format_return as u64) / 8) * nitems_return;

			let data = c_vec::CVec::new(prop_return, tmp_size as uint);
			let mut copy_data = Vec::with_capacity(tmp_size as uint);
			for b in data.as_slice().iter() {
				copy_data.push(*b);
			}

			xlib::XFree(prop_return as *mut libc::types::common::c95::c_void);

			Some(copy_data)
		}
	}

	fn get_tree (&self) -> Option<WindowTree> {
		unsafe {
			let mut root: xlib::Window = 0;
			let mut parent: xlib::Window = 0;
			let mut children: *mut xlib::Window = &mut 0u64;
			let mut nchildren: u32 = 0;

			let res = xlib::XQueryTree(
				self.display,
				self.id,
				&mut root,
				&mut parent,
				&mut children,
				&mut nchildren);

			match res {
				0 => None,
				_ => {
					let childs = match nchildren {
						0 => None,
						_ => {
							// let c = std::c_vec::CVec::new(children, nchildren);
							let mut b: Vec<Window> = Vec::new();
							for i in range(0, nchildren as int){
								b.push(Window{
									id: *children.offset(i),
									display: self.display
								});
							}
							Some(b)
						}
					};

					Some(WindowTree {
						parent: Window{
							id: parent,
							display: self.display,
						},
						children: childs
					})
				}
			}
		}
	}
}

struct WindowTree<'a> {
    parent: Window<'a>,
    children: Option<Vec<Window<'a>>>,
}<commit_msg>clean up<commit_after>#![feature(globs)]
extern crate libc;

use xlib::{XOpenDisplay, XDisplayName, XQueryExtension, XInitExtension, XSynchronize, XKeysymToString, XKeycodeToKeysym};
use libc::{c_int};
use std::ptr;
use xtst::{XRecordCreateContext, XRecordAllClients, XRecordAllocRange, XRecordRange, XRecordQueryVersion, XRecordEnableContext, XRecordEnableContextAsync,XRecordInterceptData, XRecordProcessReplies, XRecordFreeData};
use std::mem;
use std::c_str::CString;
use std::c_vec;
use std::time::Duration;
use std::io::Timer;
mod xtst;
mod xlib;
mod xlibint;
mod xutil;

struct XRecordDatum {
  xtype: ::libc::c_uchar,
  event: ::xlib::XEvent,
  req:   ::xlibint::xResourceReq,
  reply: ::xlibint::xGenericReply,
  error: ::xlibint::xError,
  setup: ::xlibint::xConnSetupPrefix,
}

static mut display_control: Display<'static> = Display {display: 0 as *mut xlib::Display};
static mut display_data: Display<'static> = Display {display: 0 as *mut xlib::Display};
static mut event_count:u32 = 0;
fn main() {
	unsafe {
		let mut a:  i8 = 0;
		display_control = Display::new();
		display_data = Display::new();

		XSynchronize(display_control.display, 1);

		let display_name = unsafe {XDisplayName(&a)};
		let ext_name = "RECORD";
		// Check presence of Record extension
		let arg2:*mut c_int = &mut 1;
		let arg3:*mut c_int = &mut 1;
		let arg4:*mut c_int = &mut 1;
		let has_record = XQueryExtension(display_control.display, ext_name.to_c_str().as_ptr() as *const i8,arg2,arg3,arg4);
		let extension = XInitExtension(display_control.display, ext_name.to_c_str().as_ptr() as *const i8);
		if extension.is_null() {
			panic!("XInitExtension() failed!");
		}

		// Get version
		let mut versionMajor: c_int = 0;
		let mut versionMinor: c_int = 0;
		XRecordQueryVersion(display_control.display, &mut versionMajor, &mut versionMinor);
		println!("RECORD extension version {}.{}", versionMajor, versionMinor);

		// Prepare record range
		let mut recordRange: XRecordRange = *XRecordAllocRange();
		let mut recordRangePtr: *mut *mut XRecordRange = std::mem::transmute(&mut &mut recordRange);
		recordRange.device_events.first = 2; // KeyPress
		recordRange.device_events.last = 6; // MotionNotify

		// Create context
		let context = XRecordCreateContext(
			display_control.display,
			0,
			&mut XRecordAllClients,
			1,
			recordRangePtr,
			1
		);
		if context == 0 {
			panic!("Fail create Record context\n");
		}

		// Run
		let res = XRecordEnableContextAsync(display_data.display, context, Some(recordCallback), &mut 0);
		if res == 0 {
			panic!("Cound not enable the Record context!\n");
		}
		xtst::XRecordFreeContext(display_data.display, context);

		// without this timer process consume 100% CPU
		let mut timer = Timer::new().unwrap();
		let periodic = timer.periodic(Duration::milliseconds(1000));
		loop {
			periodic.recv();
			XRecordProcessReplies(display_data.display);
		}
	}
}

extern "C" fn recordCallback(pointer:*mut i8, raw_data: *mut XRecordInterceptData) {

	unsafe {

		let data = &*raw_data;
		// println!("Category {}", data.category);
		if data.category != xtst::XRecordFromServer {
			return;
		}

		println!("Event count: {}", event_count);
		event_count += 1;
		println!("Time {}", data.server_time);
		println!("Datalen {}", data.data_len);
		let mut xdatum_ptr: *mut XRecordDatum = data.data as *mut XRecordDatum;
		let mut xdatum = &*xdatum_ptr;

		let mut event = xdatum.event;
		println!("Type {}", xdatum.xtype);

		// Catch key event
		if xdatum.xtype == xtst::KeyPress || xdatum.xtype == xtst::KeyRelease {
			// extract key code
			let raw_bytes: &mut [u8,..4] = std::mem::transmute(data.data);
			let c_char  = XKeysymToString(XKeycodeToKeysym(display_control.display, raw_bytes[1], 0));
			let c_string = CString::new(std::mem::transmute(c_char), false);
			let char: &str = c_string.as_str().unwrap();
			println!("Keycode: {}, Character {}", raw_bytes[1], char);
		}

		let mut current_window = display_control.get_input_focus();
		let mut parent_window: Option<Window> = None;
		let mut wm_name_str: Option<String> = None;

		let mut i = 0u;
		while i < 10 {
			if current_window.id == 0 {
				break;
			}

			wm_name_str = current_window.get_wm_name();
			match wm_name_str {
				None => {
					let tree = current_window.get_tree();
					parent_window = match tree {
						Some(WindowTree{parent: parent, children: _}) => {
							Some(parent)
						},
						_ => None
					}

				},
				Some(ref wmname) => {
					break;
				}
			}

			current_window = match parent_window {
				Some(win) => win,
				_ => current_window
			};

			i += 1;
		}
		match wm_name_str {
			Some(ref name) => {
				println!("WM_NAME: {}", *name);
			},
			None => {
				println!("WM_NAME: none");
			}
		}

		XRecordFreeData(raw_data);
	}
	println!("\n");
}

// ============================================================================
// Simple naive wrappers around X stuff
// ============================================================================
struct Display<'a> {
    display: *mut xlib::Display,
}

impl<'a> Display<'a> {
	fn new() -> Display<'a> {
		Display {display: unsafe {
			let mut a:  i8 = 0;
			let dpy = XOpenDisplay(&a);
			if dpy.is_null() {
				panic!("XOpenDisplay() failed!");
			}
			dpy
		}}
	}
	fn get_input_focus(&self) -> Window{
		let current_window: *mut xlib::Window = &mut 0;
		let revert_to_return: *mut i32 = &mut 0;
		unsafe{xlib::XGetInputFocus(self.display, current_window, revert_to_return)};
		Window {id: unsafe{*current_window}, display: self.display}
	}
}

struct Window<'a> {
    id: u64, // XID
    display: *mut xlib::Display
}

impl<'a> Window<'a> {
	fn get_wm_name(&self) -> Option<String> {
		// let mut a:String = String::new();
		// let wmname = unsafe {
			// let mut window_name: *mut i8 = 0 as *mut i8;
			// let res = xlib::XFetchName(self.display, self.id, &mut window_name);
			// if res != 0 {
			// 	let c_wm_name = CString::new(std::mem::transmute(window_name), false);
			// 	// xlib::XFree(&mut window_name);
			// 	Some(String::from_str(c_wm_name.as_str().unwrap()))
			// } else {
			// 	// Try get _NET_WM_NAME
			// 	None
			// }
			let wmname_c = self.get_property("_NET_WM_NAME", "UTF8_STRING");
			match wmname_c {
				Some(bytes) => match String::from_utf8(bytes){
					Ok(value) => Some(value),
					Err(err) => {println!("Error: {}", err); None}
				},
				None => None
			}
		// };
		// wmname
	}
	fn get_property(&self, property_name: &str, property_type: &str) -> Option<Vec<u8>>{
		unsafe {
			let xa_property_type: xlibint::Atom = xlib::XInternAtom(self.display, property_type.to_c_str().as_ptr(), 0);
			let xa_property_name: xlibint::Atom = xlib::XInternAtom(self.display, property_name.to_c_str().as_ptr(), 0);
			let mut actual_type_return  : xlibint::Atom     = 0;
			let mut actual_format_return: libc::c_int       = 0;
			let mut nitems_return       : libc::c_ulong     = 0;
			let mut bytes_after_return  : libc::c_ulong     = 0;
			let mut tmp                 : libc::c_uchar     = 0u8;
			let mut prop_return         : *mut libc::c_uchar = mem::transmute(&mut tmp);
			let res = xlib::XGetWindowProperty(
				self.display,
				self.id,
				xa_property_name,
				0,
				4096 / 4,
				0,
				xa_property_type,
				&mut actual_type_return,
				&mut actual_format_return,
				&mut nitems_return,
				&mut bytes_after_return,
				&mut prop_return
				);
			if (xa_property_type != actual_type_return) {
				println!("Invalid type of {} property", property_name);
				return None;
			}
			let tmp_size = ((actual_format_return as u64) / 8) * nitems_return;

			let data = c_vec::CVec::new(prop_return, tmp_size as uint);
			let mut copy_data = Vec::with_capacity(tmp_size as uint);
			for b in data.as_slice().iter() {
				copy_data.push(*b);
			}

			xlib::XFree(prop_return as *mut libc::types::common::c95::c_void);

			Some(copy_data)
		}
	}

	fn get_tree (&self) -> Option<WindowTree> {
		unsafe {
			let mut root: xlib::Window = 0;
			let mut parent: xlib::Window = 0;
			let mut children: *mut xlib::Window = &mut 0u64;
			let mut nchildren: u32 = 0;

			let res = xlib::XQueryTree(
				self.display,
				self.id,
				&mut root,
				&mut parent,
				&mut children,
				&mut nchildren);

			match res {
				0 => None,
				_ => {
					let childs = match nchildren {
						0 => None,
						_ => {
							// let c = std::c_vec::CVec::new(children, nchildren);
							let mut b: Vec<Window> = Vec::new();
							for i in range(0, nchildren as int){
								b.push(Window{
									id: *children.offset(i),
									display: self.display
								});
							}
							Some(b)
						}
					};

					Some(WindowTree {
						parent: Window{
							id: parent,
							display: self.display,
						},
						children: childs
					})
				}
			}
		}
	}
}

struct WindowTree<'a> {
    parent: Window<'a>,
    children: Option<Vec<Window<'a>>>,
}<|endoftext|>


<commit_before>use reqwest::blocking::Client;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use std::collections::HashMap;
use std::env;
use std::fs;
use std::io::{self, Error, Write};
use std::path::Path;
use std::process;
use std::time::Duration;

use monolith::html::{
    add_favicon, has_favicon, html_to_dom, metadata_tag, stringify_document, walk_and_embed_assets,
};
use monolith::opts::Options;
use monolith::url::{
    data_to_data_url, data_url_to_data, is_data_url, is_file_url, is_http_url, resolve_url,
};
use monolith::utils::retrieve_asset;

mod macros;

enum Output {
    Stdout(io::Stdout),
    File(fs::File),
}

impl Output {
    fn new(file_path: &str) -> Result<Output, Error> {
        if file_path.is_empty() {
            Ok(Output::Stdout(io::stdout()))
        } else {
            Ok(Output::File(fs::File::create(file_path)?))
        }
    }

    fn writeln_str(&mut self, s: &str) -> Result<(), Error> {
        match self {
            Output::Stdout(stdout) => {
                writeln!(stdout, "{}", s)?;
                stdout.flush()
            }
            Output::File(f) => {
                writeln!(f, "{}", s)?;
                f.flush()
            }
        }
    }
}

fn main() {
    let options = Options::from_args();
    let original_target: &str = &options.target;
    let target_url: &str;
    let base_url;
    let mut dom;

    // Pre-process the input
    let cwd_normalized: String =
        str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/");
    let path = Path::new(original_target);
    let mut target: String = str!(original_target.clone()).replace("\\", "/");
    let path_is_relative: bool = path.is_relative();

    // Determine exact target URL
    if target.clone().len() == 0 {
        eprintln!("No target specified");
        process::exit(1);
    } else if is_http_url(target.clone()) || is_data_url(target.clone()) {
        target_url = target.as_str();
    } else if is_file_url(target.clone()) {
        target_url = target.as_str();
    } else if path.exists() {
        if !path.is_file() {
            eprintln!("Local target is not a file: {}", original_target);
            process::exit(1);
        }
        target.insert_str(0, if cfg!(windows) { "file:///" } else { "file://" });
        if path_is_relative {
            target.insert_str(if cfg!(windows) { 8 } else { 7 }, &cwd_normalized);
            target.insert_str(
                if cfg!(windows) { 8 } else { 7 } + &cwd_normalized.len(),
                "/",
            );
        }
        target_url = target.as_str();
    } else {
        target.insert_str(0, "http://");
        target_url = target.as_str();
    }

    // Define output
    let mut output = Output::new(&options.output).expect("Could not prepare output");

    // Initialize client
    let mut cache = HashMap::new();
    let mut header_map = HeaderMap::new();
    header_map.insert(
        USER_AGENT,
        HeaderValue::from_str(&options.user_agent).expect("Invalid User-Agent header specified"),
    );
    let timeout: u64 = if options.timeout > 0 {
        options.timeout
    } else {
        std::u64::MAX / 4
    };
    let client = Client::builder()
        .timeout(Duration::from_secs(timeout))
        .danger_accept_invalid_certs(options.insecure)
        .default_headers(header_map)
        .build()
        .expect("Failed to initialize HTTP client");

    // Retrieve target document
    if is_file_url(target_url) || is_http_url(target_url) {
        match retrieve_asset(
            &mut cache,
            &client,
            target_url,
            target_url,
            options.silent,
            0,
        ) {
            Ok((data, final_url, _media_type)) => {
                base_url = final_url;
                dom = html_to_dom(&String::from_utf8_lossy(&data));
            }
            Err(_) => {
                eprintln!("Could not retrieve target document");
                process::exit(1);
            }
        }
    } else if is_data_url(target_url) {
        let (media_type, data): (String, Vec<u8>) = data_url_to_data(target_url);
        if !media_type.eq_ignore_ascii_case("text/html") {
            eprintln!("Unsupported data URL media type");
            process::exit(1);
        }
        base_url = str!(target_url);
        dom = html_to_dom(&String::from_utf8_lossy(&data));
    } else {
        process::exit(1);
    }

    // Request and embed /favicon.ico (unless it's already linked in the document)
    if !options.no_images && is_http_url(target_url) && !has_favicon(&dom.document) {
        let favicon_ico_url: String = resolve_url(&base_url, "/favicon.ico").unwrap();

        match retrieve_asset(
            &mut cache,
            &client,
            &base_url,
            &favicon_ico_url,
            options.silent,
            0,
        ) {
            Ok((data, final_url, media_type)) => {
                let favicon_data_url: String = data_to_data_url(&media_type, &data, &final_url);
                dom = add_favicon(&dom.document, favicon_data_url);
            }
            Err(_) => {
                // Failed to retrieve favicon.ico
            }
        }
    }

    // Embed remote assets
    walk_and_embed_assets(&mut cache, &client, &base_url, &dom.document, &options, 0);

    // Serialize DOM tree
    let mut result: String = stringify_document(&dom.document, &options);

    // Add metadata tag
    if !options.no_metadata {
        let metadata_comment = metadata_tag(&base_url);
        result.insert_str(0, &metadata_comment);
        if metadata_comment.len() > 0 {
            result.insert_str(metadata_comment.len(), "\n");
        }
    }

    // Write result into stdout or file
    output
        .writeln_str(&result)
        .expect("Could not write HTML output");
}
<commit_msg>treat minus for output target file path as stdout<commit_after>use reqwest::blocking::Client;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use std::collections::HashMap;
use std::env;
use std::fs;
use std::io::{self, Error, Write};
use std::path::Path;
use std::process;
use std::time::Duration;

use monolith::html::{
    add_favicon, has_favicon, html_to_dom, metadata_tag, stringify_document, walk_and_embed_assets,
};
use monolith::opts::Options;
use monolith::url::{
    data_to_data_url, data_url_to_data, is_data_url, is_file_url, is_http_url, resolve_url,
};
use monolith::utils::retrieve_asset;

mod macros;

enum Output {
    Stdout(io::Stdout),
    File(fs::File),
}

impl Output {
    fn new(file_path: &str) -> Result<Output, Error> {
        if file_path.is_empty() || file_path.eq("-") {
            Ok(Output::Stdout(io::stdout()))
        } else {
            Ok(Output::File(fs::File::create(file_path)?))
        }
    }

    fn writeln_str(&mut self, s: &str) -> Result<(), Error> {
        match self {
            Output::Stdout(stdout) => {
                writeln!(stdout, "{}", s)?;
                stdout.flush()
            }
            Output::File(f) => {
                writeln!(f, "{}", s)?;
                f.flush()
            }
        }
    }
}

fn main() {
    let options = Options::from_args();
    let original_target: &str = &options.target;
    let target_url: &str;
    let base_url;
    let mut dom;

    // Pre-process the input
    let cwd_normalized: String =
        str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/");
    let path = Path::new(original_target);
    let mut target: String = str!(original_target.clone()).replace("\\", "/");
    let path_is_relative: bool = path.is_relative();

    // Determine exact target URL
    if target.clone().len() == 0 {
        eprintln!("No target specified");
        process::exit(1);
    } else if is_http_url(target.clone()) || is_data_url(target.clone()) {
        target_url = target.as_str();
    } else if is_file_url(target.clone()) {
        target_url = target.as_str();
    } else if path.exists() {
        if !path.is_file() {
            eprintln!("Local target is not a file: {}", original_target);
            process::exit(1);
        }
        target.insert_str(0, if cfg!(windows) { "file:///" } else { "file://" });
        if path_is_relative {
            target.insert_str(if cfg!(windows) { 8 } else { 7 }, &cwd_normalized);
            target.insert_str(
                if cfg!(windows) { 8 } else { 7 } + &cwd_normalized.len(),
                "/",
            );
        }
        target_url = target.as_str();
    } else {
        target.insert_str(0, "http://");
        target_url = target.as_str();
    }

    // Define output
    let mut output = Output::new(&options.output).expect("Could not prepare output");

    // Initialize client
    let mut cache = HashMap::new();
    let mut header_map = HeaderMap::new();
    header_map.insert(
        USER_AGENT,
        HeaderValue::from_str(&options.user_agent).expect("Invalid User-Agent header specified"),
    );
    let timeout: u64 = if options.timeout > 0 {
        options.timeout
    } else {
        std::u64::MAX / 4
    };
    let client = Client::builder()
        .timeout(Duration::from_secs(timeout))
        .danger_accept_invalid_certs(options.insecure)
        .default_headers(header_map)
        .build()
        .expect("Failed to initialize HTTP client");

    // Retrieve target document
    if is_file_url(target_url) || is_http_url(target_url) {
        match retrieve_asset(
            &mut cache,
            &client,
            target_url,
            target_url,
            options.silent,
            0,
        ) {
            Ok((data, final_url, _media_type)) => {
                base_url = final_url;
                dom = html_to_dom(&String::from_utf8_lossy(&data));
            }
            Err(_) => {
                eprintln!("Could not retrieve target document");
                process::exit(1);
            }
        }
    } else if is_data_url(target_url) {
        let (media_type, data): (String, Vec<u8>) = data_url_to_data(target_url);
        if !media_type.eq_ignore_ascii_case("text/html") {
            eprintln!("Unsupported data URL media type");
            process::exit(1);
        }
        base_url = str!(target_url);
        dom = html_to_dom(&String::from_utf8_lossy(&data));
    } else {
        process::exit(1);
    }

    // Request and embed /favicon.ico (unless it's already linked in the document)
    if !options.no_images && is_http_url(target_url) && !has_favicon(&dom.document) {
        let favicon_ico_url: String = resolve_url(&base_url, "/favicon.ico").unwrap();

        match retrieve_asset(
            &mut cache,
            &client,
            &base_url,
            &favicon_ico_url,
            options.silent,
            0,
        ) {
            Ok((data, final_url, media_type)) => {
                let favicon_data_url: String = data_to_data_url(&media_type, &data, &final_url);
                dom = add_favicon(&dom.document, favicon_data_url);
            }
            Err(_) => {
                // Failed to retrieve favicon.ico
            }
        }
    }

    // Embed remote assets
    walk_and_embed_assets(&mut cache, &client, &base_url, &dom.document, &options, 0);

    // Serialize DOM tree
    let mut result: String = stringify_document(&dom.document, &options);

    // Add metadata tag
    if !options.no_metadata {
        let metadata_comment = metadata_tag(&base_url);
        result.insert_str(0, &metadata_comment);
        if metadata_comment.len() > 0 {
            result.insert_str(metadata_comment.len(), "\n");
        }
    }

    // Write result into stdout or file
    output
        .writeln_str(&result)
        .expect("Could not write HTML output");
}
<|endoftext|>


<commit_before>extern crate darknet;
use darknet::*;
use std::io::Write;

fn main() {
    run().unwrap();
}

fn run() -> Result<(), Error> {
    std::env::set_current_dir("darknet-sys").unwrap();
    // let network = Network::new("cfg/yolov2-tiny.cfg", "yolov2-tiny.weights")?;
    // let meta = Meta::new("cfg/coco.data")?;
    let image = Image::load("data/dog.jpg")?;
    // let dets = simple_detect(&network, &meta, &image);
    // for d in &dets {
    //     image.draw_box(d, 1, 1.0, 0.0, 0.0);
    // }
    // println!("{:?}", dets);
    image.save("dog2")?;
    image.save_jpg("dog2")?;

    let data = image.encode_jpg();
    {
        let mut buffer = ::std::fs::File::create("foo.jpg")?;
        buffer.write_all(&data)?;
    }

    let decoded = Image::decode_jpg(&data);
    decoded.save_jpg("decoded")?;
    Ok(())
}
<commit_msg>change main to detect objects<commit_after>extern crate darknet;
use darknet::*;
use std::io::Write;

fn main() {
    run().unwrap();
}

fn run() -> Result<(), Error> {
    std::env::set_current_dir("darknet-sys").unwrap();

    let network = Network::new("cfg/yolov2-tiny.cfg", "yolov2-tiny.weights")?;
    let meta = Meta::new("cfg/coco.data")?;
    let mut image = Image::load("data/dog.jpg")?;
    let dets = simple_detect(&network, &meta, &image);
    for d in &dets {
        image.draw_box(d, 1, 1.0, 0.0, 0.0);
    }
    println!("{:?}", dets);

    let data = image.encode_jpg();
    {
        let mut buffer = ::std::fs::File::create("prediction.jpg")?;
        buffer.write_all(&data)?;
    }
    Ok(())
}
<|endoftext|>