<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate serde;
extern crate rustc_serialize;
extern crate toml;
#[macro_use]
extern crate log;
extern crate env_logger;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hyper::client::Response;
use hubcaps::{Credentials, Github, IssueOptions};
use afterparty::*;
use serde_json::Value;
use regex::Regex;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

macro_rules! panic_log {
    ($($arg:tt)*) => ( {
        error!($($arg)*);
        panic!()
    } );
}

trait ExpectLog {
    type Item;
    fn expect_log(self, &str) -> Self::Item;
}

impl<T> ExpectLog for Option<T> {
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            None => panic_log!("{}", msg),
            Some(s) => s,
        }
    }
}

impl<T, E> ExpectLog for Result<T, E>
    where E: std::fmt::Display
{
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            Err(err) => panic_log!("{} {}", msg, err),
            Ok(s) => s,
        }
    }
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name)
        .expect_log(&format!("Couldn't open config file {}", file_name));
    let mut str = String::new();
    config_file.read_to_string(&mut str)
        .expect_log(&format!("Couldn't read config file {}", file_name));
    let toml: Option<ConfigData> = decode_str(&str);
    toml.expect_log(&format!("Couldn't decode config file {}", file_name))
}

#[test]
fn it_reads_toml() {
    env_logger::init().unwrap();
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    env_logger::init().unwrap();

    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(&pull_request.commits_url, &repository.full_name)
                }
            }
            _ => info!("Recived a request that wasn't a pull-request webhook"),
        }
    });

    let ip_and_port = get_ip_and_port();

    match Server::http(ip_and_port.as_str()) {
        Err(err) => error!("ERROR: failed to start server, the error was: {}", err),
        Ok(server) => {
            match server.handle(hub) {
                Err(err) => error!("ERROR starting handler, the error was: {}", err),
                Ok(_) => info!("Successfully started server"),
            }
        }
    }
}

fn env_or(name: &str, def: &str) -> String {
    env::var_os(name).and_then(|v| v.into_string().ok()).unwrap_or(def.to_owned())
}

fn get_ip_and_port() -> String {
    let ip = env_or("STEVE_IP", "0.0.0.0");
    let port = env_or("STEVE_PORT", "80");
    format!("{}:{}", ip, port)
}

fn handle_pr(commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
        .expect_log("Missing github token, STEVE_GITHUB_TOKEN is not defined!")
        .into_string()
        .ok()
        .expect_log("Github token contains illegal characters!");
    match config_data.repos.get(&repository.to_owned()) {
        Some(config) => {
            update_issues(&commits_url,
                          &auth_token,
                          &config,
                          &config_data.api_root,
                          &repository)
        }
        None => info!("No config found for repo {}, skipping", repository),
    }
}

fn get_with_retries(client: &Client,
                    url: &str,
                    headers: &Headers,
                    max_retries: u64)
                    -> Result<Response, String> {
    for i in 1..max_retries {
        let retry_headers = headers.clone();
        match client.get(url).headers(retry_headers).send() {
            Ok(result) => {
                if result.status == hyper::Ok {
                    return Ok(result);
                } else {
                    if i < max_retries {
                        info!("http response {:?}, retrying", result.status_raw());
                    } else {
                        return Err(format!("http error {:?}", result.status_raw()));
                    }
                }
            }
            Err(err) => {
                if i < max_retries {
                    error!("Hyper error {}, retrying anyway", err)
                } else {
                    return Err(format!("Hyper error {}", err));
                }
            }
        }
    }
    unreachable!();
}

fn update_issues(commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let retries = env_or("STEVE_MAX_RETRIES", "5")
        .parse::<u64>()
        .expect_log("max retries need to be an integer");
    let commits = match get_with_retries(&client, &commits_url, &headers, retries) {
        Ok(c) => c,
        Err(err) => {
            error!("Failed to get commits, error {}", err);
            return ();
        }
    };

    match serde_json::from_reader(commits) {
        Ok(json) => {
            match parse_commit_data(&json) {
                Ok(messages) => {
                    let mut issues = HashSet::new();
                    for message in messages {
                        get_issues(&mut issues, &message)
                    }

                    if !issues.is_empty() {
                        let github = Github::host(api_root,
                                                  "steve",
                                                  &client,
                                                  Credentials::Token(auth_token.to_owned()));
                        let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
                            strs => (strs[0], strs[1]),
                        };
                        let repo = github.repo(repo_name, owner);
                        match repo.issues().list(&Default::default()) {
                            Err(err) => error!("{:?}", err),
                            Ok(issue_data) => {
                                for issue in issues {
                                    if issue_data.len() as u64 <= issue {
                                        info!("Wanted to update issue #{}, but {} doesn't have an \
                                               issue with that number",
                                              issue,
                                              repository)
                                    } else {
                                        let ref current_flags = issue_data[issue as usize].labels;
                                        let new_flags: Vec<String> = current_flags.into_iter()
                                            .map(|l| l.name.clone())
                                            .chain(config.qa_flags.clone().into_iter())
                                            .collect();
                                        let issue_options =
                                            IssueOptions::new::<String,
                                                                String,
                                                                String,
                                                                String>(None,
                                                                        None,
                                                                        Some(config.qa_user
                                                                            .clone()),
                                                                        None,
                                                                        Some(new_flags));
                                        if let Err(err) = repo.issue(issue).edit(&issue_options) {
                                            error!("{:?}", err)
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                Err(err) => error!("{:?}", err),
            }
        }
        Err(err) => error!("{:?}", err),
    }
}

#[test]
fn it_parses_commit_data() {
    let file = File::open("test-data/test-messages.json").expect("Couldn't open message file");
    let json = serde_json::from_reader(file);
    assert!(json.is_ok());
    let json = json.unwrap();
    let messages = parse_commit_data(&json);
    assert!(messages.is_ok());
    let messages = messages.unwrap();
    assert_eq!(messages[0], "message1");
    assert_eq!(messages[1], "swordfish");
}

fn parse_commit_data<'a>(json: &'a Value) -> Result<Vec<&'a str>, &str> {
    let arr = try!(json.as_array().ok_or("expected array as top level object"));
    arr.iter()
        .map(|obj| {
            obj.pointer("/commit/message")
                .ok_or("missing message field")
                .and_then(|m| m.as_string().ok_or("message field has wrong type"))
        })
        .collect()
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    let _ = github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

#[test]
fn it_finds_issue_numbers() {
    let message = r"This is a commit message
        needs qa: #72 should get caught
        needs QA #333 should also get caught
        but #44 won't, and needs QA: #33 #45
        only gets #33. Duplicates are discarded:
        needs qa #72";
    let mut expected: HashSet<u64> = HashSet::new();
    expected.insert(72);
    expected.insert(333);
    expected.insert(33);
    let mut numbers: HashSet<u64> = HashSet::new();
    get_issues(&mut numbers, message);
    assert_eq!(numbers, expected);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").expect_log("{}");
    }
    for num in RE.captures_iter(message).filter_map(|cap| cap.at(1)) {
        numbers.insert(num.parse::<u64>().expect_log("{}"));
    }
}
<commit_msg>Handle http errors more betterer than before.<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate serde;
extern crate rustc_serialize;
extern crate toml;
#[macro_use]
extern crate log;
extern crate env_logger;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::fmt::Debug;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hyper::client::Response;
use hubcaps::{Credentials, Github, IssueOptions};
use afterparty::*;
use serde_json::Value;
use regex::Regex;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

macro_rules! panic_log {
    ($($arg:tt)*) => ( {
        error!($($arg)*);
        panic!()
    } );
}

trait ExpectLog {
    type Item;
    fn expect_log(self, &str) -> Self::Item;
}

impl<T> ExpectLog for Option<T> {
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            None => panic_log!("{}", msg),
            Some(s) => s,
        }
    }
}

impl<T, E> ExpectLog for Result<T, E>
    where E: std::fmt::Display
{
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            Err(err) => panic_log!("{} {}", msg, err),
            Ok(s) => s,
        }
    }
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name)
        .expect_log(&format!("Couldn't open config file {}", file_name));
    let mut str = String::new();
    config_file.read_to_string(&mut str)
        .expect_log(&format!("Couldn't read config file {}", file_name));
    let toml: Option<ConfigData> = decode_str(&str);
    toml.expect_log(&format!("Couldn't decode config file {}", file_name))
}

#[test]
fn it_reads_toml() {
    env_logger::init().unwrap();
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    env_logger::init().unwrap();

    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(&pull_request.commits_url, &repository.full_name)
                }
            }
            _ => info!("Recived a request that wasn't a pull-request webhook"),
        }
    });

    let ip_and_port = get_ip_and_port();

    match Server::http(ip_and_port.as_str()) {
        Err(err) => error!("ERROR: failed to start server, the error was: {}", err),
        Ok(server) => {
            match server.handle(hub) {
                Err(err) => error!("ERROR starting handler, the error was: {}", err),
                Ok(_) => info!("Successfully started server"),
            }
        }
    }
}

fn env_or(name: &str, def: &str) -> String {
    env::var_os(name).and_then(|v| v.into_string().ok()).unwrap_or(def.to_owned())
}

fn get_ip_and_port() -> String {
    let ip = env_or("STEVE_IP", "0.0.0.0");
    let port = env_or("STEVE_PORT", "80");
    format!("{}:{}", ip, port)
}

fn handle_pr(commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
        .expect_log("Missing github token, STEVE_GITHUB_TOKEN is not defined!")
        .into_string()
        .ok()
        .expect_log("Github token contains illegal characters!");
    match config_data.repos.get(&repository.to_owned()) {
        Some(config) => {
            update_issues(&commits_url,
                          &auth_token,
                          &config,
                          &config_data.api_root,
                          &repository)
        }
        None => info!("No config found for repo {}, skipping", repository),
    }
}

fn do_retry<F, R, E>(func: F, max_retries: u64) -> Result<R, E>
    where F: Fn() -> Result<R, E>,
          R: Debug,
          E: Debug
{
    for _ in 1..(max_retries - 1) {
        let result = func();
        if result.is_ok() {
            return result;
        } else {
            info!("{:?}", result.unwrap_err())
        }
    }
    return func();
}

