<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;

#[cfg(test)] extern crate toml;

extern crate libimagstore;
extern crate libimagrt;
extern crate libimagentrytag;
extern crate libimagerror;

#[cfg(test)]
#[macro_use]
extern crate libimagutil;

#[cfg(not(test))]
extern crate libimagutil;

#[cfg(test)]
extern crate toml_query;

#[cfg(test)]
extern crate env_logger;

use std::path::PathBuf;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagentrytag::tagable::Tagable;
use libimagentrytag::tag::Tag;
use libimagerror::trace::{trace_error, trace_error_exit};
use libimagstore::storeid::StoreId;
use libimagutil::warn_exit::warn_exit;

use clap::ArgMatches;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-store",
                                    &version!()[..],
                                    "Direct interface to the store. Use with great care!",
                                    build_ui);

    let id = rt.cli().value_of("id").unwrap(); // enforced by clap
    rt.cli()
        .subcommand_name()
        .map_or_else(
            || {
                let id = PathBuf::from(id);
                let add = get_add_tags(rt.cli());
                let rem = get_remove_tags(rt.cli());
                alter(&rt, id, add, rem);
            },
            |name| {
                let id = PathBuf::from(id);
                debug!("Call: {}", name);
                match name {
                    "list" => list(id, &rt),
                    _ => {
                        warn!("Unknown command");
                        // More error handling
                    },
                };
            });
}

fn alter(rt: &Runtime, id: PathBuf, add: Option<Vec<Tag>>, rem: Option<Vec<Tag>>) {
    let path = {
        match StoreId::new(Some(rt.store().path().clone()), id) {
            Err(e) => trace_error_exit(&e, 1),
            Ok(s) => s,
        }
    };
    debug!("path = {:?}", path);

    match rt.store().get(path) {
        Ok(Some(mut e)) => {
            add.map(|tags| {
                for tag in tags {
                    debug!("Adding tag '{:?}'", tag);
                    if let Err(e) = e.add_tag(tag) {
                        trace_error(&e);
                    }
                }
            }); // it is okay to ignore a None here

            rem.map(|tags| {
                for tag in tags {
                    debug!("Removing tag '{:?}'", tag);
                    if let Err(e) = e.remove_tag(tag) {
                        trace_error(&e);
                    }
                }
            }); // it is okay to ignore a None here
        },

        Ok(None) => {
            info!("No entry found.");
        },

        Err(e) => {
            info!("No entry.");
            trace_error(&e);
        },
    }
}

fn list(id: PathBuf, rt: &Runtime) {
    let path = match StoreId::new(Some(rt.store().path().clone()), id) {
        Err(e) => trace_error_exit(&e, 1),
        Ok(s)  => s,
    };
    debug!("path = {:?}", path);

    let entry = match rt.store().get(path.clone()) {
        Ok(Some(e)) => e,
        Ok(None) => warn_exit("No entry found.", 1),

        Err(e) => {
            warn!("Could not get entry '{:?}'", path);
            trace_error_exit(&e, 1);
        },
    };

    let scmd = rt.cli().subcommand_matches("list").unwrap(); // safe, we checked in main()

    let json_out = scmd.is_present("json");
    let line_out = scmd.is_present("linewise");
    let sepp_out = scmd.is_present("sep");
    let mut comm_out = scmd.is_present("commasep");

    if !vec![json_out, line_out, comm_out, sepp_out].iter().any(|v| *v) {
        // None of the flags passed, go to default
        comm_out = true;
    }

    let tags = entry.get_tags();
    if tags.is_err() {
        trace_error_exit(&tags.unwrap_err(), 1);
    }
    let tags = tags.unwrap();

    if json_out {
        unimplemented!()
    }

    if line_out {
        for tag in &tags {
            println!("{}", tag);
        }
    }

    if sepp_out {
        let sepp = scmd.value_of("sep").unwrap(); // we checked before
        println!("{}", tags.join(sepp));
    }

    if comm_out {
        println!("{}", tags.join(", "));
    }
}

/// Get the tags which should be added from the commandline
///
/// Returns none if the argument was not specified
fn get_add_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
    let a = "add-tags";
    extract_tags(matches, a, '+')
        .or_else(|| matches.values_of(a).map(|values| values.map(String::from).collect()))
}

/// Get the tags which should be removed from the commandline
///
/// Returns none if the argument was not specified
fn get_remove_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
    let r = "remove-tags";
    extract_tags(matches, r, '+')
        .or_else(|| matches.values_of(r).map(|values| values.map(String::from).collect()))
}

fn extract_tags(matches: &ArgMatches, specifier: &str, specchar: char) -> Option<Vec<Tag>> {
    if let Some(submatch) = matches.subcommand_matches("tags") {
        submatch.values_of(specifier)
            .map(|values| values.map(String::from).collect())
    } else {
        matches.values_of("specify-tags")
            .map(|argmatches| {
                argmatches
                    .map(String::from)
                    .filter(|s| s.starts_with(specchar))
                    .map(|s| {
                        String::from(s.split_at(1).1)
                    })
                    .collect()
            })
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;
    use std::ffi::OsStr;

    use toml::value::Value;
    use toml_query::read::TomlValueReadExt;
    use toml_query::error::Result as TomlQueryResult;

    use libimagrt::runtime::Runtime;
    use libimagstore::storeid::StoreId;
    use libimagstore::store::{Result as StoreResult, FileLockEntry, Entry};

    use super::*;

    make_mock_app! {
        app "imag-tag";
        modulename mock;
        version "0.4.0";
        with help "imag-tag mocking app";
    }
    use self::mock::generate_test_runtime;

    fn create_test_default_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> StoreResult<StoreId> {
        let mut path = PathBuf::new();
        path.set_file_name(name);

        let default_entry = Entry::new(StoreId::new_baseless(PathBuf::from("")).unwrap()).to_str();

        let id = StoreId::new_baseless(path)?;
        let mut entry = rt.store().create(id.clone())?;
        entry.get_content_mut().push_str(&default_entry);

        Ok(id)
    }

    fn get_entry_tags<'a>(entry: &'a FileLockEntry<'a>) -> TomlQueryResult<Option<&'a Value>> {
        entry.get_header().read(&"tag.values".to_owned())
    }

    fn tags_toml_value<'a, I: IntoIterator<Item = &'static str>>(tags: I) -> Value {
        Value::Array(tags.into_iter().map(|s| Value::String(s.to_owned())).collect())
    }

    fn setup_logging() {
        use env_logger;
        let _ = env_logger::init().unwrap_or(());
    }

    #[test]
    fn test_tag_add_adds_tag() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-add-adds-tags";
        let rt = generate_test_runtime(vec![name, "--add", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        debug!("Getting 'add' tags");
        let add = get_add_tags(rt.cli());
        debug!("Add-tags: {:?}", add);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();

        let test_tags  = get_entry_tags(&test_entry);
        assert!(test_tags.is_ok(), "Should be Ok(_) = {:?}", test_tags);

        let test_tags  = test_tags.unwrap();
        assert!(test_tags.is_some(), "Should be Some(_) = {:?}", test_tags);
        let test_tags  = test_tags.unwrap();

        assert_ne!(*test_tags, tags_toml_value(vec![]));
        assert_eq!(*test_tags, tags_toml_value(vec!["foo"]));
    }

    #[test]
    fn test_tag_add_more_than_remove_adds_tags() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-add-more-than-remove-adds-tags";
        let rt = generate_test_runtime(vec![name,
                                       "--add", "foo",
                                       "--add", "bar",
                                       "--add", "baz",
                                       "--add", "bub",
                                       "--remove", "foo",
                                       "--remove", "bar",
                                       "--remove", "baz",
        ]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        // Manually add tags
        let add = get_add_tags(rt.cli());

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec!["bub"]));
    }

    #[test]
    fn test_tag_remove_removes_tag() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-remove-removes-tag";
        let rt = generate_test_runtime(vec![name, "--remove", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
    }

    #[test]
    fn test_tag_remove_removes_only_to_remove_tag() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-remove-removes-only-to-remove-tag-doesnt-crash-on-nonexistent-tag";
        let rt = generate_test_runtime(vec![name, "--remove", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned(), "bar".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec!["bar"]));
    }

    #[test]
    fn test_tag_remove_removes_but_doesnt_crash_on_nonexistent_tag() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-remove-removes-but-doesnt-crash-on-nonexistent-tag";
        let rt = generate_test_runtime(vec![name, "--remove", "foo", "--remove", "bar"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
    }

}

<commit_msg>Add debugging output<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;

#[cfg(test)] extern crate toml;

extern crate libimagstore;
extern crate libimagrt;
extern crate libimagentrytag;
extern crate libimagerror;

#[cfg(test)]
#[macro_use]
extern crate libimagutil;

#[cfg(not(test))]
extern crate libimagutil;

#[cfg(test)]
extern crate toml_query;

#[cfg(test)]
extern crate env_logger;

use std::path::PathBuf;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagentrytag::tagable::Tagable;
use libimagentrytag::tag::Tag;
use libimagerror::trace::{trace_error, trace_error_exit};
use libimagerror::trace::MapErrTrace;
use libimagstore::storeid::StoreId;
use libimagutil::warn_exit::warn_exit;

use clap::ArgMatches;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-store",
                                    &version!()[..],
                                    "Direct interface to the store. Use with great care!",
                                    build_ui);

    let id = rt.cli().value_of("id").unwrap(); // enforced by clap
    rt.cli()
        .subcommand_name()
        .map_or_else(
            || {
                let id = PathBuf::from(id);
                let add = get_add_tags(rt.cli());
                let rem = get_remove_tags(rt.cli());
                alter(&rt, id, add, rem);
            },
            |name| {
                let id = PathBuf::from(id);
                debug!("Call: {}", name);
                match name {
                    "list" => list(id, &rt),
                    _ => {
                        warn!("Unknown command");
                        // More error handling
                    },
                };
            });
}