fn update_issues(commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let retries = env_or("STEVE_MAX_RETRIES", "5")
        .parse::<u64>()
        .expect_log("max retries need to be an integer");
    let commits = match do_retry(|| client.get(commits_url).headers(headers.clone()).send(),
                                 retries) {
        Ok(c) => c,
        Err(err) => {
            error!("Failed to get commits, error {:?}", err);
            return ();
        }
    };

    match serde_json::from_reader(commits) {
        Ok(json) => {
            match parse_commit_data(&json) {
                Ok(messages) => {
                    let mut issues = HashSet::new();
                    for message in messages {
                        get_issues(&mut issues, &message)
                    }

                    if !issues.is_empty() {
                        let github = Github::host(api_root,
                                                  "steve",
                                                  &client,
                                                  Credentials::Token(auth_token.to_owned()));
                        let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
                            strs => (strs[0], strs[1]),
                        };
                        let repo = github.repo(repo_name, owner);
                        match do_retry(|| repo.issues().list(&Default::default()), retries) {
                            Err(err) => error!("{:?}", err),
                            Ok(issue_data) => {
                                for issue in issues {
                                    if issue_data.len() as u64 <= issue {
                                        info!("Wanted to update issue #{}, but {} doesn't have an \
                                               issue with that number",
                                              issue,
                                              repository)
                                    } else {
                                        let ref current_flags = issue_data[issue as usize].labels;
                                        let new_flags: Vec<String> = current_flags.into_iter()
                                            .map(|l| l.name.clone())
                                            .chain(config.qa_flags.clone().into_iter())
                                            .collect();
                                        let issue_options =
                                            IssueOptions::new::<String,
                                                                String,
                                                                String,
                                                                String>(None,
                                                                        None,
                                                                        Some(config.qa_user
                                                                            .clone()),
                                                                        None,
                                                                        Some(new_flags));
                                        if let Err(err) = do_retry(|| {
                                                                       repo.issue(issue)
                                                                           .edit(&issue_options)
                                                                   },
                                                                   retries) {
                                            error!("{:?}", err)
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                Err(err) => error!("{:?}", err),
            }
        }
        Err(err) => error!("{:?}", err),
    }
}

#[test]
fn it_parses_commit_data() {
    let file = File::open("test-data/test-messages.json").expect("Couldn't open message file");
    let json = serde_json::from_reader(file);
    assert!(json.is_ok());
    let json = json.unwrap();
    let messages = parse_commit_data(&json);
    assert!(messages.is_ok());
    let messages = messages.unwrap();
    assert_eq!(messages[0], "message1");
    assert_eq!(messages[1], "swordfish");
}

fn parse_commit_data<'a>(json: &'a Value) -> Result<Vec<&'a str>, &str> {
    let arr = try!(json.as_array().ok_or("expected array as top level object"));
    arr.iter()
        .map(|obj| {
            obj.pointer("/commit/message")
                .ok_or("missing message field")
                .and_then(|m| m.as_string().ok_or("message field has wrong type"))
        })
        .collect()
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    let _ = github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

#[test]
fn it_finds_issue_numbers() {
    let message = r"This is a commit message
        needs qa: #72 should get caught
        needs QA #333 should also get caught
        but #44 won't, and needs QA: #33 #45
        only gets #33. Duplicates are discarded:
        needs qa #72";
    let mut expected: HashSet<u64> = HashSet::new();
    expected.insert(72);
    expected.insert(333);
    expected.insert(33);
    let mut numbers: HashSet<u64> = HashSet::new();
    get_issues(&mut numbers, message);
    assert_eq!(numbers, expected);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").expect_log("{}");
    }
    for num in RE.captures_iter(message).filter_map(|cap| cap.at(1)) {
        numbers.insert(num.parse::<u64>().expect_log("{}"));
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * FIXME: running `cargo run -- fsf.org` opens about:blank.
 * FIXME: using Escape in insert mode triggers Escape in the web page (in Scala doc).
 * FIXME: the insert mode sometimes disable itself (using rofi-pass). For instance, on https://courrielweb.videotron.com/cw/legacyLoginResidentiel.action
 * FIXME: I can set tags on URLs that are not bookmarked.
 * FIXME: hints on wrong locations on http://www.mensacanada.org/contact/.
 * FIXME: follow open in a new window after a follow-win.
 * FIXME: crash when attempting to open a PDF on Air Transat, Mon Dossier.
 * FIXME: web process crash on print (follow onclick="javascript:print").
 * TODO: auto-delete tags.
 * TODO: allow to remove tags from bookmarks.
 * FIXME: cookies are not synced between windows (cookies not reloaded in existing windows: use a thread and/or catch_unwind()).
 *
 * TODO: unlock the password store when loading a password.
 * FIXME: loading credentials does not work.
 * FIXME: saving empty credentials on https://lichess4545.slack.com/
 *
 * TODO: switch to one UI process (and one DBus server to see if it resolves the timeout issues).
 * TODO: save the current URLs of every window in case of a crash.
 * TODO: command to restore the last closed window.
 *
 * TODO: sort bookmark completion with number of access (the most accessed URLs come first, then by
 * alphebetical order).
 * TODO: automatically propose tags when editting bookmark tags (fetch them from the webpage, <meta
 * property="og:article:tag"/> is a start).
 *
 * TODO: use lifetimes to ensure the pointers live long enough for the connect!() macro.
 *
 * TODO: hint file input.
 * FIXME: open in new tab does not work in Github (https://github.com/rust-lang/rust/pull/37128).
 * Vimperator simulates ctrl-click to work around this.
 *
 * TODO: Create a gdbus binding that works similar to elm subscriptions.
 *
 * TODO: check if an extension process crashing causes issues in other extension process.
 * FIXME: missing hints on duckduckgo.com menu (caused by CSS3 transform).
 *
 * TODO: show an error when there are no hints.
 * FIXME: show hints for element with click event.
 * FIXME: a link on https://www.verywell.com/ear-pressure-pose-karnapidasana-3567089 cannot be
 * clicked (and many gobject critical error: g_object_ref assertion G_IS_OBJECT failed).
 *
 * TODO: handle the errors instead of unwrap().
 * TODO: continue to parse the config files even when there are errors.
 * TODO: #[default(value)] attribute for settings.
 *
 * TODO: show an error for request blocked by host blocker.
 * TODO: add a command to do the redirections to avoid being blocked by the ad blocker.
 * FIXME: ctrl-/ should not trigger the mapping for /.
 * TODO: allow using Backspace to remove the last hint character.
 * FIXME: hover does not always work (usherbrooke.ca) (perhaps trigger real click/hover mouse events in GTK+ instead of using DOM while still using the DOM focus function).
 * FIXME: an element visible but whose top-left corner is not shown wont get an hint.
 * FIXME: sometimes does not go to insert mode after focusing first input.
 * TODO: generate the default files from the code (for instance, from default settings) instead of
 * copying predefined files.
 * TODO: shortcut to open the selected (searched) word.
 * TODO: support CTRL-Z in input elements.
 * TODO: allow paste from selection clipboard (if the other is empty or with another shortcut?).
 * TODO: message when search fails (and when it wraps to the start/end).
 * TODO: hide the scrollbars?
 * FIXME: select dropdown can open in the other screen (webkit2gtk bug, move the cursor before clicking?).
 * TODO: unselect text when focusing a field.
 * TODO: add a passthrough mode.
 * TODO: add help text for commands and settings.
 * TODO: handle network errors.
 * TODO: support marks.
 * FIXME: titanium seems slower than other browsers.
 * TODO: preferred languages.
 * TODO: store cache.
 * TODO: add a command to delete history, …
 * TODO: NoScript.
 * TODO: open textarea in text editor.
 * TODO: add option to use light theme variant instead of dark variant.
 * TODO: private browsing.
 * TODO: soft scrolling (to avoid flickering for fixed elements, set_enable_smooth_scrolling).
 * TODO: do not search for the empty string, only disable the current search to allow continuing
 * the search on another page.
 * FIXME: do not show (or move) hints hidden by another element (branch button on GitHub).
 * TODO: use a custom error type (wrapping the other errors) instead of Box<Error>.
 * TODO: show source.
 * TODO: prevent videos from autoplaying.
 * TODO: copier plugin (word, line, sentense, block, links…).
 * TODO: separate config options in section (like webkit.enable-java instead of webkit-enable-java).
 * TODO: i18n.
 * TODO: plugin to block modal JavaScript dialog (https://www.sitepoint.com/community/t/ie-hover-trigger/69968).
 * FIXME: trigger a GTK+ event to activate hints (this will fix clicking on a link hidden by
 * another element).
 * TODO: do not consider right-click open in new window as a popup.
 * TODO: delete the files opened (perhaps by placing them in a temporary directory).
 *
 * TODO: command to update adblocker hosts file.
 * TODO: option to disable the adblocker.
 * TODO: block ads coming from websocket.
 * TODO: create a whitelist-based adblocker.
 * TODO: automatically detach the inspector when it is opened with "Inspect element".
 * TODO: remove the title bar of the inspector (window decorated property).
 * TODO: hide the hints when activating a hint.
 * TODO: in command and input mode, put the messages into a queue.
 * TODO: ask confirmation before submitting again the same form.
 * TODO: do not hard-code the extension directory: use the one provided by cargo.
 * TODO: find a way to install the titanium web extension library on cargo install.
 * TODO: activate insert mode after focusing a text element (disable insert mode when focus is lost).
 * TODO: show URL in title when the title is not available.
 * TODO: add command (;f) to focus frame.
 * TODO: add a validator for the file input (browse): check that a file is selected (and not a
 * directory), check that the input file exists.
 * FIXME: cannot scroll on http://dudzik.co/digress-into-development/syntax-off/.
 * FIXME: prompt slow to show (it seems to slow down when there are other events waiting: try
 * starting a download when the page is still loading).
 * FIXME: issues when multiple input are shown (they must be inserted in a queue and shown one at a
 * time, or perhaps just using a blocking input for popups will do it).
 *
 * TODO: add tests.
 *
 * FIXME: some dbus calls timeout (seems to be caused by the click method since it triggers an
 * action in the application which is waiting for the answer of the call).
 * FIXME: webview hides when resizing the screen (seems related to the web extension, or when the
 * page is not yet loaded, error: WebKitWebProcess: cairo-ft-font.c :669 : _cairo_ft_unscaled_font_lock_face:  l'assertion « !unscaled->from_face » a échoué.).
 */

//! Titanium is a webkit2 keyboard-driven web browser.

#![warn(missing_docs)]

extern crate cairo;
#[macro_use]
extern crate gdbus;
extern crate docopt;
extern crate gdk;
extern crate gio_sys;
extern crate glib;
extern crate glib_sys;
extern crate gtk;
extern crate gtk_sys;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[cfg(test)]
extern crate libxdo;
#[macro_use]
extern crate log;
#[macro_use]
extern crate mg;
#[macro_use]
extern crate mg_settings;
#[macro_use]
extern crate mg_settings_macros;
extern crate number_prefix;
extern crate open;
extern crate rusqlite;
extern crate rustc_serialize;
#[macro_use]
extern crate secret;
extern crate simplelog;
#[cfg(test)]
extern crate tempdir;
extern crate tempfile;
extern crate titanium_common;
extern crate url;
extern crate webkit2gtk;
extern crate xdg;

mod app;
mod bookmarks;
mod clipboard;
mod commands;
mod completers;
mod config_dir;
mod dialogs;
mod download;
mod download_view;
mod download_list_view;
mod file;
mod glib_ext;
mod message_server;
mod pass_manager;
mod popup_manager;
mod settings;
mod stylesheet;
mod urls;
mod webview;

use docopt::Docopt;
use log::LogLevel::Error;
use simplelog::{Config, LogLevelFilter, TermLogger};

use app::App;

const USAGE: &'static str = "
Titanium web browser.

Usage:
    titanium [<url>] [--config=<dir>] [--log]

Options:
    --config=<dir>  The configuration directory.
    --log           Show the log messages.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_url: Option<String>,
    flag_config: Option<String>,
    flag_log: bool,
}

fn main() {
    gtk::init().unwrap();

    let args: Args = Docopt::new(USAGE)
        .and_then(|decoder| decoder.decode())
        .unwrap_or_else(|error| error.exit());

    if args.flag_log {
        let config = Config {
            time: Some(Error),
            level: Some(Error),
            target: None,
            location: None,
        };
        TermLogger::init(LogLevelFilter::max(), config).unwrap();
    }

    let _app = App::new(args.arg_url, args.flag_config);

    gtk::main();
}
<commit_msg>Add FIXME<commit_after>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * FIXME: negative zoom level.
 * FIXME: running `cargo run -- fsf.org` opens about:blank.
 * FIXME: using Escape in insert mode triggers Escape in the web page (in Scala doc).
 * FIXME: the insert mode sometimes disable itself (using rofi-pass). For instance, on https://courrielweb.videotron.com/cw/legacyLoginResidentiel.action
 * FIXME: I can set tags on URLs that are not bookmarked.
 * FIXME: hints on wrong locations on http://www.mensacanada.org/contact/.
 * FIXME: follow open in a new window after a follow-win.
 * FIXME: crash when attempting to open a PDF on Air Transat, Mon Dossier.
 * FIXME: web process crash on print (follow onclick="javascript:print").
 * TODO: auto-delete tags.
 * TODO: allow to remove tags from bookmarks.
 * FIXME: cookies are not synced between windows (cookies not reloaded in existing windows: use a thread and/or catch_unwind()).
 *
 * TODO: unlock the password store when loading a password.
 * FIXME: loading credentials does not work.
 * FIXME: saving empty credentials on https://lichess4545.slack.com/
 *
 * TODO: switch to one UI process (and one DBus server to see if it resolves the timeout issues).
 * TODO: save the current URLs of every window in case of a crash.
 * TODO: command to restore the last closed window.
 *
 * TODO: sort bookmark completion with number of access (the most accessed URLs come first, then by
 * alphebetical order).
 * TODO: automatically propose tags when editting bookmark tags (fetch them from the webpage, <meta
 * property="og:article:tag"/> is a start).
 *
 * TODO: use lifetimes to ensure the pointers live long enough for the connect!() macro.
 *
 * TODO: hint file input.
 * FIXME: open in new tab does not work in Github (https://github.com/rust-lang/rust/pull/37128).
 * Vimperator simulates ctrl-click to work around this.
 *
 * TODO: Create a gdbus binding that works similar to elm subscriptions.
 *
 * TODO: check if an extension process crashing causes issues in other extension process.
 * FIXME: missing hints on duckduckgo.com menu (caused by CSS3 transform).
 *
 * TODO: show an error when there are no hints.
 * FIXME: show hints for element with click event.
 * FIXME: a link on https://www.verywell.com/ear-pressure-pose-karnapidasana-3567089 cannot be
 * clicked (and many gobject critical error: g_object_ref assertion G_IS_OBJECT failed).
 *
 * TODO: handle the errors instead of unwrap().
 * TODO: continue to parse the config files even when there are errors.
 * TODO: #[default(value)] attribute for settings.
 *
 * TODO: show an error for request blocked by host blocker.
 * TODO: add a command to do the redirections to avoid being blocked by the ad blocker.
 * FIXME: ctrl-/ should not trigger the mapping for /.
 * TODO: allow using Backspace to remove the last hint character.
 * FIXME: hover does not always work (usherbrooke.ca) (perhaps trigger real click/hover mouse events in GTK+ instead of using DOM while still using the DOM focus function).
 * FIXME: an element visible but whose top-left corner is not shown wont get an hint.
 * FIXME: sometimes does not go to insert mode after focusing first input.
 * TODO: generate the default files from the code (for instance, from default settings) instead of
 * copying predefined files.
 * TODO: shortcut to open the selected (searched) word.
 * TODO: support CTRL-Z in input elements.
 * TODO: allow paste from selection clipboard (if the other is empty or with another shortcut?).
 * TODO: message when search fails (and when it wraps to the start/end).
 * TODO: hide the scrollbars?
 * FIXME: select dropdown can open in the other screen (webkit2gtk bug, move the cursor before clicking?).
 * TODO: unselect text when focusing a field.
 * TODO: add a passthrough mode.
 * TODO: add help text for commands and settings.
 * TODO: handle network errors.
 * TODO: support marks.
 * FIXME: titanium seems slower than other browsers.
 * TODO: preferred languages.
 * TODO: store cache.
 * TODO: add a command to delete history, …
 * TODO: NoScript.
 * TODO: open textarea in text editor.
 * TODO: add option to use light theme variant instead of dark variant.
 * TODO: private browsing.
 * TODO: soft scrolling (to avoid flickering for fixed elements, set_enable_smooth_scrolling).
 * TODO: do not search for the empty string, only disable the current search to allow continuing
 * the search on another page.
 * FIXME: do not show (or move) hints hidden by another element (branch button on GitHub).
 * TODO: use a custom error type (wrapping the other errors) instead of Box<Error>.
 * TODO: show source.
 * TODO: prevent videos from autoplaying.
 * TODO: copier plugin (word, line, sentense, block, links…).
 * TODO: separate config options in section (like webkit.enable-java instead of webkit-enable-java).
 * TODO: i18n.
 * TODO: plugin to block modal JavaScript dialog (https://www.sitepoint.com/community/t/ie-hover-trigger/69968).
 * FIXME: trigger a GTK+ event to activate hints (this will fix clicking on a link hidden by
 * another element).
 * TODO: do not consider right-click open in new window as a popup.
 * TODO: delete the files opened (perhaps by placing them in a temporary directory).
 *
 * TODO: command to update adblocker hosts file.
 * TODO: option to disable the adblocker.
 * TODO: block ads coming from websocket.
 * TODO: create a whitelist-based adblocker.
 * TODO: automatically detach the inspector when it is opened with "Inspect element".
 * TODO: remove the title bar of the inspector (window decorated property).
 * TODO: hide the hints when activating a hint.
 * TODO: in command and input mode, put the messages into a queue.
 * TODO: ask confirmation before submitting again the same form.
 * TODO: do not hard-code the extension directory: use the one provided by cargo.
 * TODO: find a way to install the titanium web extension library on cargo install.
 * TODO: activate insert mode after focusing a text element (disable insert mode when focus is lost).
 * TODO: show URL in title when the title is not available.
 * TODO: add command (;f) to focus frame.
 * TODO: add a validator for the file input (browse): check that a file is selected (and not a
 * directory), check that the input file exists.
 * FIXME: cannot scroll on http://dudzik.co/digress-into-development/syntax-off/.
 * FIXME: prompt slow to show (it seems to slow down when there are other events waiting: try
 * starting a download when the page is still loading).
 * FIXME: issues when multiple input are shown (they must be inserted in a queue and shown one at a
 * time, or perhaps just using a blocking input for popups will do it).
 *
 * TODO: add tests.
 *
 * FIXME: some dbus calls timeout (seems to be caused by the click method since it triggers an
 * action in the application which is waiting for the answer of the call).
 * FIXME: webview hides when resizing the screen (seems related to the web extension, or when the
 * page is not yet loaded, error: WebKitWebProcess: cairo-ft-font.c :669 : _cairo_ft_unscaled_font_lock_face:  l'assertion « !unscaled->from_face » a échoué.).
 */

//! Titanium is a webkit2 keyboard-driven web browser.

#![warn(missing_docs)]

extern crate cairo;
#[macro_use]
extern crate gdbus;
extern crate docopt;
extern crate gdk;
extern crate gio_sys;
extern crate glib;
extern crate glib_sys;
extern crate gtk;
extern crate gtk_sys;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[cfg(test)]
extern crate libxdo;
#[macro_use]
extern crate log;
#[macro_use]
extern crate mg;
#[macro_use]
extern crate mg_settings;
#[macro_use]
extern crate mg_settings_macros;
extern crate number_prefix;
extern crate open;
extern crate rusqlite;
extern crate rustc_serialize;
#[macro_use]
extern crate secret;
extern crate simplelog;
#[cfg(test)]
extern crate tempdir;
extern crate tempfile;
extern crate titanium_common;
extern crate url;
extern crate webkit2gtk;
extern crate xdg;

mod app;
mod bookmarks;
mod clipboard;
mod commands;
mod completers;
mod config_dir;
mod dialogs;
mod download;
mod download_view;
mod download_list_view;
mod file;
mod glib_ext;
mod message_server;
mod pass_manager;
mod popup_manager;
mod settings;
mod stylesheet;
mod urls;
mod webview;

use docopt::Docopt;
use log::LogLevel::Error;
use simplelog::{Config, LogLevelFilter, TermLogger};

use app::App;

const USAGE: &'static str = "
Titanium web browser.

Usage:
    titanium [<url>] [--config=<dir>] [--log]

Options:
    --config=<dir>  The configuration directory.
    --log           Show the log messages.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_url: Option<String>,
    flag_config: Option<String>,
    flag_log: bool,
}

fn main() {
    gtk::init().unwrap();

    let args: Args = Docopt::new(USAGE)
        .and_then(|decoder| decoder.decode())
        .unwrap_or_else(|error| error.exit());

    if args.flag_log {
        let config = Config {
            time: Some(Error),
            level: Some(Error),
            target: None,
            location: None,
        };
        TermLogger::init(LogLevelFilter::max(), config).unwrap();
    }

    let _app = App::new(args.arg_url, args.flag_config);

    gtk::main();
}
<|endoftext|>


<commit_before>use std::env;
use std::io::{self, Write};
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let args: Vec<String> = env::args().collect();

    for file in &args[1..] {
        readfile(file);
    }
}

fn readfile(file: &String) {
    const BUFFERSIZE: usize = 1024;
    let fd = File::open(file);
    if fd.is_ok() {

        let mut fdu = fd.unwrap();

        let metadata = fdu.metadata().unwrap();

        if metadata.is_file() {
            let mut buffer = [0; BUFFERSIZE];

            loop {
                let rstat = fdu.read(&mut buffer);
                if rstat.is_ok() {
                    io::stdout().write(&buffer);

                    if rstat.unwrap() < BUFFERSIZE {
                        break;
                    }
                } else {
                    break;
                }
            }
            //let mut buf_reader = BufReader::new(fdu);
        }

    } else {
        eprintln!("{}: No such file!", file);
    }
}
<commit_msg>moved printing to separate function<commit_after>use std::env;
use std::io::{self, Write};
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;

fn main() {
    let args: Vec<String> = env::args().collect();

    for file in &args[1..] {
        readfile(file);
    }
}

fn readfile(file: &String) {
    let fd = File::open(file);
    if fd.is_ok() {

        let mut fdu = fd.unwrap();

        let metadata = fdu.metadata().unwrap();

        if metadata.is_file() {

            print_file(fdu)

        }

    } else {
        eprintln!("{}: No such file!", file);
    }
}

fn print_file(mut fd: File) {

    const BUFFERSIZE: usize = 1024;
    let mut buffer = [0; BUFFERSIZE];
    loop {
            let rstat = fd.read(&mut buffer);
            if rstat.is_ok() {
                io::stdout().write(&buffer);

                if rstat.unwrap() < BUFFERSIZE {
                    break;
                }
            } else {
                break;
            }
        }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate fish;

use std::process;
use clap::{Arg, App};

const NAME: &'static str = env!("CARGO_PKG_NAME");
const VERSION: &'static str = env!("CARGO_PKG_VERSION");

fn main() {
    let matches = App::new(NAME)
        .version(VERSION)
        .author("Marc Noirot <marc.noirot@gmail.com>")
        .about("Fish language interpreter")
        .arg(Arg::with_name("INPUT")
            .help("set the input file to use")
            .conflicts_with("code")
            .value_name("FILE")
            .index(1))
        .arg(Arg::with_name("code")
            .short("c")
            .long("code")
            .help("string of instructions to execute instead of FILE")
            .takes_value(true)
            .value_name("CODE"))
        .arg(Arg::with_name("string")
            .short("s")
            .long("string")
            .help("push strings onto the stack before execution starts")
            .takes_value(true)
            .value_name("STRING")
            .multiple(true)
            .number_of_values(1))
        .arg(Arg::with_name("value")
            .short("v")
            .long("value")
            .help("push numbers onto the stack before execution starts")
            .takes_value(true)
            .value_name("NUMBER")
            .multiple(true)
            .number_of_values(1))
        .arg(Arg::with_name("tick")
            .short("t")
            .long("tick")
            .help("define a delay between the execution of each instruction")
            .takes_value(true)
            .value_name("DELAY"))
        .arg(Arg::with_name("always_tick")
            .short("a")
            .long("always-tick")
            .help("make every instruction cause a tick, even whitespace and skipped instructions"))
        .arg(Arg::with_name("debug")
            .short("d")
            .long("debug")
            .help("dump interpreter state before executing an instruction"))
        .get_matches();

    let code_box = match matches.value_of("code") {
        Some(c) => fish::CodeBox::load_from_string(&c),
        None => {
            let input = match matches.value_of("INPUT") {
                Some(v) => v,
                None => {
                    println!("{}", matches.usage());
                    process::exit(1)
                },
            };

            match fish::CodeBox::load_from_file(&input) {
                Ok(cb) => cb,
                Err(e) => {
                    println!("Error: {}", e);
                    process::exit(2)
                }
            }
        }
    };

    let input = std::io::stdin();
    let output = std::io::stdout();

    let mut fish = fish::Interpreter::new(input, output);

    if let Some(strings) = matches.values_of("string") {
        for c in strings {
            fish.push_str(c);
        }
    }

    if let Some(numbers) = matches.values_of("value") {
        for c in numbers {
            let n = match c.parse::<i64>() {
                Ok(v) => v,
                Err(e) => {
                    println!("Error: {}", e);
                    process::exit(2)
                }
            };
            fish.push_i64(n);
        }
    }

    if matches.is_present("debug") {
        fish.trace = true;
    }

    if fish.run(&code_box).is_err() {
        println!("something smells fishy...");
        process::exit(3);
    }

    println!();
}
<commit_msg>Elide unnecessary lifetimes<commit_after>extern crate clap;
extern crate fish;

use std::process;
use clap::{Arg, App};

const NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

fn main() {
    let matches = App::new(NAME)
        .version(VERSION)
        .author("Marc Noirot <marc.noirot@gmail.com>")
        .about("Fish language interpreter")
        .arg(Arg::with_name("INPUT")
            .help("set the input file to use")
            .conflicts_with("code")
            .value_name("FILE")
            .index(1))
        .arg(Arg::with_name("code")
            .short("c")
            .long("code")
            .help("string of instructions to execute instead of FILE")
            .takes_value(true)
            .value_name("CODE"))
        .arg(Arg::with_name("string")
            .short("s")
            .long("string")
            .help("push strings onto the stack before execution starts")
            .takes_value(true)
            .value_name("STRING")
            .multiple(true)
            .number_of_values(1))
        .arg(Arg::with_name("value")
            .short("v")
            .long("value")
            .help("push numbers onto the stack before execution starts")
            .takes_value(true)
            .value_name("NUMBER")
            .multiple(true)
            .number_of_values(1))
        .arg(Arg::with_name("tick")
            .short("t")
            .long("tick")
            .help("define a delay between the execution of each instruction")
            .takes_value(true)
            .value_name("DELAY"))
        .arg(Arg::with_name("always_tick")
            .short("a")
            .long("always-tick")
            .help("make every instruction cause a tick, even whitespace and skipped instructions"))
        .arg(Arg::with_name("debug")
            .short("d")
            .long("debug")
            .help("dump interpreter state before executing an instruction"))
        .get_matches();

    let code_box = match matches.value_of("code") {
        Some(c) => fish::CodeBox::load_from_string(&c),
        None => {
            let input = match matches.value_of("INPUT") {
                Some(v) => v,
                None => {
                    println!("{}", matches.usage());
                    process::exit(1)
                },
            };

            match fish::CodeBox::load_from_file(&input) {
                Ok(cb) => cb,
                Err(e) => {
                    println!("Error: {}", e);
                    process::exit(2)
                }
            }
        }
    };

    let input = std::io::stdin();
    let output = std::io::stdout();

    let mut fish = fish::Interpreter::new(input, output);

    if let Some(strings) = matches.values_of("string") {
        for c in strings {
            fish.push_str(c);
        }
    }

    if let Some(numbers) = matches.values_of("value") {
        for c in numbers {
            let n = match c.parse::<i64>() {
                Ok(v) => v,
                Err(e) => {
                    println!("Error: {}", e);
                    process::exit(2)
                }
            };
            fish.push_i64(n);
        }
    }

    if matches.is_present("debug") {
        fish.trace = true;
    }

    if fish.run(&code_box).is_err() {
        println!("something smells fishy...");
        process::exit(3);
    }

    println!();
}
<|endoftext|>


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

use rgtk::*;
use rgtk::gtk::signals;

fn main() {
    gtk::init();

    let width = 1242;
    let height = 768;
    let sidebar_width = ((width as f32) * 0.20) as i32;
    let top_height = ((height as f32) * 0.8) as i32;

    let mut window = gtk::Window::new(gtk::window_type::TopLevel).unwrap();
    window.set_title("SolidOak");
    window.set_window_position(gtk::window_position::Center);
    window.set_default_size(width, height);

    window.connect(signals::DeleteEvent::new(|_| {
        gtk::main_quit();
        true
    }));

    let new_project_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_project_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_pane = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    let project_tree = gtk::TreeView::new().unwrap();
    project_pane.set_size_request(sidebar_width, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&project_tree, true, true, 0);

    let editor_pane = gtk::TextView::new().unwrap();
    let build_pane = gtk::TextView::new().unwrap();
    editor_pane.set_size_request(-1, top_height);
    let mut content = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    window.show_all();
    gtk::main();
}
<commit_msg>Remove or rename size variables<commit_after>#![feature(globs)]
extern crate rgtk;

use rgtk::*;
use rgtk::gtk::signals;

fn main() {
    gtk::init();

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.8) as i32;

    let mut window = gtk::Window::new(gtk::window_type::TopLevel).unwrap();
    window.set_title("SolidOak");
    window.set_window_position(gtk::window_position::Center);
    window.set_default_size(width, height);

    window.connect(signals::DeleteEvent::new(|_| {
        gtk::main_quit();
        true
    }));

    let new_project_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_project_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_pane = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    let project_tree = gtk::TreeView::new().unwrap();
    project_pane.set_size_request(-1, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&project_tree, true, true, 0);

    let editor_pane = gtk::TextView::new().unwrap();
    let build_pane = gtk::TextView::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    let mut content = gtk::Box::new(gtk::orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    window.show_all();
    gtk::main();
}
<|endoftext|>


<commit_before>
extern crate libc;
extern crate openal;
extern crate cocoa;
extern crate time;
extern crate hyper;
extern crate block;
extern crate rustc_serialize;
#[macro_use] extern crate objc;
extern crate IOKit_sys as iokit;

use std::thread;
use std::io::Read;
use std::string::String;
use std::fs::File;
use libc::{c_void};
use core_foundation::*;
use objc::*;
use objc::runtime::*;
use cocoa::base::{class,id,nil};
use cocoa::foundation::{NSAutoreleasePool, NSString};
use hyper::Client;
use hyper::header::{Connection};
use hyper::status::StatusCode;
use self::block::{ConcreteBlock};
use rustc_serialize::json;

mod core_graphics;
mod core_foundation;
mod alut;
mod event_tap;
mod tickeys;
#[macro_use] mod cocoa_util;
mod consts;
mod settings_ui;
mod pref;

use tickeys::*;
use cocoa_util::*;
use settings_ui::*;
use consts::*;
use pref::*;

fn main()
{
	unsafe { NSAutoreleasePool::new(nil); }

	request_ax();
	begin_check_update(&nsstring_to_string(l10n_str("check_update_url")));

	let schems = load_audio_schemes();
	let pref = Pref::load(&schems);

	let mut tickeys = Tickeys::new(schems);
	tickeys.load_scheme(&get_data_path(&pref.audio_scheme), &pref.audio_scheme);
	tickeys.set_volume(pref.volume);
	tickeys.set_pitch(pref.pitch);
	tickeys.set_on_keydown(Some(handle_keydown)); //handle qaz123
	tickeys.start();

	show_noti(l10n_str("Tickeys_Running"), l10n_str("press_qaz123"), noti_click_callback);
	//relaunch on os wakeup from sleep
	register_os_wake_noti();
	//main loop
	app_run();
}

fn register_os_wake_noti()
{
	println!("register_os_wake_noti()");

	#[allow(unused_variables)]
 	extern fn power_callback(ref_con: *mut c_void, service: iokit::io_service_t,
		msg: u32, msg_args: *mut c_void)
	{
		println!("System Power Callback! ");
		match msg
		{
			iokit::kIOMessageSystemHasPoweredOn =>
			{
				println!("System PoweredOn");
				app_relaunch_self(); //just relaunch;
			},
			_ => {}
		}
	}

	unsafe
	{
		// notification port allocated by IORegisterForSystemPower
	    let mut notify_port_ref: iokit::IONotificationPortRef = std::ptr::null_mut();
	    // notifier object, used to deregister later
	    let mut notifier_object: iokit::io_object_t = 0;
	    // this parameter is passed to the callback
	    let ref_con: *mut c_void = std::ptr::null_mut();
	    // register to receive system sleep notifications
	    let root_port = iokit::IORegisterForSystemPower( ref_con, &mut notify_port_ref as *mut _,
			power_callback, &mut notifier_object as *mut _);

	    if root_port == 0
	    {
	        println!("IORegisterForSystemPower failed\n");
	        return; //ignore for now
	    }
	    // add the notification port to the application runloop
	    core_foundation::CFRunLoopAddSource( core_foundation::CFRunLoopGetCurrent(),
	    	iokit::IONotificationPortGetRunLoopSource(notify_port_ref) as CFRunLoopSourceRef,
	    	core_foundation::kCFRunLoopCommonModes );
	}

}

fn request_ax()
{
	println!("request_ax");

	#[link(name = "ApplicationServices", kind = "framework")]
	extern "system"
	{
	 	fn AXIsProcessTrustedWithOptions (options: id) -> bool;
	}

 	unsafe fn is_enabled(prompt: bool) -> bool
 	{
		let dict: id = msg_send![class("NSDictionary"),
			dictionaryWithObject: (if prompt {kCFBooleanTrue}else{kCFBooleanFalse})
			forKey: kAXTrustedCheckOptionPrompt];

		dict.autorelease();
		return AXIsProcessTrustedWithOptions(dict);
	}

	unsafe
	{
		if is_enabled(false) {return;}

		while !is_enabled(true)
		{
			thread::sleep_ms(3000);

			let alert:id = msg_send![class("NSAlert"), new];
			alert.autorelease();
			let _:id = msg_send![alert, setMessageText: l10n_str("ax_tip")];
			let _:id = msg_send![alert, addButtonWithTitle: l10n_str("quit")];
			let _:id = msg_send![alert, addButtonWithTitle: l10n_str("doneWithThis")];

			let btn:i32 = msg_send![alert, runModal];
			println!("request_ax alert: {}", btn);
			match btn
			{
				1001 => {continue},
				1000 => {app_terminate();},
				_ => {panic!("request_ax");}
			}
		}

		app_relaunch_self();
	}
}

fn load_audio_schemes() -> Vec<AudioScheme>
{
	let path = get_res_path("data/schemes.json");
	let mut file = File::open(path).unwrap();

	let mut json_str = String::with_capacity(512);
	match file.read_to_string(&mut json_str)
	{
		Ok(_) => {},
		Err(e) => panic!("Failed to read json:{}",e)
	}

	json::decode(&json_str).unwrap()
}

fn get_data_path(sub_path: &str) -> String
{
	get_res_path(&("data/".to_string() + sub_path))
}

fn begin_check_update(url: &str)
{
	#[derive(RustcDecodable, RustcEncodable)]
	#[allow(non_snake_case)]
	struct Version
	{
		Version: String,
		WhatsNew: String,
	}

	fn do_job(check_update_url: String, run_loop_ref: usize) -> Result<(), hyper::Error>
	{
		let client = Client::new();
	    let mut resp = try!{ client.get(&check_update_url).header(Connection::close()).send() };
	    if resp.status == StatusCode::Ok
	    {
	    	let mut content = String::new();
			try!{ resp.read_to_string(&mut content) };
	    	println!("Response: {}", content);

	    	if content.contains("Version")
	    	{
	    		let ver:Version = json::decode(&content).unwrap();
	    		println!("ver={}",ver.Version);
	    		if ver.Version != CURRENT_VERSION
	    		{
	    			let cblock : ConcreteBlock<(),(),_> = ConcreteBlock::new(move ||
			    	{
			    		println!("New Version Available!");

			    		let title = l10n_str("newVersion");
			    		let whats_new = unsafe
						{
							NSString::alloc(nil).init_str(
								&format!("{} -> {}: {}",CURRENT_VERSION, ver.Version, ver.WhatsNew)
							).autorelease()
						};

			    		show_noti(title, whats_new, noti_click_callback)
			    	});

			    	let block = & *cblock.copy();
			    	unsafe { CFRunLoopPerformBlock(run_loop_ref as *mut c_void, kCFRunLoopDefaultMode, block); }
		    	}
	    	}
			return Ok(());
	    }else
	    {
	    	println!("Failed to check for update: Status {}", resp.status);
			return Err(hyper::Error::Status);
	    }
	}

	let run_loop_ref = unsafe{CFRunLoopGetCurrent() as usize};
	let check_update_url = url.to_string();
	thread::spawn(move ||
	{
		thread::sleep_ms(1000 * 30); //do it xx seconds later.
		println!("begin_check_update do_job!");

		match do_job(check_update_url, run_loop_ref)
		{
			Ok(()) => {println!("begin_check_update(): Ok");},
			Err(e) => {println!("begin_check_update() Error: {:}", e);}
		}
	});
}

fn handle_keydown(tickeys: &Tickeys, key: u8)
{
	//println!("key: {:}", key);
	let last_keys = tickeys.get_last_keys();
	let last_keys_len = last_keys.len();

	let mut pass = false;
	for seq in OPEN_SETTINGS_KEY_SEQ
	{
		let seq_len = seq.len();
		if last_keys_len < seq_len {return;}

		pass = true;
		//cmp from tail to head
		for i in 1..(seq_len+1)
		{
			if last_keys[last_keys_len - i] != seq[seq_len - i]
			{
				pass = false;
				break;
			}
		}

		if pass { break;}
	}

	if pass
	{
		show_settings(tickeys);
	}
}

fn show_settings(tickeys: &Tickeys)
{
	println!("Settings!");
	unsafe
	{
		SettingsDelegate::get_instance(nil, std::mem::transmute(tickeys));
	}
}

extern fn noti_click_callback(this: &mut Object, _cmd: Sel, center: id, note: id)
{
	println!("noti_click_callback");
	unsafe
	{
		let workspace: id = msg_send![class("NSWorkspace"), sharedWorkspace];
		let url:id = msg_send![class("NSURL"), URLWithString: NSString::alloc(nil).init_str(WEBSITE)];
		let _:bool = msg_send![workspace, openURL: url];

		msg_send![center, removeDeliveredNotification:note]
	}
}
<commit_msg>cleanups<commit_after>
extern crate libc;
extern crate openal;
extern crate cocoa;
extern crate time;
extern crate hyper;
extern crate block;
extern crate rustc_serialize;
#[macro_use] extern crate objc;
extern crate IOKit_sys as iokit;

use std::thread;
use std::io::Read;
use std::string::String;
use std::fs::File;
use libc::{c_void};
use core_foundation::*;
use objc::*;
use objc::runtime::*;
use cocoa::base::{class,id,nil};
use cocoa::foundation::{NSAutoreleasePool, NSString};
use hyper::Client;
use hyper::header::{Connection};
use hyper::status::StatusCode;
use self::block::{ConcreteBlock};
use rustc_serialize::json;

mod core_graphics;
mod core_foundation;
mod alut;
mod event_tap;
mod tickeys;
#[macro_use] mod cocoa_util;
mod consts;
mod settings_ui;
mod pref;

use tickeys::*;
use cocoa_util::*;
use settings_ui::*;
use consts::*;
use pref::*;

fn main()
{
	unsafe { NSAutoreleasePool::new(nil); }

	request_ax();
	begin_check_update(&nsstring_to_string(l10n_str("check_update_url")));

	let schems = load_audio_schemes();
	let pref = Pref::load(&schems);

	let mut tickeys = Tickeys::new(schems);
	tickeys.load_scheme(&get_data_path(&pref.audio_scheme), &pref.audio_scheme);
	tickeys.set_volume(pref.volume);
	tickeys.set_pitch(pref.pitch);
	tickeys.set_on_keydown(Some(handle_keydown)); //handle qaz123
	tickeys.start();

	show_noti(l10n_str("Tickeys_Running"), l10n_str("press_qaz123"), noti_click_callback);
	//relaunch on os wakeup from sleep
	register_os_wake_noti();
	//main loop
	app_run();
}

fn register_os_wake_noti()
{
	println!("register_os_wake_noti()");
	#[allow(unused_variables)]
 	extern fn power_callback(ref_con: *mut c_void, service: iokit::io_service_t,
		msg: u32, msg_args: *mut c_void)
	{
		println!("System Power Callback! ");
		match msg
		{
			iokit::kIOMessageSystemHasPoweredOn =>
			{
				println!("System PoweredOn");
				app_relaunch_self(); //just relaunch;
			},
			_ => {}
		}
	}

	unsafe
	{
		// notification port allocated by IORegisterForSystemPower
	    let mut notify_port_ref: iokit::IONotificationPortRef = std::ptr::null_mut();
	    // notifier object, used to deregister later
	    let mut notifier_object: iokit::io_object_t = 0;
	    // this parameter is passed to the callback
	    let ref_con: *mut c_void = std::ptr::null_mut();
	    // register to receive system sleep notifications
	    let root_port = iokit::IORegisterForSystemPower( ref_con, &mut notify_port_ref as *mut _,
			power_callback, &mut notifier_object as *mut _);

	    if root_port == 0
	    {
	        println!("IORegisterForSystemPower failed\n");
	        return; //ignore for now
	    }
	    // add the notification port to the application runloop
	    core_foundation::CFRunLoopAddSource( core_foundation::CFRunLoopGetCurrent(),
	    	iokit::IONotificationPortGetRunLoopSource(notify_port_ref) as CFRunLoopSourceRef,
	    	core_foundation::kCFRunLoopCommonModes );
	}
}

fn request_ax()
{
	println!("request_ax");
	#[link(name = "ApplicationServices", kind = "framework")]
	extern "system"
	{
	 	fn AXIsProcessTrustedWithOptions (options: id) -> bool;
	}

 	unsafe fn is_enabled(prompt: bool) -> bool
 	{
		let dict: id = msg_send![class("NSDictionary"),
			dictionaryWithObject: (if prompt {kCFBooleanTrue}else{kCFBooleanFalse})
			forKey: kAXTrustedCheckOptionPrompt];
		dict.autorelease();
		return AXIsProcessTrustedWithOptions(dict);
	}

	unsafe
	{
		if is_enabled(false) { return; }
		while !is_enabled(true)
		{
			thread::sleep_ms(3000);

			let alert:id = msg_send![class("NSAlert"), new];
			alert.autorelease();
			let _:id = msg_send![alert, setMessageText: l10n_str("ax_tip")];
			let _:id = msg_send![alert, addButtonWithTitle: l10n_str("quit")];
			let _:id = msg_send![alert, addButtonWithTitle: l10n_str("doneWithThis")];

			let btn:i32 = msg_send![alert, runModal];
			println!("request_ax alert: {}", btn);
			match btn
			{
				1001 => continue,
				1000 => app_terminate(),
				_ => panic!("request_ax")
			}
		}

		app_relaunch_self();
	}
}

fn load_audio_schemes() -> Vec<AudioScheme>
{
	let path = get_res_path("data/schemes.json");
	let mut file = File::open(path).unwrap();

	let mut json_str = String::with_capacity(512);
	match file.read_to_string(&mut json_str)
	{
		Ok(_) => {},
		Err(e) => panic!("Failed to read json:{}",e)
	}
	json::decode(&json_str).unwrap()
}

fn get_data_path(sub_path: &str) -> String
{
	get_res_path(&("data/".to_string() + sub_path))
}

fn begin_check_update(url: &str)
{
	#[derive(RustcDecodable, RustcEncodable)]
	#[allow(non_snake_case)]
	struct Version
	{
		Version: String,
		WhatsNew: String,
	}

	let run_loop_ref = unsafe { CFRunLoopGetCurrent() as usize };
	let check_update_url = url.to_string();
	thread::spawn(move ||
	{
		thread::sleep_ms(1000 * 30); //do it xx seconds later.
		println!("begin_check_update do_job!");
		match do_job(check_update_url, run_loop_ref)
		{
			Ok(()) => println!("begin_check_update(): Ok"),
			Err(e) => println!("begin_check_update() Error: {:}", e)
		}
	});

	fn do_job(check_update_url: String, run_loop_ref: usize) -> Result<(), hyper::Error>
	{
		let client = Client::new();
	    let mut resp = try!{ client.get(&check_update_url).header(Connection::close()).send() };
	    if resp.status == StatusCode::Ok
	    {
	    	let mut content = String::new();
			try!{ resp.read_to_string(&mut content) };
	    	println!("Response: {}", content);

	    	if content.contains("Version")
	    	{
	    		let ver:Version = json::decode(&content).unwrap();
	    		println!("ver={}",ver.Version);
	    		if ver.Version != CURRENT_VERSION
	    		{
	    			let cblock : ConcreteBlock<(),(),_> = ConcreteBlock::new(move ||
			    	{
			    		println!("New Version Available!");
			    		let title = l10n_str("newVersion");
			    		let whats_new = unsafe
						{
							NSString::alloc(nil).init_str(
								&format!("{} -> {}: {}",CURRENT_VERSION, ver.Version, ver.WhatsNew)
							).autorelease()
						};
			    		show_noti(title, whats_new, noti_click_callback)
			    	});

			    	let block = & *cblock.copy();
			    	unsafe { CFRunLoopPerformBlock(run_loop_ref as *mut c_void, kCFRunLoopDefaultMode, block); }
		    	}
	    	}
			return Ok(());
	    }else
	    {
	    	println!("Failed to check for update: Status {}", resp.status);
			return Err(hyper::Error::Status);
	    }
	}

}

fn handle_keydown(tickeys: &Tickeys, key: u8)
{
	//println!("key: {:}", key);
	let last_keys = tickeys.get_last_keys();
	let last_keys_len = last_keys.len();

	let mut pass = false;
	for seq in OPEN_SETTINGS_KEY_SEQ
	{
		let seq_len = seq.len();
		if last_keys_len < seq_len {return;}

		pass = true;
		//cmp from tail to head
		for i in 1..(seq_len+1)
		{
			if last_keys[last_keys_len - i] != seq[seq_len - i]
			{
				pass = false;
				break;
			}
		}

		if pass { break;}
	}

	if pass
	{
		show_settings(tickeys);
	}
}

fn show_settings(tickeys: &Tickeys)
{
	println!("Settings!");
	unsafe
	{
		SettingsDelegate::get_instance(nil, std::mem::transmute(tickeys));
	}
}

extern fn noti_click_callback(this: &mut Object, _cmd: Sel, center: id, note: id)
{
	println!("noti_click_callback");
	unsafe
	{
		let workspace: id = msg_send![class("NSWorkspace"), sharedWorkspace];
		let url:id = msg_send![class("NSURL"), URLWithString: NSString::alloc(nil).init_str(WEBSITE)];
		let _:bool = msg_send![workspace, openURL: url];

		msg_send![center, removeDeliveredNotification:note]
	}
}
<|endoftext|>


<commit_before>use std::env;
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::str::Chars;
use std::rc::Rc;
use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq)]
enum Builtin {
    dot,
    plus,
    minus,
    times,
    divide,
}

impl Builtin {
    fn call(&self, stack: &mut Vec<i32>) {
        match *self {
            Builtin::dot => print!("{}", stack.pop().unwrap()),
            Builtin::plus => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 + n2);
            },
            Builtin::minus => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 - n2);
            },
            Builtin::times => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 * n2);
            },
            Builtin::divide => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 / n2);
            },
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
enum Word {
    custom(Rc<Vec<Branch>>),
    builtin(Builtin),
    int(i32),
    float(f32),
    parenthesis,
    colon,
    semicolon,
    _if,
    then,
    _else,
    dotquote,
}

#[derive(Debug, PartialEq)]
enum Branch {
    custom(Rc<Vec<Branch>>),
    builtin(Builtin),
    int(i32),
    float(f32),
    ifelse(Vec<Branch>, Vec<Branch>),
    dotquote(String)
}

struct Dictionary {
    items: HashMap<String, Word>
}

impl Dictionary {
    fn get(&self, name: &str) -> Option<Word> {
        if let Some(word) = self.get(name) {
            Some(word)
        } else if let Ok(num) = name.parse::<i32>() {
            Some(Word::int(num))
        } else if let Ok(num) = name.parse::<f32>() {
            Some(Word::float(num))
        } else {
            None
        }
    }
    fn set(&mut self, name: &str, value: Word) {
        if self.items.contains_key(name) {
            // XXX display debug message another way
            println!("Redefined {}", name);
        }
        self.items.insert(name.to_owned(), value);
    }
}

impl Default for Dictionary {
    fn default() -> Dictionary {
        let mut dict = HashMap::new();
        dict.insert(".\"".to_owned(), Word::dotquote);
        dict.insert(".".to_owned(), Word::builtin(Builtin::dot));
        dict.insert("+".to_owned(), Word::builtin(Builtin::plus));
        dict.insert("-".to_owned(), Word::builtin(Builtin::minus));
        dict.insert("*".to_owned(), Word::builtin(Builtin::times));
        dict.insert("/".to_owned(), Word::builtin(Builtin::divide));
        dict.insert(":".to_owned(), Word::colon);
        dict.insert(";".to_owned(), Word::semicolon);
        dict.insert("if".to_owned(), Word::_if);
        dict.insert("then".to_owned(), Word::then);
        dict.insert("else".to_owned(), Word::_else);
        dict.insert("(".to_owned(), Word::parenthesis);
        Dictionary{items: dict}
    }
}

fn next_word(chars: &mut Chars) -> Option<String> {
    let mut word = String::new();

    while let Some(c) = chars.next() {
        if (c == ' ' || c == '\n') && !word.is_empty() {
            break;
        }
        word.push(c);
    }

    if word.is_empty() {
        // No input left
        None
    } else {
        Some(word)
    }
}

fn parse_word(word_str: &str, chars: &mut Chars, dict: &mut Dictionary) -> Option<Branch> {
    if let Some(word) = dict.get(word_str) {
        match word {
            Word::custom(x) => Some(Branch::custom(x)),
            Word::builtin(x) => Some(Branch::builtin(x)),
            Word::int(x) => Some(Branch::int(x)),
            Word::float(x) => Some(Branch::float(x)),
            Word::dotquote => {
                let text = chars.take_while(|x| *x != '"').collect();
                Some(Branch::dotquote(text))
            },
            Word::parenthesis => {
                chars.take_while(|x| *x != ')');
                None
            },
            Word::colon => {
                let name = next_word(chars).unwrap();
                let mut inner_branches: Vec<Branch> = Vec::new();
                while let Some(inner_word_str) = next_word(chars) {
                    if dict.get(&inner_word_str) == Some(Word::semicolon) {
                        break;
                    }
                    if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                        inner_branches.push(branch);
                    }
                }
                dict.set(&name, Word::custom(Rc::new(inner_branches)));
                None
            },
            Word::_if => {
                let mut ifbranches = Vec::new();
                let mut elsebranches = Vec::new();
                while let Some(inner_word_str) = next_word(chars) {
                    if dict.get(&inner_word_str) == Some(Word::_else) {
                        while let Some(inner_word_str) = next_word(chars) {
                            if dict.get(&inner_word_str) == Some(Word::then) {
                                break;
                            }
                    }
                        if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                            elsebranches.push(branch);
                        }
                        break;
                    } else if dict.get(&inner_word_str) == Some(Word::then) {
                        break;
                    }
                    if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                        ifbranches.push(branch);
                    }
                }
                Some(Branch::ifelse(ifbranches, elsebranches))
            },
            Word::semicolon | Word::_else | Word::then => {
                panic!("Invalid here");
            }
        }
    } else {
        panic!("Word not in dictionary.");
    }
}