fn alter(rt: &Runtime, id: PathBuf, add: Option<Vec<Tag>>, rem: Option<Vec<Tag>>) {
    let path = {
        match StoreId::new(Some(rt.store().path().clone()), id) {
            Err(e) => trace_error_exit(&e, 1),
            Ok(s) => s,
        }
    };
    debug!("path = {:?}", path);

    match rt.store().get(path) {
        Ok(Some(mut e)) => {
            debug!("Entry header now = {:?}", e.get_header());

            add.map(|tags| {
                    debug!("Adding tags = '{:?}'", tags);
                    for tag in tags {
                        debug!("Adding tag '{:?}'", tag);
                        if let Err(e) = e.add_tag(tag) {
                            trace_error(&e);
                        } else {
                            debug!("Adding tag worked");
                        }
                    }
                }); // it is okay to ignore a None here

            debug!("Entry header now = {:?}", e.get_header());

            rem.map(|tags| {
                debug!("Removing tags = '{:?}'", tags);
                for tag in tags {
                    debug!("Removing tag '{:?}'", tag);
                    if let Err(e) = e.remove_tag(tag) {
                        trace_error(&e);
                    }
                }
            }); // it is okay to ignore a None here

            debug!("Entry header now = {:?}", e.get_header());

        },

        Ok(None) => {
            info!("No entry found.");
        },

        Err(e) => {
            info!("No entry.");
            trace_error(&e);
        },
    }
}

fn list(id: PathBuf, rt: &Runtime) {
    let path = match StoreId::new(Some(rt.store().path().clone()), id) {
        Err(e) => trace_error_exit(&e, 1),
        Ok(s)  => s,
    };
    debug!("path = {:?}", path);

    let entry = match rt.store().get(path.clone()) {
        Ok(Some(e)) => e,
        Ok(None) => warn_exit("No entry found.", 1),

        Err(e) => {
            warn!("Could not get entry '{:?}'", path);
            trace_error_exit(&e, 1);
        },
    };

    let scmd = rt.cli().subcommand_matches("list").unwrap(); // safe, we checked in main()

    let json_out = scmd.is_present("json");
    let line_out = scmd.is_present("linewise");
    let sepp_out = scmd.is_present("sep");
    let mut comm_out = scmd.is_present("commasep");

    if !vec![json_out, line_out, comm_out, sepp_out].iter().any(|v| *v) {
        // None of the flags passed, go to default
        comm_out = true;
    }

    let tags = entry.get_tags();
    if tags.is_err() {
        trace_error_exit(&tags.unwrap_err(), 1);
    }
    let tags = tags.unwrap();

    if json_out {
        unimplemented!()
    }

    if line_out {
        for tag in &tags {
            println!("{}", tag);
        }
    }

    if sepp_out {
        let sepp = scmd.value_of("sep").unwrap(); // we checked before
        println!("{}", tags.join(sepp));
    }

    if comm_out {
        println!("{}", tags.join(", "));
    }
}

/// Get the tags which should be added from the commandline
///
/// Returns none if the argument was not specified
fn get_add_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
    let a = "add-tags";
    extract_tags(matches, a, '+')
        .or_else(|| matches.values_of(a).map(|values| values.map(String::from).collect()))
}

/// Get the tags which should be removed from the commandline
///
/// Returns none if the argument was not specified
fn get_remove_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
    let r = "remove-tags";
    extract_tags(matches, r, '+')
        .or_else(|| matches.values_of(r).map(|values| values.map(String::from).collect()))
}

fn extract_tags(matches: &ArgMatches, specifier: &str, specchar: char) -> Option<Vec<Tag>> {
    if let Some(submatch) = matches.subcommand_matches("tags") {
        submatch.values_of(specifier)
            .map(|values| values.map(String::from).collect())
    } else {
        matches.values_of("specify-tags")
            .map(|argmatches| {
                argmatches
                    .map(String::from)
                    .filter(|s| s.starts_with(specchar))
                    .map(|s| {
                        String::from(s.split_at(1).1)
                    })
                    .collect()
            })
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;
    use std::ffi::OsStr;

    use toml::value::Value;
    use toml_query::read::TomlValueReadExt;
    use toml_query::error::Result as TomlQueryResult;

    use libimagrt::runtime::Runtime;
    use libimagstore::storeid::StoreId;
    use libimagstore::store::{Result as StoreResult, FileLockEntry, Entry};

    use super::*;

    make_mock_app! {
        app "imag-tag";
        modulename mock;
        version "0.4.0";
        with help "imag-tag mocking app";
    }
    use self::mock::generate_test_runtime;

    fn create_test_default_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> StoreResult<StoreId> {
        let mut path = PathBuf::new();
        path.set_file_name(name);

        let default_entry = Entry::new(StoreId::new_baseless(PathBuf::from("")).unwrap()).to_str();

        let id = StoreId::new_baseless(path)?;
        let mut entry = rt.store().create(id.clone())?;
        entry.get_content_mut().push_str(&default_entry);

        Ok(id)
    }

    fn get_entry_tags<'a>(entry: &'a FileLockEntry<'a>) -> TomlQueryResult<Option<&'a Value>> {
        entry.get_header().read(&"tag.values".to_owned())
    }

    fn tags_toml_value<'a, I: IntoIterator<Item = &'static str>>(tags: I) -> Value {
        Value::Array(tags.into_iter().map(|s| Value::String(s.to_owned())).collect())
    }

    fn setup_logging() {
        use env_logger;
        let _ = env_logger::init().unwrap_or(());
    }

    #[test]
    fn test_tag_add_adds_tag() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-add-adds-tags";
        let rt = generate_test_runtime(vec![name, "--add", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        debug!("Getting 'add' tags");
        let add = get_add_tags(rt.cli());
        debug!("Add-tags: {:?}", add);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();

        let test_tags  = get_entry_tags(&test_entry);
        assert!(test_tags.is_ok(), "Should be Ok(_) = {:?}", test_tags);

        let test_tags  = test_tags.unwrap();
        assert!(test_tags.is_some(), "Should be Some(_) = {:?}", test_tags);
        let test_tags  = test_tags.unwrap();

        assert_ne!(*test_tags, tags_toml_value(vec![]));
        assert_eq!(*test_tags, tags_toml_value(vec!["foo"]));
    }

    #[test]
    fn test_tag_add_more_than_remove_adds_tags() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-add-more-than-remove-adds-tags";
        let rt = generate_test_runtime(vec![name,
                                       "--add", "foo",
                                       "--add", "bar",
                                       "--add", "baz",
                                       "--add", "bub",
                                       "--remove", "foo",
                                       "--remove", "bar",
                                       "--remove", "baz",
        ]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        // Manually add tags
        let add = get_add_tags(rt.cli());

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec!["bub"]));
    }

    #[test]
    fn test_tag_remove_removes_tag() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-remove-removes-tag";
        let rt = generate_test_runtime(vec![name, "--remove", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
    }

    #[test]
    fn test_tag_remove_removes_only_to_remove_tag() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-remove-removes-only-to-remove-tag-doesnt-crash-on-nonexistent-tag";
        let rt = generate_test_runtime(vec![name, "--remove", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned(), "bar".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec!["bar"]));
    }

    #[test]
    fn test_tag_remove_removes_but_doesnt_crash_on_nonexistent_tag() {
        setup_logging();
        debug!("Generating runtime");
        let name = "test-tag-remove-removes-but-doesnt-crash-on-nonexistent-tag";
        let rt = generate_test_runtime(vec![name, "--remove", "foo", "--remove", "bar"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, name).unwrap();
        let id = PathBuf::from(String::from(name));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
    }

}

<|endoftext|>


<commit_before>use ast::*;
use std::collections::HashMap;
use runtime_error::RuntimeError;

#[derive(Clone, Debug)]
pub struct State {
  pub mem: Vec<HashMap<String, Binding>>,
}

#[derive(Clone, Debug)]
pub enum Binding {
  Var(Box<Expr>),
  Const(Box<Expr>),
}

impl State {
  pub fn new() -> State {
    return State {
      mem: vec!(HashMap::new()),
    }
  }

  fn first_map_for(&mut self, x: String) -> Option<&mut HashMap<String, Binding>> {
    self.mem.iter_mut().rev().find(|map| map.contains_key(&x))
  }

  pub fn alloc(&mut self, x: String, v1: Expr) -> Result<(), RuntimeError> {
    let binding = Binding::Var(Box::new(v1));

    match self.mem.last_mut() {
      Some(map) => { map.insert(x, binding); () },
      None => return Err(RuntimeError::InvalidMemoryState("no memory frame for var allocation".to_string())),
    }

    Ok(())
  }

  pub fn alloc_const(&mut self, x: String, v1: Expr) ->Result<(), RuntimeError> {
    let binding = Binding::Const(Box::new(v1));

    match self.mem.last_mut() {
      Some(map) => { map.insert(x, binding); () },
      None => return Err(RuntimeError::InvalidMemoryState("no memory frame for const allocation".to_string())),
    }

    Ok(())
  }

  pub fn assign(&mut self, x: String, v1: Expr) -> Result<(), RuntimeError> {
    let mut map_option = self.first_map_for(x.clone());
    let map = map_option.as_mut().unwrap();

    let binding = map.get_mut(&x).unwrap().clone();

    match binding {
      Binding::Var(_) => map.insert(x, Binding::Var(Box::new(v1))),
      Binding::Const(_) => return Err(RuntimeError::InvalidConstAssignment(v1, x)),
    };
    Ok(())
  }

  pub fn get(&mut self, x: String) -> Option<Expr> {
    match self.first_map_for(x.clone()) {
      Some(map) => match map.get(&x).unwrap().clone() {
        Binding::Var(e) => Some(*e),
        Binding::Const(e) => Some(*e),
      },
      None => None,
    }
  }

  pub fn begin_scope(&mut self) {
    self.mem.push(HashMap::new());
  }

  pub fn end_scope(&mut self) {
    self.mem.pop();
  }
}
<commit_msg>removing unwraps<commit_after>use ast::*;
use std::collections::HashMap;
use runtime_error::RuntimeError;

#[derive(Clone, Debug)]
pub struct State {
  pub mem: Vec<HashMap<String, Binding>>,
}

#[derive(Clone, Debug)]
pub enum Binding {
  Var(Box<Expr>),
  Const(Box<Expr>),
}

impl State {
  pub fn new() -> State {
    return State {
      mem: vec!(HashMap::new()),
    }
  }

  fn first_map_for(&mut self, x: String) -> Option<&mut HashMap<String, Binding>> {
    self.mem.iter_mut().rev().find(|map| map.contains_key(&x))
  }

  pub fn alloc(&mut self, x: String, v1: Expr) -> Result<(), RuntimeError> {
    let binding = Binding::Var(Box::new(v1));

    match self.mem.last_mut() {
      Some(map) => {
        map.insert(x, binding);
        Ok(())
      },
      None => Err(RuntimeError::InvalidMemoryState("no memory frame for var allocation".to_string())),
    }
  }

  pub fn alloc_const(&mut self, x: String, v1: Expr) ->Result<(), RuntimeError> {
    let binding = Binding::Const(Box::new(v1));

    match self.mem.last_mut() {
      Some(map) => {
        map.insert(x, binding);
        Ok(())
      },
      None => return Err(RuntimeError::InvalidMemoryState("no memory frame for const allocation".to_string())),
    }
  }

  pub fn assign(&mut self, x: String, v1: Expr) -> Result<(), RuntimeError> {
    let mut map_option = self.first_map_for(x.clone());
    let map = map_option.as_mut().unwrap();

    let binding = map.get_mut(&x).unwrap().clone();

    match binding {
      Binding::Var(_) => map.insert(x, Binding::Var(Box::new(v1))),
      Binding::Const(_) => return Err(RuntimeError::InvalidConstAssignment(v1, x)),
    };
    Ok(())
  }

  pub fn get(&mut self, x: String) -> Option<Expr> {
    match self.first_map_for(x.clone()) {
      Some(map) => match map.get(&x).clone() {
        Some(&Binding::Var(ref e)) => Some(*e.clone()),
        Some(&Binding::Const(ref e)) => Some(*e.clone()),
        _ => None,
      },
      None => None,
    }
  }

  pub fn begin_scope(&mut self) {
    self.mem.push(HashMap::new());
  }

  pub fn end_scope(&mut self) {
    self.mem.pop();
  }
}
<|endoftext|>


<commit_before>extern crate id_tree;

use id_tree::NodeId;
use id_tree::Node;
use id_tree::TreeBuilder;
use id_tree::Tree;

fn main() {
    let mut tree: Tree<i32> = TreeBuilder::new()
        .with_capacity(3)
        .build();

    let root_node = Node::new(1);
    let child_1 = Node::new(2);
    let child_2 = Node::new(3);

    let root_id: NodeId = tree.set_root(root_node);
    let child_1_id: NodeId = tree.add_child(root_id, child_1);
    let child_2_id: NodeId = tree.add_child(root_id, child_2);

    println!("{}", tree.get(root_id).unwrap().data());
    println!("{}", tree.get(child_1_id).unwrap().data());
    println!("{}", tree.get(child_2_id).unwrap().data());
}
<commit_msg>flesh out the basic example a bit more<commit_after>extern crate id_tree;

use id_tree::NodeId;
use id_tree::Node;
use id_tree::TreeBuilder;
use id_tree::Tree;

fn main() {
    let mut tree: Tree<i32> = TreeBuilder::new()
        .with_capacity(3)
        .build();

    let root_node = Node::new(1);
    let child_1 = Node::new(2);
    let child_2 = Node::new(3);
    let grandchild_4 = Node::new(4);
    let grandchild_5 = Node::new(5);

    let root_id: NodeId = tree.set_root(root_node);
    let child_1_id: NodeId = tree.add_child(root_id, child_1);
    tree.add_child(root_id, child_2);
    tree.add_child(child_1_id, grandchild_4);
    tree.add_child(child_1_id, grandchild_5);

    print_children_post_order(&tree, root_id);
    println!("{}", tree.get(root_id).unwrap().data());
}

fn print_children_post_order(tree: &Tree<i32>, node_id: NodeId) {
    let node_ref: &Node<i32> = tree.get(node_id).unwrap();

    for child_id in node_ref.children() {
        print_children_post_order(tree, *child_id);
        println!("{}", tree.get(*child_id).unwrap().data());
    }
}
<|endoftext|>


<commit_before>/*
 * This thing crashes Enigo.
 * Obviously, this is a bug, and is being worked on.
 * It's here for us to experiment around to see why it crashes and
 * how to fix it.
 *
 * Cheers!
 */
extern crate enigo;
use enigo::{Enigo, KeyboardControllable, Key};

fn main() {
    let mut enigo = Enigo::new();

    //enigo.key_sequence("ä#+ -> hello world ... 𝕊");
    enigo.key_sequence("aaa𝕊");
    println!("woot m9");
    enigo.key_click(Key::RETURN);
    println!("m9 woot");
}
<commit_msg>fixed crash.rs<commit_after>/*
 * This thing crashes Enigo.
 * Obviously, this is a bug, and is being worked on.
 * It's here for us to experiment around to see why it crashes and
 * how to fix it.
 *
 * Cheers!
 */
extern crate enigo;
use enigo::{Enigo, KeyboardControllable, Key};

fn main() {
    let mut enigo = Enigo::new();

    //enigo.key_sequence("ä#+ -> hello world ... 𝕊");
    enigo.key_sequence("aaa𝕊");
    println!("woot m9");
    enigo.key_click(Key::Return);
    println!("m9 woot");
}
<|endoftext|>


<commit_before>/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Sahid Orentino Ferdjaoui <sahid.ferdjaoui@redhat.com>
 */

//! This file contains trivial example code to connect to the running
//! hypervisor and gather a few bits of information about domains.
//! Similar API's exist for storage pools, networks, and interfaces.
//!
//! Largely inspired by hellolibvirt.c

extern crate virt;

use std::env;

use virt::connect::Connect;
use virt::error::Error;

fn show_hypervisor_info(conn: Connect) -> Result<(), Error> {
    if let Ok(hv_type) = conn.get_type() {
        if let Ok(mut hv_ver) = conn.get_hyp_version() {
            let major = hv_ver / 1000000;
            hv_ver %= 1000000;
            let minor = hv_ver / 1000;
            let release = hv_ver % 1000;
            println!("Hypervisor: '{}' version: {}.{}.{}",
                     hv_type,
                     major,
                     minor,
                     release);
            return Ok(());
        }
    }
    Err(Error::new())
}

fn show_domains(conn: Connect) -> Result<(), Error> {
    let flags = virt::connect::VIR_CONNECT_LIST_DOMAINS_ACTIVE |
                virt::connect::VIR_CONNECT_LIST_DOMAINS_INACTIVE;

    if let Ok(num_active_domains) = conn.num_of_domains() {
        if let Ok(num_inactive_domains) = conn.num_of_defined_domains() {
            println!("There are {} active and {} inactive domains",
                     num_active_domains,
                     num_inactive_domains);
            /* Return a list of all active and inactive domains. Using this API
             * instead of virConnectListDomains() and virConnectListDefinedDomains()
             * is preferred since it "solves" an inherit race between separated API
             * calls if domains are started or stopped between calls */
            if let Ok(doms) = conn.list_all_domains(flags) {
                for dom in doms {
                    let name = dom.get_name().unwrap_or(String::from("no-name"));
                    let active = dom.is_active().unwrap_or(false);
                    println!("domain: {}, active: {}", name, active);
                }
            }
            return Ok(());
        }
    }
    Err(Error::new())
}

fn main() {
    let uri = match env::args().nth(1) {
        Some(u) => u,
        None => String::from(""),
    };
    println!("Attempting to connect to hypervisor: '{}'", uri);

    let conn = match Connect::open(&uri) {
        Ok(c) => c,
        Err(e) => {
            panic!("No connection to hypervisor: code {}, message: {}",
                   e.code,
                   e.message)
        }
    };

    match conn.get_uri() {
        Ok(u) => println!("Connected to hypervisor at '{}'", u),
        Err(e) => {
            disconnect(conn);
            panic!("Failed to get URI for hypervisor connection: code {}, message: {}",
                   e.code,
                   e.message);
        }
    };

    if let Err(e) = show_hypervisor_info(conn.clone()) {
        disconnect(conn);
        panic!("Failed to show hypervisor info: code {}, message: {}",
               e.code,
               e.message);
    }

    if let Err(e) = show_domains(conn.clone()) {
        disconnect(conn);
        panic!("Failed to show domains info: code {}, message: {}",
               e.code,
               e.message);
    }

    fn disconnect(mut conn: Connect) {
        if let Err(e) = conn.close() {
            panic!("Failed to disconnect from hypervisor: code {}, message: {}",
                   e.code,
                   e.message);
        }
        println!("Disconnected from hypervisor");
    }
}
<commit_msg>example: hello<commit_after>/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Sahid Orentino Ferdjaoui <sahid.ferdjaoui@redhat.com>
 */

//! This file contains trivial example code to connect to the running
//! hypervisor and gather a few bits of information about domains.
//! Similar API's exist for storage pools, networks, and interfaces.
//!
//! Largely inspired by hellolibvirt.c

extern crate virt;

use std::env;

use virt::connect::Connect;
use virt::error::Error;

fn show_hypervisor_info(conn: Connect) -> Result<(), Error> {
    if let Ok(hv_type) = conn.get_type() {
        if let Ok(mut hv_ver) = conn.get_hyp_version() {
            let major = hv_ver / 1000000;
            hv_ver %= 1000000;
            let minor = hv_ver / 1000;
            let release = hv_ver % 1000;
            println!("Hypervisor: '{}' version: {}.{}.{}",
                     hv_type,
                     major,
                     minor,
                     release);
            return Ok(());
        }
    }
    Err(Error::new())
}

fn show_domains(conn: Connect) -> Result<(), Error> {
    let flags = virt::connect::VIR_CONNECT_LIST_DOMAINS_ACTIVE |
                virt::connect::VIR_CONNECT_LIST_DOMAINS_INACTIVE;

    if let Ok(num_active_domains) = conn.num_of_domains() {
        if let Ok(num_inactive_domains) = conn.num_of_defined_domains() {
            println!("There are {} active and {} inactive domains",
                     num_active_domains,
                     num_inactive_domains);
            /* Return a list of all active and inactive domains. Using this API
             * instead of virConnectListDomains() and virConnectListDefinedDomains()
             * is preferred since it "solves" an inherit race between separated API
             * calls if domains are started or stopped between calls */
            if let Ok(doms) = conn.list_all_domains(flags) {
                for dom in doms {
                    let id = dom.get_id().unwrap_or(0);
                    let name = dom.get_name().unwrap_or(String::from("no-name"));
                    let active = dom.is_active().unwrap_or(false);
                    println!("ID: {}, Name: {}, Active: {}", id, name, active);
                    if let Ok(dinfo) = dom.get_info() {
                        println!("    State: {}", dinfo.state);
                        println!("    Max Memory: {}", dinfo.max_mem);
                        println!("    Memory: {}", dinfo.memory);
                        println!("    CPUs: {}", dinfo.nr_virt_cpu);
                        println!("    CPU Time: {}", dinfo.cpu_time);
                    }
                }
            }
            return Ok(());
        }
    }
    Err(Error::new())
}

fn main() {
    let uri = match env::args().nth(1) {
        Some(u) => u,
        None => String::from(""),
    };
    println!("Attempting to connect to hypervisor: '{}'", uri);

    let conn = match Connect::open(&uri) {
        Ok(c) => c,
        Err(e) => {
            panic!("No connection to hypervisor: code {}, message: {}",
                   e.code,
                   e.message)
        }
    };

    match conn.get_uri() {
        Ok(u) => println!("Connected to hypervisor at '{}'", u),
        Err(e) => {
            disconnect(conn);
            panic!("Failed to get URI for hypervisor connection: code {}, message: {}",
                   e.code,
                   e.message);
        }
    };

    if let Err(e) = show_hypervisor_info(conn.clone()) {
        disconnect(conn);
        panic!("Failed to show hypervisor info: code {}, message: {}",
               e.code,
               e.message);
    }

    if let Err(e) = show_domains(conn.clone()) {
        disconnect(conn);
        panic!("Failed to show domains info: code {}, message: {}",
               e.code,
               e.message);
    }

    fn disconnect(mut conn: Connect) {
        if let Err(e) = conn.close() {
            panic!("Failed to disconnect from hypervisor: code {}, message: {}",
                   e.code,
                   e.message);
        }
        println!("Disconnected from hypervisor");
    }
}
<|endoftext|>


<commit_before>extern crate polydraw;

use std::usize;

use polydraw::{Application, Renderer, Frame};
use polydraw::draw::RGB;
use polydraw::raster::{Scene, Point, Segment, Circle, EdgeType, EdgeSrc, Poly, Rasterizer};

struct SceneRenderer {
   rasterizer: Rasterizer,
}

impl SceneRenderer {
   fn new() -> Self {
      SceneRenderer {
         rasterizer: Rasterizer::new(),
      }
   }

   fn create_scene(&self) -> Scene {
      let points = vec![
         Point::new(0, 0),   // 0
         Point::new(8, 0),   // 1
         Point::new(12, 0),  // 2
         Point::new(12, 4),  // 3
         Point::new(0, 8),   // 4
         Point::new(0, 12),  // 5
         Point::new(4, 12),  // 6
         Point::new(12, 12), // 7
         Point::new(8, 4),   // 8
         Point::new(4, 8),   // 9
      ];

      let segments = vec![
         Segment::new(0, 1), // a 0
         Segment::new(1, 2), // b 1
         Segment::new(0, 4), // c 2
         Segment::new(1, 3), // d 3
         Segment::new(2, 3), // e 4
         Segment::new(3, 7), // f 5
         Segment::new(4, 5), // g 6
         Segment::new(4, 6), // h 7
         Segment::new(5, 6), // i 8
         Segment::new(6, 7), // j 9
      ];

      let circles = vec![
         Circle::new(8, 4),  // 0
         Circle::new(9, 4),  // 1
      ];

      let edges = vec![
         EdgeSrc::new(EdgeType::LVT, 2, usize::MAX),
         EdgeSrc::new(EdgeType::CTR, 7, 1),
         EdgeSrc::new(EdgeType::LHR, 9, usize::MAX),
         EdgeSrc::new(EdgeType::LVB, 5, usize::MAX),
         EdgeSrc::new(EdgeType::CBL, 3, 0),
         EdgeSrc::new(EdgeType::LHL, 0, usize::MAX),
         EdgeSrc::new(EdgeType::ATR, 3, 0),
         EdgeSrc::new(EdgeType::LVB, 4, usize::MAX),
         EdgeSrc::new(EdgeType::LHL, 1, usize::MAX),
         EdgeSrc::new(EdgeType::LVT, 6, usize::MAX),
         EdgeSrc::new(EdgeType::LHR, 8, usize::MAX),
         EdgeSrc::new(EdgeType::ABL, 7, 1),
      ];

      let polys = vec![
         Poly::new(0, 6, 0),
         Poly::new(6, 9, 1),
         Poly::new(9, 12, 2),
      ];

      let colors = vec![
         RGB::new(194, 243, 137),
         RGB::new(154, 222, 76),
         RGB::new(172, 58, 162),
      ];

      Scene {
         points: points,
         segments: segments,
         circles: circles,
         edges: edges,
         polys: polys,
         colors: colors,
      }
   }

   fn scale_scene(&self, scene: &mut Scene) {
      let scale = 50_000;

      for point in &mut scene.points {
         point.x = scale * point.x;
         point.y = scale * point.y;
      }

      for circle in &mut scene.circles {
         circle.radius = scale * circle.radius;
      }
   }
}

impl Renderer for SceneRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      let mut scene = self.create_scene();
      self.scale_scene(&mut scene);

      self.rasterizer.render(&scene, frame);
   }
}