fn parse(code: &mut Chars) -> Vec<Branch> {
    let mut branches: Vec<Branch> = Vec::new();
    let mut word_str = String::new();
    let mut dict = Dictionary::default();
    while let Some(word_str) = next_word(code) {
        if let Some(branch) = parse_word(&word_str, code, &mut dict) {
            branches.push(branch);
        }
    }

    branches
}

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("{:#?}", parse(&mut code.chars()));
}
<commit_msg>Fixes; now runs (parsing only; may have issues)<commit_after>use std::env;
use std::io::prelude::*;
use std::fs::File;
use std::str::Chars;
use std::rc::Rc;
use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq)]
enum Builtin {
    dot,
    plus,
    minus,
    times,
    divide,
}

impl Builtin {
    fn call(&self, stack: &mut Vec<i32>) {
        match *self {
            Builtin::dot => print!("{}", stack.pop().unwrap()),
            Builtin::plus => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 + n2);
            },
            Builtin::minus => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 - n2);
            },
            Builtin::times => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 * n2);
            },
            Builtin::divide => {
                let n2 = stack.pop().unwrap();
                let n1 = stack.pop().unwrap();
                stack.push(n1 / n2);
            },
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
enum Word {
    custom(Rc<Vec<Branch>>),
    builtin(Builtin),
    int(i32),
    float(f32),
    parenthesis,
    colon,
    semicolon,
    _if,
    then,
    _else,
    dotquote,
}