fn main() {
   let mut renderer = SceneRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Scene")
      .run();
}
<commit_msg>Changing the scene scale ratio<commit_after>extern crate polydraw;

use std::usize;

use polydraw::{Application, Renderer, Frame};
use polydraw::draw::RGB;
use polydraw::raster::{Scene, Point, Segment, Circle, EdgeType, EdgeSrc, Poly, Rasterizer};

struct SceneRenderer {
   rasterizer: Rasterizer,
}

impl SceneRenderer {
   fn new() -> Self {
      SceneRenderer {
         rasterizer: Rasterizer::new(),
      }
   }

   fn create_scene(&self) -> Scene {
      let points = vec![
         Point::new(0, 0),   // 0
         Point::new(8, 0),   // 1
         Point::new(12, 0),  // 2
         Point::new(12, 4),  // 3
         Point::new(0, 8),   // 4
         Point::new(0, 12),  // 5
         Point::new(4, 12),  // 6
         Point::new(12, 12), // 7
         Point::new(8, 4),   // 8
         Point::new(4, 8),   // 9
      ];

      let segments = vec![
         Segment::new(0, 1), // a 0
         Segment::new(1, 2), // b 1
         Segment::new(0, 4), // c 2
         Segment::new(1, 3), // d 3
         Segment::new(2, 3), // e 4
         Segment::new(3, 7), // f 5
         Segment::new(4, 5), // g 6
         Segment::new(4, 6), // h 7
         Segment::new(5, 6), // i 8
         Segment::new(6, 7), // j 9
      ];

      let circles = vec![
         Circle::new(8, 4),  // 0
         Circle::new(9, 4),  // 1
      ];

      let edges = vec![
         EdgeSrc::new(EdgeType::LVT, 2, usize::MAX),
         EdgeSrc::new(EdgeType::CTR, 7, 1),
         EdgeSrc::new(EdgeType::LHR, 9, usize::MAX),
         EdgeSrc::new(EdgeType::LVB, 5, usize::MAX),
         EdgeSrc::new(EdgeType::CBL, 3, 0),
         EdgeSrc::new(EdgeType::LHL, 0, usize::MAX),
         EdgeSrc::new(EdgeType::ATR, 3, 0),
         EdgeSrc::new(EdgeType::LVB, 4, usize::MAX),
         EdgeSrc::new(EdgeType::LHL, 1, usize::MAX),
         EdgeSrc::new(EdgeType::LVT, 6, usize::MAX),
         EdgeSrc::new(EdgeType::LHR, 8, usize::MAX),
         EdgeSrc::new(EdgeType::ABL, 7, 1),
      ];

      let polys = vec![
         Poly::new(0, 6, 0),
         Poly::new(6, 9, 1),
         Poly::new(9, 12, 2),
      ];

      let colors = vec![
         RGB::new(194, 243, 137),
         RGB::new(154, 222, 76),
         RGB::new(172, 58, 162),
      ];

      Scene {
         points: points,
         segments: segments,
         circles: circles,
         edges: edges,
         polys: polys,
         colors: colors,
      }
   }