#[derive(Debug, PartialEq)]
enum Branch {
    custom(Rc<Vec<Branch>>),
    builtin(Builtin),
    int(i32),
    float(f32),
    ifelse(Vec<Branch>, Vec<Branch>),
    dotquote(String)
}

struct Dictionary {
    items: HashMap<String, Word>
}

impl Dictionary {
    fn get(&self, name: &str) -> Option<Word> {
        if let Some(word) = self.items.get(name) {
            Some(word.clone())
        } else if let Ok(num) = name.parse::<i32>() {
            Some(Word::int(num))
        } else if let Ok(num) = name.parse::<f32>() {
            Some(Word::float(num))
        } else {
            None
        }
    }
    fn set(&mut self, name: &str, value: Word) {
        if self.items.contains_key(name) {
            // XXX display debug message another way
            println!("Redefined {}", name);
        }
        self.items.insert(name.to_owned(), value);
    }
}

impl Default for Dictionary {
    fn default() -> Dictionary {
        let mut dict = HashMap::new();
        dict.insert(".\"".to_owned(), Word::dotquote);
        dict.insert(".".to_owned(), Word::builtin(Builtin::dot));
        dict.insert("+".to_owned(), Word::builtin(Builtin::plus));
        dict.insert("-".to_owned(), Word::builtin(Builtin::minus));
        dict.insert("*".to_owned(), Word::builtin(Builtin::times));
        dict.insert("/".to_owned(), Word::builtin(Builtin::divide));
        dict.insert(":".to_owned(), Word::colon);
        dict.insert(";".to_owned(), Word::semicolon);
        dict.insert("if".to_owned(), Word::_if);
        dict.insert("then".to_owned(), Word::then);
        dict.insert("else".to_owned(), Word::_else);
        dict.insert("(".to_owned(), Word::parenthesis);
        Dictionary{items: dict}
    }
}