   fn scale_scene(&self, scene: &mut Scene) {
      let scale = 100_000;

      for point in &mut scene.points {
         point.x = scale * point.x;
         point.y = scale * point.y;
      }

      for circle in &mut scene.circles {
         circle.radius = scale * circle.radius;
      }
   }
}

impl Renderer for SceneRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      let mut scene = self.create_scene();
      self.scale_scene(&mut scene);

      self.rasterizer.render(&scene, frame);
   }
}

fn main() {
   let mut renderer = SceneRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Scene")
      .run();
}
<|endoftext|>


<commit_before>use io;
use io::prelude::*;
use byteorder::LittleEndian;
use byteorder_ext::{ReadBytesExt, WriteBytesExt};
use fs::{FatType, FsStatusFlags, DiskSlice, ReadSeek};

struct Fat<T> {
    #[allow(dead_code)]
    dummy: [T; 0],
}

type Fat12 = Fat<u8>;
type Fat16 = Fat<u16>;
type Fat32 = Fat<u32>;

#[derive(Debug, Clone, Copy)]
enum FatValue {
    Free,
    Data(u32),
    Bad,
    EndOfChain,
}

trait FatTrait {
    fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue>;
    fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()>;
    fn find_free(fat: &mut ReadSeek, start_cluster: u32, end_cluster: u32) -> io::Result<u32>;
    fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32>;
}

fn read_fat(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<FatValue> {
    match fat_type {
        FatType::Fat12 => Fat12::get(fat, cluster),
        FatType::Fat16 => Fat16::get(fat, cluster),
        FatType::Fat32 => Fat32::get(fat, cluster),
    }
}

fn write_fat(fat: &mut DiskSlice, fat_type: FatType, cluster: u32, value: FatValue) -> io::Result<()> {
    trace!("write FAT - cluster {} value {:?}", cluster, value);
    match fat_type {
        FatType::Fat12 => Fat12::set(fat, cluster, value),
        FatType::Fat16 => Fat16::set(fat, cluster, value),
        FatType::Fat32 => Fat32::set(fat, cluster, value),
    }
}

fn get_next_cluster(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<Option<u32>> {
    let val = read_fat(fat, fat_type, cluster)?;
    match val {
        FatValue::Data(n) => Ok(Some(n)),
        _ => Ok(None),
    }
}

fn find_free_cluster(fat: &mut ReadSeek, fat_type: FatType, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
    match fat_type {
        FatType::Fat12 => Fat12::find_free(fat, start_cluster, end_cluster),
        FatType::Fat16 => Fat16::find_free(fat, start_cluster, end_cluster),
        FatType::Fat32 => Fat32::find_free(fat, start_cluster, end_cluster),
    }
}

pub(crate) fn alloc_cluster(fat: &mut DiskSlice, fat_type: FatType, prev_cluster: Option<u32>, hint: Option<u32>, total_clusters: u32) -> io::Result<u32> {
    let start_cluster = match hint {
        Some(n) if n < total_clusters => n,
        _ => 2,
    };
    let new_cluster = match find_free_cluster(fat, fat_type, start_cluster, total_clusters) {
        Ok(n) => n,
        Err(_) if start_cluster > 2 => find_free_cluster(fat, fat_type, 2, start_cluster)?,
        Err(e) => return Err(e),
    };
    write_fat(fat, fat_type, new_cluster, FatValue::EndOfChain)?;
    match prev_cluster {
        Some(n) => write_fat(fat, fat_type, n, FatValue::Data(new_cluster))?,
        None => {},
    }
    trace!("allocated cluster {}", new_cluster);
    Ok(new_cluster)
}

pub(crate) fn read_fat_flags(fat: &mut DiskSlice, fat_type: FatType) -> io::Result<FsStatusFlags> {
    // check MSB (except in FAT12)
    let val = match fat_type {
        FatType::Fat12 => 0xFFF,
        FatType::Fat16 => Fat16::get_raw(fat, 1)?,
        FatType::Fat32 => Fat32::get_raw(fat, 1)?,
    };
    let dirty = match fat_type {
        FatType::Fat12 => false,
        FatType::Fat16 => val & (1 << 15) == 0,
        FatType::Fat32 => val & (1 << 27) == 0,
    };
    let io_error = match fat_type {
        FatType::Fat12 => false,
        FatType::Fat16 => val & (1 << 14) == 0,
        FatType::Fat32 => val & (1 << 26) == 0,
    };
    Ok(FsStatusFlags {
        dirty, io_error,
    })
}

impl FatTrait for Fat12 {
    fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
        let fat_offset = cluster + (cluster / 2);
        fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
        let packed_val = fat.read_u16::<LittleEndian>()?;
        Ok(match cluster & 1 {
            0 => packed_val & 0x0FFF,
            _ => packed_val >> 4,
        } as u32)
    }

    fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
        let val = Self::get_raw(fat, cluster)?;
        Ok(match val {
            0 => FatValue::Free,
            0xFF7 => FatValue::Bad,
            0xFF8...0xFFF => FatValue::EndOfChain,
            n => FatValue::Data(n as u32),
        })
    }

    fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
        let raw_val = match value {
            FatValue::Free => 0,
            FatValue::Bad => 0xFF7,
            FatValue::EndOfChain => 0xFFF,
            FatValue::Data(n) => n as u16,
        };
        let fat_offset = cluster + (cluster / 2);
        fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
        let old_packed = fat.read_u16::<LittleEndian>()?;
        fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
        let new_packed = match cluster & 1 {
            0 => (old_packed & 0xF000) | raw_val,
            _ => (old_packed & 0x000F) | (raw_val << 4),
        };
        fat.write_u16::<LittleEndian>(new_packed)?;
        Ok(())
    }

    fn find_free(fat: &mut ReadSeek, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
        let mut cluster = start_cluster;
        let fat_offset = cluster + (cluster / 2);
        fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
        let mut packed_val = fat.read_u16::<LittleEndian>()?;
        loop {
            let val = match cluster & 1 {
                0 => packed_val & 0x0FFF,
                _ => packed_val >> 4,
            };
            if val == 0 {
                return Ok(cluster);
            }
            cluster += 1;
            if cluster == end_cluster {
                return Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"));
            }
            packed_val = match cluster & 1 {
                0 => fat.read_u16::<LittleEndian>()?,
                _ => {
                    let next_byte = fat.read_u8()? as u16;
                    (packed_val >> 8) | (next_byte << 8)
                },
            };
        }
    }
}