fn next_word(chars: &mut Chars) -> Option<String> {
    let mut word = String::new();

    while let Some(c) = chars.next() {
        if (c == ' ' || c == '\n') {
            if word.is_empty() {
                continue;
            }
            break;
        }
        word.push(c);
    }

    if word.is_empty() {
        // No input left
        None
    } else {
        Some(word)
    }
}

fn parse_word(word_str: &str, chars: &mut Chars, dict: &mut Dictionary) -> Option<Branch> {
    if let Some(word) = dict.get(word_str) {
        match word {
            Word::custom(x) => Some(Branch::custom(x)),
            Word::builtin(x) => Some(Branch::builtin(x)),
            Word::int(x) => Some(Branch::int(x)),
            Word::float(x) => Some(Branch::float(x)),
            Word::dotquote => {
                let text = chars.take_while(|x| *x != '"').collect();
                Some(Branch::dotquote(text))
            },
            Word::parenthesis => {
                // Consumes all characters up to next )
                chars.take_while(|x| *x != ')').count();
                None
            },
            Word::colon => {
                let name = next_word(chars).unwrap();
                let mut inner_branches: Vec<Branch> = Vec::new();
                while let Some(inner_word_str) = next_word(chars) {
                    if dict.get(&inner_word_str) == Some(Word::semicolon) {
                        break;
                    }
                    if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                        inner_branches.push(branch);
                    }
                }
                dict.set(&name, Word::custom(Rc::new(inner_branches)));
                None
            },
            Word::_if => {
                let mut ifbranches = Vec::new();
                let mut elsebranches = Vec::new();
                while let Some(inner_word_str) = next_word(chars) {
                    if dict.get(&inner_word_str) == Some(Word::_else) {
                        while let Some(inner_word_str) = next_word(chars) {
                            if dict.get(&inner_word_str) == Some(Word::then) {
                                break;
                            }
                    }
                        if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                            elsebranches.push(branch);
                        }
                        break;
                    } else if dict.get(&inner_word_str) == Some(Word::then) {
                        break;
                    }
                    if let Some(branch) = parse_word(&inner_word_str, chars, dict) {
                        ifbranches.push(branch);
                    }
                }
                Some(Branch::ifelse(ifbranches, elsebranches))
            },
            Word::semicolon | Word::_else | Word::then => {
                panic!("Invalid here");
            }
        }
    } else {
        panic!("Word '{}' not in dictionary.", word_str);
    }
}

fn parse(code: &mut Chars) -> Vec<Branch> {
    let mut branches: Vec<Branch> = Vec::new();
    let mut dict = Dictionary::default();
    while let Some(word_str) = next_word(code) {
        if let Some(branch) = parse_word(&word_str, code, &mut dict) {
            branches.push(branch);
        }
    }

    branches
}

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("{:#?}", parse(&mut code.chars()));
}
<|endoftext|>


<commit_before>extern crate kernel32;
extern crate user32;
extern crate winapi;
extern crate libc;
extern crate gdi32;

use winapi::windef::HWND;
use winapi::windef::HMENU;
use winapi::windef::HBRUSH;
use winapi::minwindef::HINSTANCE;

use winapi::minwindef::UINT;
use winapi::minwindef::DWORD;
use winapi::minwindef::WPARAM;
use winapi::minwindef::LPARAM;
use winapi::minwindef::LRESULT;
use winapi::minwindef::BOOL;
use winapi::winnt::LPCWSTR;

use winapi::winuser::WS_OVERLAPPEDWINDOW;
use winapi::winuser::WS_VISIBLE;
use winapi::winuser::WNDCLASSW;
use winapi::winuser::CF_BITMAP;

use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr;

const g_class_name: &'static str = "winshot";

// Rectangle represents screen coordinates
struct Rectangle {
	x: i32,
	y: i32,
	x2: i32,
	y2: i32,
}