impl FatTrait for Fat16 {
    fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
        fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
        Ok(fat.read_u16::<LittleEndian>()? as u32)
    }

    fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
        let val = Self::get_raw(fat, cluster)?;
        Ok(match val {
            0 => FatValue::Free,
            0xFFF7 => FatValue::Bad,
            0xFFF8...0xFFFF => FatValue::EndOfChain,
            n => FatValue::Data(n as u32),
        })
    }

    fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
        fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
        let raw_val = match value {
            FatValue::Free => 0,
            FatValue::Bad => 0xFFF7,
            FatValue::EndOfChain => 0xFFFF,
            FatValue::Data(n) => n as u16,
        };
        fat.write_u16::<LittleEndian>(raw_val)?;
        Ok(())
    }

    fn find_free(fat: &mut ReadSeek, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
        let mut cluster = start_cluster;
        fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
        loop {
            let val = fat.read_u16::<LittleEndian>()?;
            if val == 0 {
                return Ok(cluster);
            }
            cluster += 1;
            if cluster == end_cluster {
                return Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"));
            }
        }
    }
}

impl FatTrait for Fat32 {
    fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
        fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
        Ok(fat.read_u32::<LittleEndian>()? & 0x0FFFFFFF)
    }

    fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
        let val = Self::get_raw(fat, cluster)?;
        Ok(match val {
            0 => FatValue::Free,
            0x0FFFFFF7 => FatValue::Bad,
            0x0FFFFFF8...0x0FFFFFFF => FatValue::EndOfChain,
            n => FatValue::Data(n as u32),
        })
    }

    fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
        fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
        let raw_val = match value {
            FatValue::Free => 0,
            FatValue::Bad => 0x0FFFFFF7,
            FatValue::EndOfChain => 0x0FFFFFFF,
            FatValue::Data(n) => n,
        };
        fat.write_u32::<LittleEndian>(raw_val)?;
        Ok(())
    }

    fn find_free(fat: &mut ReadSeek, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
        let mut cluster = start_cluster;
        fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
        loop {
            let val = fat.read_u32::<LittleEndian>()? & 0x0FFFFFFF;
            if val == 0 {
                return Ok(cluster);
            }
            cluster += 1;
            if cluster == end_cluster {
                return Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"));
            }
        }
    }
}

pub(crate) struct ClusterIterator<'a, 'b: 'a> {
    fat: DiskSlice<'a, 'b>,
    fat_type: FatType,
    cluster: Option<u32>,
    err: bool,
}

impl <'a, 'b> ClusterIterator<'a, 'b> {
    pub(crate) fn new(fat: DiskSlice<'a, 'b>, fat_type: FatType, cluster: u32)
    -> ClusterIterator<'a, 'b> {
        ClusterIterator {
            fat: fat,
            fat_type: fat_type,
            cluster: Some(cluster),
            err: false,
        }
    }

    pub(crate) fn truncate(&mut self) -> io::Result<()> {
        match self.cluster {
            Some(n) => {
                write_fat(&mut self.fat, self.fat_type, n, FatValue::EndOfChain)?;
                self.next();
                self.free()
            },
            None => Ok(()),
        }
    }

    pub(crate) fn free(&mut self) -> io::Result<()> {
        loop {
            let prev = self.cluster;
            self.next();
            match prev {
                Some(n) => write_fat(&mut self.fat, self.fat_type, n, FatValue::Free)?,
                None => break,
            };
        }
        Ok(())
    }
}

impl <'a, 'b> Iterator for ClusterIterator<'a, 'b> {
    type Item = io::Result<u32>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.err {
            return None;
        }
        match self.cluster {
            Some(current_cluster) => {
                self.cluster = match get_next_cluster(&mut self.fat, self.fat_type, current_cluster) {
                    Ok(next_cluster) => next_cluster,
                    Err(err) => {
                        self.err = true;
                        return Some(Err(err));
                    },
                }
            },
            None => {},
        };
        match self.cluster {
            Some(n) => Some(Ok(n)),
            None => None,
        }
    }
}
<commit_msg>Do not ignore 2 last clusters when allocating<commit_after>use io;
use io::prelude::*;
use byteorder::LittleEndian;
use byteorder_ext::{ReadBytesExt, WriteBytesExt};
use fs::{FatType, FsStatusFlags, DiskSlice, ReadSeek};

struct Fat<T> {
    #[allow(dead_code)]
    dummy: [T; 0],
}

type Fat12 = Fat<u8>;
type Fat16 = Fat<u16>;
type Fat32 = Fat<u32>;

const RESERVED_FAT_ENTRIES: u32 = 2;

#[derive(Debug, Clone, Copy)]
enum FatValue {
    Free,
    Data(u32),
    Bad,
    EndOfChain,
}

trait FatTrait {
    fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue>;
    fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()>;
    fn find_free(fat: &mut ReadSeek, start_cluster: u32, end_cluster: u32) -> io::Result<u32>;
    fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32>;
}

fn read_fat(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<FatValue> {
    match fat_type {
        FatType::Fat12 => Fat12::get(fat, cluster),
        FatType::Fat16 => Fat16::get(fat, cluster),
        FatType::Fat32 => Fat32::get(fat, cluster),
    }
}

fn write_fat(fat: &mut DiskSlice, fat_type: FatType, cluster: u32, value: FatValue) -> io::Result<()> {
    trace!("write FAT - cluster {} value {:?}", cluster, value);
    match fat_type {
        FatType::Fat12 => Fat12::set(fat, cluster, value),
        FatType::Fat16 => Fat16::set(fat, cluster, value),
        FatType::Fat32 => Fat32::set(fat, cluster, value),
    }
}

fn get_next_cluster(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<Option<u32>> {
    let val = read_fat(fat, fat_type, cluster)?;
    match val {
        FatValue::Data(n) => Ok(Some(n)),
        _ => Ok(None),
    }
}

fn find_free_cluster(fat: &mut ReadSeek, fat_type: FatType, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
    match fat_type {
        FatType::Fat12 => Fat12::find_free(fat, start_cluster, end_cluster),
        FatType::Fat16 => Fat16::find_free(fat, start_cluster, end_cluster),
        FatType::Fat32 => Fat32::find_free(fat, start_cluster, end_cluster),
    }
}

pub(crate) fn alloc_cluster(fat: &mut DiskSlice, fat_type: FatType, prev_cluster: Option<u32>, hint: Option<u32>, total_clusters: u32) -> io::Result<u32> {
    let end_cluster = total_clusters + RESERVED_FAT_ENTRIES;
    let start_cluster = match hint {
        Some(n) if n < end_cluster => n,
        _ => 2,
    };
    let new_cluster = match find_free_cluster(fat, fat_type, start_cluster, end_cluster) {
        Ok(n) => n,
        Err(_) if start_cluster > 2 => find_free_cluster(fat, fat_type, 2, start_cluster)?,
        Err(e) => return Err(e),
    };
    write_fat(fat, fat_type, new_cluster, FatValue::EndOfChain)?;
    match prev_cluster {
        Some(n) => write_fat(fat, fat_type, n, FatValue::Data(new_cluster))?,
        None => {},
    }
    trace!("allocated cluster {}", new_cluster);
    Ok(new_cluster)
}

pub(crate) fn read_fat_flags(fat: &mut DiskSlice, fat_type: FatType) -> io::Result<FsStatusFlags> {
    // check MSB (except in FAT12)
    let val = match fat_type {
        FatType::Fat12 => 0xFFF,
        FatType::Fat16 => Fat16::get_raw(fat, 1)?,
        FatType::Fat32 => Fat32::get_raw(fat, 1)?,
    };
    let dirty = match fat_type {
        FatType::Fat12 => false,
        FatType::Fat16 => val & (1 << 15) == 0,
        FatType::Fat32 => val & (1 << 27) == 0,
    };
    let io_error = match fat_type {
        FatType::Fat12 => false,
        FatType::Fat16 => val & (1 << 14) == 0,
        FatType::Fat32 => val & (1 << 26) == 0,
    };
    Ok(FsStatusFlags {
        dirty, io_error,
    })
}

impl FatTrait for Fat12 {
    fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
        let fat_offset = cluster + (cluster / 2);
        fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
        let packed_val = fat.read_u16::<LittleEndian>()?;
        Ok(match cluster & 1 {
            0 => packed_val & 0x0FFF,
            _ => packed_val >> 4,
        } as u32)
    }

    fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
        let val = Self::get_raw(fat, cluster)?;
        Ok(match val {
            0 => FatValue::Free,
            0xFF7 => FatValue::Bad,
            0xFF8...0xFFF => FatValue::EndOfChain,
            n => FatValue::Data(n as u32),
        })
    }

    fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
        let raw_val = match value {
            FatValue::Free => 0,
            FatValue::Bad => 0xFF7,
            FatValue::EndOfChain => 0xFFF,
            FatValue::Data(n) => n as u16,
        };
        let fat_offset = cluster + (cluster / 2);
        fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
        let old_packed = fat.read_u16::<LittleEndian>()?;
        fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
        let new_packed = match cluster & 1 {
            0 => (old_packed & 0xF000) | raw_val,
            _ => (old_packed & 0x000F) | (raw_val << 4),
        };
        fat.write_u16::<LittleEndian>(new_packed)?;
        Ok(())
    }

    fn find_free(fat: &mut ReadSeek, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
        let mut cluster = start_cluster;
        let fat_offset = cluster + (cluster / 2);
        fat.seek(io::SeekFrom::Start(fat_offset as u64))?;
        let mut packed_val = fat.read_u16::<LittleEndian>()?;
        loop {
            let val = match cluster & 1 {
                0 => packed_val & 0x0FFF,
                _ => packed_val >> 4,
            };
            if val == 0 {
                return Ok(cluster);
            }
            cluster += 1;
            if cluster == end_cluster {
                return Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"));
            }
            packed_val = match cluster & 1 {
                0 => fat.read_u16::<LittleEndian>()?,
                _ => {
                    let next_byte = fat.read_u8()? as u16;
                    (packed_val >> 8) | (next_byte << 8)
                },
            };
        }
    }
}