macro_rules! switch(
    ($var:expr { $($pred:expr => $body:block),+ _ => $default:block }) => (
        $(if $var == $pred $body else)+

        $default
    )
)

// convert normal string to wide string
#[allow(dead_code)]
fn to_wstring(str: &str) -> *const u16 {
	let v: Vec<u16> = OsStr::new(str).encode_wide() . chain(Some(0).into_iter()).collect();
	v.as_ptr()
}

// This is our window message handler function. Currently,
// it only processes the WM_DESTROY message to exit our window properly on close event.
#[allow(dead_code)]
pub unsafe extern "system" fn window_proc(h_wnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
	use winapi::winuser::*;

	switch!(msg {
		WM_DESTROY => {
			user32::PostQuitMessage(0);
		},
		WM_MOUSEMOVE => {
			return 0 as LRESULT;
		},
		WM_LBUTTONDOWN => {
			return 0 as LRESULT;
		},
		WM_LBUTTONUP => {
			return 0 as LRESULT;
		},
		_ => {
			user32::DefWindowProcW(h_wnd, msg, w_param, l_param);
		},
	})
}

// TODO figure out how the heck to use pointers
fn normalize_coords(rect: Rectangle) -> Rectangle {
    let Rectangle{mut x, mut y, mut x2, mut y2} = rect;

    if x > x2 {
        let tmp = x;
        x = x2;
        x2 = tmp;
    }
    if y > y2 {
        let tmp = y;
        y = y2;
        y2 = tmp;
    }

    Rectangle{x: x, y: y, x2: x2, y2: y2}
}

fn get_screen_rect() -> Rectangle {
	Rectangle{
		x /* left */: user32::GetSystemMetrics(winapi::winuser::SM_XVIRTUALSCREEN),
		y /* top */: user32::GetSystemMetrics(winapi::winuser::SM_YVIRTUALSCREEN),
		x2 /* right */: user32::GetSystemMetrics(winapi::winuser::SM_CXVIRTUALSCREEN) + user32::GetSystemMetrics(winapi::winuser::SM_XVIRTUALSCREEN),
		y2 /* bottom */: user32::GetSystemMetrics(winapi::winuser::SM_CYVIRTUALSCREEN) + user32::GetSystemMetrics(winapi::winuser::SM_CYVIRTUALSCREEN),
	}
}

fn capture_screen_clipboard(hwnd: HWND, mut rect: Rectangle) -> bool {
	// normalize coordinates
	let rect = normalize_coords(rect);
	let w = rect.x2 - rect.x;
	let h = rect.y2 - rect.y;

	let screen_dc = user32::GetDC(0 as HWND);
	let shot_dc = gdi32::CreateCompatibleDC(screen_dc);
	let shot_bitmap = gdi32::CreateCompatibleBitmap(screen_dc, w, h);
	let old_obj = gdi32::SelectObject(shot_dc, shot_bitmap);

	if !gdi32::BitBlt(shot_dc, 0, 0, w, h, screen_dc, rect.x, rect.y, 0 as DWORD) {
		println!("{:?}", "BitBlt failed? Perhaps.");
		false
	}

	let mut ret = false;
	if user32::OpenClipboard(hwnd) as BOOL == winapi::minwindef::TRUE {
		if user32::EmptyClipboard() as BOOL == winapi::minwindef::TRUE {
			if user32::SetClipboardData(CF_BITMAP, shot_bitmap) as BOOL == winapi::minwindef::TRUE {
				ret = true;
			}
			user32::CloseClipboard();
		}
	} else {
		println!("{:?}", "could not open clipboard");
	}

	// Clean up
	gdi32::DeleteDC(shot_dc);
	gdi32::DeleteDC(screen_dc);
	gdi32::SelectObject(shot_dc, old_obj);

	return ret;
}

fn main() {
	unsafe {
		let hInstance = kernel32::GetModuleHandleW(0 as LPCWSTR);
		let m_class_name = to_wstring(g_class_name);
		let wnd = WNDCLASSW {
			hInstance: hInstance,
			lpszClassName: m_class_name,
			lpszMenuName: 0 as LPCWSTR,
			lpfnWndProc: Some(window_proc),
			cbClsExtra: 0,
			cbWndExtra: 0,
			style: winapi::winuser::CS_HREDRAW | winapi::winuser::CS_VREDRAW,
			hbrBackground: 0,
			hCursor: user32::LoadCursorW(0 as HINSTANCE, winapi::winuser::IDC_CROSS),
			hIcon: user32::LoadIconW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
		};

		user32::RegisterClassW(&wnd);
		let rect = get_screen_rect();
		let exstyle = winapi::winuser::WS_EX_TRANSPARENT;
		let style = winapi::winuser::WS_POPUP;

		let win = user32::CreateWindowExW(exstyle,
			m_class_name,
			m_class_name,
			style,
			// dimensions
			0, 0, rect.x2, rect.y2,
			0, // no parent
			0 as HWND, // no menu
			hInstance, // module_instance
			0 as LPARAM,
		);

		user32::ShowWindow(win, winapi::winuser::SW_SHOW);
		user32::ShowWindow(win, winapi::winuser::SW_SHOWMAXIMIZED);
		user32::SetForegroundWindow(win);

		capture_screen_clipboard(win, Rectangle{
			x: 0,
			y: 0,
			x2: 200,
			y2: 200,
		});
		user32::DestroyWindow(win);
	}
}

// fn main() {
// 	// Here our unsafe code goes
// 	unsafe {
// 		// First we hide the console window
// 		hide_console_window();

// 		// Then we initialize the WNDCLASS structure
// 		let m_class_name = to_wstring(g_class_name);
// 		let wnd = WNDCLASSW {
// 			hInstance: 0 as HINSTANCE,
// 			lpszClassName = m_class_name,
// 			lpszMenuName: 0 as LPCWSTR,
// 			lpfnWndProc: Some(window_proc),

// 			style: winapi::winuser::CS_HREDRAW | winapi::winuser::CS_VREDRAW,

// 			cbClsExtra: 0,
// 			cbWndExtra: 0,

// 			hIcon: user32::LoadIconW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
// 			hCursor: user32::LoadCursorW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
// 			hbrBackground: 16 as HBRUSH,

// 		};

// 		// Register our class
// 		user32::RegisterClassW(&wnd);

// 		let h_wnd_desktop = user32::GetDesktopWindow();

// 		user32::CreateWindowExA(0, "my_window".as_ptr() as *mut _,
// 			"Simple Window".as_ptr() as *mut _, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
// 			0, 0, 400, 400, h_wnd_desktop, 0 as HMENU, 0 as HINSTANCE,
// 			std::ptr::null_mut());

// 		let mut msg = winapi::winuser::MSG {
// 			hwnd: 0 as HWND,
// 			message: 0 as UINT,
// 			wParam: 0 as WPARAM,
// 			lParam: 0 as LPARAM,
// 			time: 0 as DWORD,
// 			pt : winapi::windef::POINT{ x: 0, y: 0, },
// 		};

// 		// Kick off the application loop
// 		loop {
// 			let pm = user32::PeekMessageW(&mut msg, 0 as HWND, 0, 0, winapi::winuser::PM_REMOVE);

// 			if pm == 0 {
// 				continue;
// 			}

// 			if msg.message == winapi::winuser::WM_QUIT {
// 				break;
// 			}

// 			user32::TranslateMessage(&mut msg);
// 			user32::DispatchMessageW(&mut msg);
// 		}
// 	}
// }<commit_msg>Remove commented code<commit_after>extern crate kernel32;
extern crate user32;
extern crate winapi;
extern crate libc;
extern crate gdi32;

use winapi::windef::HWND;
use winapi::windef::HMENU;
use winapi::windef::HBRUSH;
use winapi::minwindef::HINSTANCE;

use winapi::minwindef::UINT;
use winapi::minwindef::DWORD;
use winapi::minwindef::WPARAM;
use winapi::minwindef::LPARAM;
use winapi::minwindef::LRESULT;
use winapi::minwindef::BOOL;
use winapi::winnt::LPCWSTR;

use winapi::winuser::WS_OVERLAPPEDWINDOW;
use winapi::winuser::WS_VISIBLE;
use winapi::winuser::WNDCLASSW;
use winapi::winuser::CF_BITMAP;

use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr;

const g_class_name: &'static str = "winshot";

// Rectangle represents screen coordinates
struct Rectangle {
	x: i32,
	y: i32,
	x2: i32,
	y2: i32,
}

macro_rules! switch(
    ($var:expr { $($pred:expr => $body:block),+ _ => $default:block }) => (
        $(if $var == $pred $body else)+

        $default
    )
)

// convert normal string to wide string
#[allow(dead_code)]
fn to_wstring(str: &str) -> *const u16 {
	let v: Vec<u16> = OsStr::new(str).encode_wide() . chain(Some(0).into_iter()).collect();
	v.as_ptr()
}

// This is our window message handler function. Currently,
// it only processes the WM_DESTROY message to exit our window properly on close event.
#[allow(dead_code)]
pub unsafe extern "system" fn window_proc(h_wnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
	use winapi::winuser::*;

	switch!(msg {
		WM_DESTROY => {
			user32::PostQuitMessage(0);
		},
		WM_MOUSEMOVE => {
			return 0 as LRESULT;
		},
		WM_LBUTTONDOWN => {
			return 0 as LRESULT;
		},
		WM_LBUTTONUP => {
			return 0 as LRESULT;
		},
		_ => {
			user32::DefWindowProcW(h_wnd, msg, w_param, l_param);
		},
	})
}

// TODO figure out how the heck to use pointers
fn normalize_coords(rect: Rectangle) -> Rectangle {
    let Rectangle{mut x, mut y, mut x2, mut y2} = rect;

    if x > x2 {
        let tmp = x;
        x = x2;
        x2 = tmp;
    }
    if y > y2 {
        let tmp = y;
        y = y2;
        y2 = tmp;
    }

    Rectangle{x: x, y: y, x2: x2, y2: y2}
}

fn get_screen_rect() -> Rectangle {
	Rectangle{
		x /* left */: user32::GetSystemMetrics(winapi::winuser::SM_XVIRTUALSCREEN),
		y /* top */: user32::GetSystemMetrics(winapi::winuser::SM_YVIRTUALSCREEN),
		x2 /* right */: user32::GetSystemMetrics(winapi::winuser::SM_CXVIRTUALSCREEN) + user32::GetSystemMetrics(winapi::winuser::SM_XVIRTUALSCREEN),
		y2 /* bottom */: user32::GetSystemMetrics(winapi::winuser::SM_CYVIRTUALSCREEN) + user32::GetSystemMetrics(winapi::winuser::SM_CYVIRTUALSCREEN),
	}
}

fn capture_screen_clipboard(hwnd: HWND, mut rect: Rectangle) -> bool {
	// normalize coordinates
	let rect = normalize_coords(rect);
	let w = rect.x2 - rect.x;
	let h = rect.y2 - rect.y;

	let screen_dc = user32::GetDC(0 as HWND);
	let shot_dc = gdi32::CreateCompatibleDC(screen_dc);
	let shot_bitmap = gdi32::CreateCompatibleBitmap(screen_dc, w, h);
	let old_obj = gdi32::SelectObject(shot_dc, shot_bitmap);

	if !gdi32::BitBlt(shot_dc, 0, 0, w, h, screen_dc, rect.x, rect.y, 0 as DWORD) {
		println!("{:?}", "BitBlt failed? Perhaps.");
		false
	}

	let mut ret = false;
	if user32::OpenClipboard(hwnd) as BOOL == winapi::minwindef::TRUE {
		if user32::EmptyClipboard() as BOOL == winapi::minwindef::TRUE {
			if user32::SetClipboardData(CF_BITMAP, shot_bitmap) as BOOL == winapi::minwindef::TRUE {
				ret = true;
			}
			user32::CloseClipboard();
		}
	} else {
		println!("{:?}", "could not open clipboard");
	}

	// Clean up
	gdi32::DeleteDC(shot_dc);
	gdi32::DeleteDC(screen_dc);
	gdi32::SelectObject(shot_dc, old_obj);

	return ret;
}

fn main() {
	unsafe {
		let hInstance = kernel32::GetModuleHandleW(0 as LPCWSTR);
		let m_class_name = to_wstring(g_class_name);
		let wnd = WNDCLASSW {
			hInstance: hInstance,
			lpszClassName: m_class_name,
			lpszMenuName: 0 as LPCWSTR,
			lpfnWndProc: Some(window_proc),
			cbClsExtra: 0,
			cbWndExtra: 0,
			style: winapi::winuser::CS_HREDRAW | winapi::winuser::CS_VREDRAW,
			hbrBackground: 0,
			hCursor: user32::LoadCursorW(0 as HINSTANCE, winapi::winuser::IDC_CROSS),
			hIcon: user32::LoadIconW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
		};

		user32::RegisterClassW(&wnd);
		let rect = get_screen_rect();
		let exstyle = winapi::winuser::WS_EX_TRANSPARENT;
		let style = winapi::winuser::WS_POPUP;

		let win = user32::CreateWindowExW(exstyle,
			m_class_name,
			m_class_name,
			style,
			// dimensions
			0, 0, rect.x2, rect.y2,
			0, // no parent
			0 as HWND, // no menu
			hInstance, // module_instance
			0 as LPARAM,
		);

		user32::ShowWindow(win, winapi::winuser::SW_SHOW);
		user32::ShowWindow(win, winapi::winuser::SW_SHOWMAXIMIZED);
		user32::SetForegroundWindow(win);

		capture_screen_clipboard(win, Rectangle{
			x: 0,
			y: 0,
			x2: 200,
			y2: 200,
		});
		user32::DestroyWindow(win);
	}
}<|endoftext|>