impl FatTrait for Fat16 {
    fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
        fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
        Ok(fat.read_u16::<LittleEndian>()? as u32)
    }

    fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
        let val = Self::get_raw(fat, cluster)?;
        Ok(match val {
            0 => FatValue::Free,
            0xFFF7 => FatValue::Bad,
            0xFFF8...0xFFFF => FatValue::EndOfChain,
            n => FatValue::Data(n as u32),
        })
    }

    fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
        fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
        let raw_val = match value {
            FatValue::Free => 0,
            FatValue::Bad => 0xFFF7,
            FatValue::EndOfChain => 0xFFFF,
            FatValue::Data(n) => n as u16,
        };
        fat.write_u16::<LittleEndian>(raw_val)?;
        Ok(())
    }

    fn find_free(fat: &mut ReadSeek, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
        let mut cluster = start_cluster;
        fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
        loop {
            let val = fat.read_u16::<LittleEndian>()?;
            if val == 0 {
                return Ok(cluster);
            }
            cluster += 1;
            if cluster == end_cluster {
                return Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"));
            }
        }
    }
}

impl FatTrait for Fat32 {
    fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
        fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
        Ok(fat.read_u32::<LittleEndian>()? & 0x0FFFFFFF)
    }

    fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue> {
        let val = Self::get_raw(fat, cluster)?;
        Ok(match val {
            0 => FatValue::Free,
            0x0FFFFFF7 => FatValue::Bad,
            0x0FFFFFF8...0x0FFFFFFF => FatValue::EndOfChain,
            n => FatValue::Data(n as u32),
        })
    }

    fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()> {
        fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
        let raw_val = match value {
            FatValue::Free => 0,
            FatValue::Bad => 0x0FFFFFF7,
            FatValue::EndOfChain => 0x0FFFFFFF,
            FatValue::Data(n) => n,
        };
        fat.write_u32::<LittleEndian>(raw_val)?;
        Ok(())
    }

    fn find_free(fat: &mut ReadSeek, start_cluster: u32, end_cluster: u32) -> io::Result<u32> {
        let mut cluster = start_cluster;
        fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
        loop {
            let val = fat.read_u32::<LittleEndian>()? & 0x0FFFFFFF;
            if val == 0 {
                return Ok(cluster);
            }
            cluster += 1;
            if cluster == end_cluster {
                return Err(io::Error::new(io::ErrorKind::Other, "end of FAT reached"));
            }
        }
    }
}

pub(crate) struct ClusterIterator<'a, 'b: 'a> {
    fat: DiskSlice<'a, 'b>,
    fat_type: FatType,
    cluster: Option<u32>,
    err: bool,
}

impl <'a, 'b> ClusterIterator<'a, 'b> {
    pub(crate) fn new(fat: DiskSlice<'a, 'b>, fat_type: FatType, cluster: u32)
    -> ClusterIterator<'a, 'b> {
        ClusterIterator {
            fat: fat,
            fat_type: fat_type,
            cluster: Some(cluster),
            err: false,
        }
    }

    pub(crate) fn truncate(&mut self) -> io::Result<()> {
        match self.cluster {
            Some(n) => {
                write_fat(&mut self.fat, self.fat_type, n, FatValue::EndOfChain)?;
                self.next();
                self.free()
            },
            None => Ok(()),
        }
    }

    pub(crate) fn free(&mut self) -> io::Result<()> {
        loop {
            let prev = self.cluster;
            self.next();
            match prev {
                Some(n) => write_fat(&mut self.fat, self.fat_type, n, FatValue::Free)?,
                None => break,
            };
        }
        Ok(())
    }
}

impl <'a, 'b> Iterator for ClusterIterator<'a, 'b> {
    type Item = io::Result<u32>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.err {
            return None;
        }
        match self.cluster {
            Some(current_cluster) => {
                self.cluster = match get_next_cluster(&mut self.fat, self.fat_type, current_cluster) {
                    Ok(next_cluster) => next_cluster,
                    Err(err) => {
                        self.err = true;
                        return Some(Err(err));
                    },
                }
            },
            None => {},
        };
        match self.cluster {
            Some(n) => Some(Ok(n)),
            None => None,
        }
    }
}
<|endoftext|>


<commit_before>use config::{Benchmark, Config, parse_config};
use Commit;
use Push;
use repo::Workspace;

use std::collections::HashMap;
use std::io;
use std::path::Path;
use std::process::{Command, ExitStatus};
use std::str;

/// `(val, percentage_change)`
#[derive(Debug, Clone)]
pub enum BenchmarkResult<T> {
    Improvement(T, f64),
    Regression(T, f64),
    Neutral(T, f64),
}

#[derive(Debug, Clone)]
pub struct TastingResult {
    pub branch: Option<String>,
    pub commit: Commit,
    pub build: bool,
    pub test: bool,
    pub bench: bool,
    pub results: Option<Vec<HashMap<String, BenchmarkResult<f64>>>>,
}

fn benchmark(workdir: &str,
             cfg: &Benchmark,
             previous_result: Option<&HashMap<String, BenchmarkResult<f64>>>)
             -> (ExitStatus, HashMap<String, BenchmarkResult<f64>>) {
    let mut cmd = Command::new("cargo");
    cmd.current_dir(workdir)
        .env("RUST_BACKTRACE", "1")
        .arg(&cfg.cmd)
        .args(cfg.args.as_slice());

    let output = cmd.output()
        .expect(&format!("Failed to execute benchmark '{}'!", cfg.name));

    let lines = str::from_utf8(output.stdout.as_slice()).unwrap().lines();
    let mut res = HashMap::new();

    // Don't try parsing the output if we didn't succeed
    if !output.status.success() {
        return (output.status, res);
    }

    // Success, so let's look for the results
    for l in lines {
        for (i, regex) in cfg.result_expr.iter().enumerate() {
            for cap in regex.captures_iter(l) {
                let (metric, value) = if cap.len() > 2 {
                    (String::from(cap.at(1).unwrap()), cap.at(2))
                } else {
                    (format!("{}", i), cap.at(1))
                };
                let bm_name = format!("{}/{}", cfg.name, &metric);
                if let Some(c) = value {
                    use std::str::FromStr;
                    let val = match f64::from_str(&c) {
                        Ok(f) => f,
                        Err(_) => {
                            println!("failed to parse value '{}' for {} into f64 number, ignoring",
                                     c,
                                     bm_name);
                            continue;
                        }
                    };
                    let new_result = match previous_result {
                        None => BenchmarkResult::Improvement(val, 0.0),
                        Some(prev_res) => {
                            let old_val = match prev_res.get(&bm_name) {
                                None => val,
                                Some(pv) => {
                                    match *pv {
                                        BenchmarkResult::Improvement(v, _) => v,
                                        BenchmarkResult::Regression(v, _) => v,
                                        BenchmarkResult::Neutral(v, _) => v,
                                    }
                                }
                            };
                            let new_result = if cfg.lower_is_better {
                                if val >= old_val * (1.0 + cfg.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - cfg.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            } else {
                                if val >= old_val * (1.0 + cfg.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - cfg.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            };
                            new_result
                        }
                    };
                    res.insert(bm_name, new_result);
                }
            }
        }
    }
    (output.status, res)
}

fn build(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("build")
        .arg("--release")
        .env("RUST_BACKTRACE", "1")
        .status()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(ws: &Workspace,
                    history: &mut HashMap<String,
                                          HashMap<String, HashMap<String, BenchmarkResult<f64>>>>,
                    push: &Push,
                    commit: &Commit,
                    def_improvement_threshold: f64,
                    def_regression_threshold: f64)
                    -> Result<(Option<Config>, TastingResult), String> {
    println!("Tasting commit {}", commit.id);
    ws.checkout_commit(&commit.id)?;

    let branch = match push.push_ref {
        None => None,
        Some(ref pr) => {
            match pr.rfind("/") {
                None => None,
                Some(i) => Some(String::from(&pr[i + 1..])),
            }
        }
    };

    let build_success = update(&ws.path).success() && build(&ws.path).success();
    let test_success = test(&ws.path).success();

    let cfg = match parse_config(Path::new(&format!("{}/taster.toml", ws.path)),
                                 def_improvement_threshold,
                                 def_regression_threshold) {
        Ok(c) => c,
        Err(e) => {
            match e.kind() {
                io::ErrorKind::NotFound => {
                    println!("Skipping commit {} which doesn't have a Taster config.",
                             commit.id);
                    return Ok((None,
                               TastingResult {
                                   branch: branch,
                                   commit: commit.clone(),
                                   build: build_success,
                                   test: test_success,
                                   bench: false,
                                   results: None,
                               }));
                }
                _ => unimplemented!(),
            }
        }
    };

    let bench_out = match branch {
        Some(ref branch) => {
            let branch_history = history.entry(branch.clone()).or_insert(HashMap::new());
            cfg.benchmarks
                .iter()
                .map(|b| {
                    let new_result = benchmark(&ws.path, b, branch_history.get(&b.name));
                    branch_history.insert(b.name.clone(), new_result.1.clone());
                    new_result
                })
                .collect::<Vec<(ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
        }
        None => {
            cfg.benchmarks
                .iter()
                .map(|b| benchmark(&ws.path, b, None))
                .collect::<Vec<(ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
        }
    };
    let bench_success = bench_out.iter().all(|x| x.0.success());
    let bench_results = bench_out.iter().map(|x| x.1.clone()).collect();

    Ok((Some(cfg),
        TastingResult {
            branch: branch,
            commit: commit.clone(),
            build: build_success,
            test: test_success,
            bench: bench_success,
            results: Some(bench_results),
        }))
}

fn test(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("test")
        .env("RUST_BACKTRACE", "1")
        .env("RUST_TEST_THREADS", "1")
        .status()
        .expect("Failed to execute 'cargo test'!")
}

fn update(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("update")
        .status()
        .expect("Failed to execute 'cargo update'!")
}
<commit_msg>Gracefully handle invalid config<commit_after>use config::{Benchmark, Config, parse_config};
use Commit;
use Push;
use repo::Workspace;

use std::collections::HashMap;
use std::io;
use std::path::Path;
use std::process::{Command, ExitStatus};
use std::str;

/// `(val, percentage_change)`
#[derive(Debug, Clone)]
pub enum BenchmarkResult<T> {
    Improvement(T, f64),
    Regression(T, f64),
    Neutral(T, f64),
}

#[derive(Debug, Clone)]
pub struct TastingResult {
    pub branch: Option<String>,
    pub commit: Commit,
    pub build: bool,
    pub test: bool,
    pub bench: bool,
    pub results: Option<Vec<HashMap<String, BenchmarkResult<f64>>>>,
}

fn benchmark(workdir: &str,
             cfg: &Benchmark,
             previous_result: Option<&HashMap<String, BenchmarkResult<f64>>>)
             -> (ExitStatus, HashMap<String, BenchmarkResult<f64>>) {
    let mut cmd = Command::new("cargo");
    cmd.current_dir(workdir)
        .env("RUST_BACKTRACE", "1")
        .arg(&cfg.cmd)
        .args(cfg.args.as_slice());

    let output = cmd.output()
        .expect(&format!("Failed to execute benchmark '{}'!", cfg.name));

    let lines = str::from_utf8(output.stdout.as_slice()).unwrap().lines();
    let mut res = HashMap::new();

    // Don't try parsing the output if we didn't succeed
    if !output.status.success() {
        return (output.status, res);
    }

    // Success, so let's look for the results
    for l in lines {
        for (i, regex) in cfg.result_expr.iter().enumerate() {
            for cap in regex.captures_iter(l) {
                let (metric, value) = if cap.len() > 2 {
                    (String::from(cap.at(1).unwrap()), cap.at(2))
                } else {
                    (format!("{}", i), cap.at(1))
                };
                let bm_name = format!("{}/{}", cfg.name, &metric);
                if let Some(c) = value {
                    use std::str::FromStr;
                    let val = match f64::from_str(&c) {
                        Ok(f) => f,
                        Err(_) => {
                            println!("failed to parse value '{}' for {} into f64 number, ignoring",
                                     c,
                                     bm_name);
                            continue;
                        }
                    };
                    let new_result = match previous_result {
                        None => BenchmarkResult::Improvement(val, 0.0),
                        Some(prev_res) => {
                            let old_val = match prev_res.get(&bm_name) {
                                None => val,
                                Some(pv) => {
                                    match *pv {
                                        BenchmarkResult::Improvement(v, _) => v,
                                        BenchmarkResult::Regression(v, _) => v,
                                        BenchmarkResult::Neutral(v, _) => v,
                                    }
                                }
                            };
                            let new_result = if cfg.lower_is_better {
                                if val >= old_val * (1.0 + cfg.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - cfg.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            } else {
                                if val >= old_val * (1.0 + cfg.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - cfg.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            };
                            new_result
                        }
                    };
                    res.insert(bm_name, new_result);
                }
            }
        }
    }
    (output.status, res)
}

fn build(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("build")
        .arg("--release")
        .env("RUST_BACKTRACE", "1")
        .status()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(ws: &Workspace,
                    history: &mut HashMap<String,
                                          HashMap<String, HashMap<String, BenchmarkResult<f64>>>>,
                    push: &Push,
                    commit: &Commit,
                    def_improvement_threshold: f64,
                    def_regression_threshold: f64)
                    -> Result<(Option<Config>, TastingResult), String> {
    println!("Tasting commit {}", commit.id);
    ws.checkout_commit(&commit.id)?;

    let branch = match push.push_ref {
        None => None,
        Some(ref pr) => {
            match pr.rfind("/") {
                None => None,
                Some(i) => Some(String::from(&pr[i + 1..])),
            }
        }
    };

    let build_success = update(&ws.path).success() && build(&ws.path).success();
    let test_success = test(&ws.path).success();

    let cfg = match parse_config(Path::new(&format!("{}/taster.toml", ws.path)),
                                 def_improvement_threshold,
                                 def_regression_threshold) {
        Ok(c) => c,
        Err(e) => {
            match e.kind() {
                io::ErrorKind::NotFound => {
                    println!("Skipping commit {} which doesn't have a Taster config.",
                             commit.id);
                    return Ok((None,
                               TastingResult {
                                   branch: branch,
                                   commit: commit.clone(),
                                   build: build_success,
                                   test: test_success,
                                   bench: false,
                                   results: None,
                               }));
                }
                io::ErrorKind::InvalidData => {
                    println!("Skipping commit {} which has an invalid Taster config.",
                             commit.id);
                    return Ok((None,
                               TastingResult {
                                   branch: branch,
                                   commit: commit.clone(),
                                   build: build_success,
                                   test: test_success,
                                   bench: false,
                                   results: None,
                               }));
                }
                _ => unimplemented!(),
            }
        }
    };

    let bench_out = match branch {
        Some(ref branch) => {
            let branch_history = history.entry(branch.clone()).or_insert(HashMap::new());
            cfg.benchmarks
                .iter()
                .map(|b| {
                    let new_result = benchmark(&ws.path, b, branch_history.get(&b.name));
                    branch_history.insert(b.name.clone(), new_result.1.clone());
                    new_result
                })
                .collect::<Vec<(ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
        }
        None => {
            cfg.benchmarks
                .iter()
                .map(|b| benchmark(&ws.path, b, None))
                .collect::<Vec<(ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
        }
    };
    let bench_success = bench_out.iter().all(|x| x.0.success());
    let bench_results = bench_out.iter().map(|x| x.1.clone()).collect();

    Ok((Some(cfg),
        TastingResult {
            branch: branch,
            commit: commit.clone(),
            build: build_success,
            test: test_success,
            bench: bench_success,
            results: Some(bench_results),
        }))
}

fn test(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("test")
        .env("RUST_BACKTRACE", "1")
        .env("RUST_TEST_THREADS", "1")
        .status()
        .expect("Failed to execute 'cargo test'!")
}

fn update(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("update")
        .status()
        .expect("Failed to execute 'cargo update'!")
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_imports)]
use super::*;
use std::env;

const ENV_KEY: &str = "PROXER_API_KEY";

#[test]
fn api_response() {
	// is the api structured as we want it to be?
	let client =  Client::with_env_key(ENV_KEY).unwrap();

	let res = client.api().info().get_fullentry(parameter::InfoGetFullEntry {
		id: 53
	}).send();

	println!();

	match res {
		Err(e) => {
			match e {
				error::Error::Api(e) => {
					match e.error() {
						error::api::Errcode::NoApiPermissions => return,
						_ => panic!("this error is not expected"),
					}
				},
				error::Error::Json(e) => panic!("can't parse json: {}", e),
				error::Error::Unknown => panic!("unknown error"),
				_ => return,
			}
		},

		Ok(r) => {
			assert_eq!(r.medium, "animeseries");
		}
	}
}
<commit_msg>make the test pass<commit_after>#![allow(dead_code, unused_imports)]
use super::*;
use std::env;

const ENV_KEY: &str = "PROXER_API_KEY";

#[test]
fn api_response() {
	// is the api structured as we want it to be?
	let client =  Client::with_env_key(ENV_KEY).unwrap();

    let req = client
        .api()
        .info()
        .get_fullentry(parameter::InfoGetFullEntry {id: 53});

    let res = client.execute(req);

	eprintln!();

	match res {
		Err(e) => {
			match e {
				error::Error::Api(e) => {
					match e.error() {
						error::api::Errcode::NoApiPermissions => return,
						_ => panic!("this error is not expected"),
					}
				},
				error::Error::Json(e) => panic!("can't parse json: {}", e),
				error::Error::Unknown => panic!("unknown error"),
				_ => return,
			}
		},

		Ok(r) => {
			assert_eq!(r.medium, "animeseries");
		}
	}
}
<|endoftext|>