<commit_before>// Support code for rustc's built in test runner generator. Currently,
// none of this is meant for users. It is intended to support the
// simplest interface possible for representing and running tests
// while providing a base that other test frameworks may build off of.

import sort = sort::ivector;
import generic_os::getenv;

export test_name;
export test_fn;
export test_desc;
export test_main;
export test_result;
export test_opts;
export tr_ok;
export tr_failed;
export tr_ignored;
export run_tests_console;
export run_tests_console_;
export run_test;
export filter_tests;
export parse_opts;
export test_to_task;
export default_test_to_task;
export configure_test_task;

// The name of a test. By convention this follows the rules for rust
// paths, i.e it should be a series of identifiers seperated by double
// colons. This way if some test runner wants to arrange the tests
// heirarchically it may.
type test_name = str;

// A function that runs a test. If the function returns successfully,
// the test succeeds; if the function fails then the test fails. We
// may need to come up with a more clever definition of test in order
// to support isolation of tests into tasks.
type test_fn = fn() ;

// The definition of a single test. A test runner will run a list of
// these.
type test_desc = {name: test_name, fn: test_fn, ignore: bool};

// The default console test runner. It accepts the command line
// arguments and a vector of test_descs (generated at compile time).
fn test_main(args: &vec[str], tests: &test_desc[]) {
    let ivec_args =
        { let iargs = ~[]; for arg: str  in args { iargs += ~[arg] } iargs };
    check (ivec::is_not_empty(ivec_args));
    let opts =
        alt parse_opts(ivec_args) {
          either::left(o) { o }
          either::right(m) { fail m }
        };
    if !run_tests_console(opts, tests) { fail "Some tests failed"; }
}

type test_opts = {filter: option::t[str], run_ignored: bool};

type opt_res = either::t[test_opts, str];

// Parses command line arguments into test options
fn parse_opts(args: &str[]) : ivec::is_not_empty(args) -> opt_res {

    // FIXME (#649): Shouldn't have to check here
    check (ivec::is_not_empty(args));
    let args_ = ivec::tail(args);
    let opts = ~[getopts::optflag("ignored")];
    let match =
        alt getopts::getopts_ivec(args_, opts) {
          getopts::success(m) { m }
          getopts::failure(f) { ret either::right(getopts::fail_str(f)) }
        };

    let filter =
        if vec::len(match.free) > 0u {
            option::some(match.free.(0))
        } else { option::none };

    let run_ignored = getopts::opt_present(match, "ignored");

    let test_opts = {filter: filter, run_ignored: run_ignored};

    ret either::left(test_opts);
}

tag test_result { tr_ok; tr_failed; tr_ignored; }

// To get isolation and concurrency tests have to be run in their own tasks.
// In cases where test functions and closures it is not ok to just dump them
// into a task and run them, so this transformation gives the caller a chance
// to create the test task.
type test_to_task = fn(&fn()) -> task ;

// A simple console test runner
fn run_tests_console(opts: &test_opts, tests: &test_desc[]) -> bool {
    run_tests_console_(opts, tests, default_test_to_task)
}

fn run_tests_console_(opts: &test_opts, tests: &test_desc[],
                      to_task: &test_to_task) -> bool {

    type test_state = @{
        out: io::writer,
        use_color: bool,
        mutable total: uint,
        mutable passed: uint,
        mutable failed: uint,
        mutable ignored: uint,
        mutable failures: test_desc[]
    };

    fn callback(event: testevent, st: test_state) {
        alt event {
          te_filtered(filtered_tests) {
            st.total = ivec::len(filtered_tests);
            st.out.write_line(#fmt("\nrunning %u tests", st.total));
          }
          te_result(test, result) {
            st.out.write_str(#fmt("test %s ... ", test.name));
            alt result {
              tr_ok. {
                st.passed += 1u;
                write_ok(st.out, st.use_color);
                st.out.write_line("");
              }
              tr_failed. {
                st.failed += 1u;
                write_failed(st.out, st.use_color);
                st.out.write_line("");
                st.failures += ~[test];
              }
              tr_ignored. {
                st.ignored += 1u;
                write_ignored(st.out, st.use_color);
                st.out.write_line("");
              }
            }
          }
        }
    }

    let st = @{
        out: io::stdout(),
        use_color: use_color(),
        mutable total: 0u,
        mutable passed: 0u,
        mutable failed: 0u,
        mutable ignored: 0u,
        mutable failures: ~[]
    };

    run_tests(opts, tests, to_task,
              bind callback(_, st));

    assert st.passed + st.failed + st.ignored == st.total;
    let success = st.failed == 0u;

    if !success {
        st.out.write_line("\nfailures:");
        for test: test_desc in st.failures {
            let testname = test.name; // Satisfy alias analysis
            st.out.write_line(#fmt("    %s", testname));
        }
    }

    st.out.write_str(#fmt("\nresult: "));
    if success {
        write_ok(st.out, st.use_color);
    } else { write_failed(st.out, st.use_color); }
    st.out.write_str(#fmt(". %u passed; %u failed; %u ignored\n\n",
                       st.passed, st.failed, st.ignored));

    ret success;

    fn write_ok(out: &io::writer, use_color: bool) {
        write_pretty(out, "ok", term::color_green, use_color);
    }

    fn write_failed(out: &io::writer, use_color: bool) {
        write_pretty(out, "FAILED", term::color_red, use_color);
    }

    fn write_ignored(out: &io::writer, use_color: bool) {
        write_pretty(out, "ignored", term::color_yellow, use_color);
    }

    fn write_pretty(out: &io::writer, word: &str, color: u8,
                    use_color: bool) {
        if use_color && term::color_supported() {
            term::fg(out.get_buf_writer(), color);
        }
        out.write_str(word);
        if use_color && term::color_supported() {
            term::reset(out.get_buf_writer());
        }
    }
}

fn use_color() -> bool {
    ret get_concurrency() == 1u;
}

tag testevent {
    te_filtered(test_desc[]);
    te_result(test_desc, test_result);
}

fn run_tests(opts: &test_opts, tests: &test_desc[],
             to_task: &test_to_task, callback: fn(testevent)) {

    let filtered_tests = filter_tests(opts, tests);

    callback(te_filtered(filtered_tests));

    // It's tempting to just spawn all the tests at once but that doesn't
    // provide a great user experience because you might sit waiting for the
    // result of a particular test for an unusually long amount of time.
    let concurrency = get_concurrency();
    log #fmt("using %u test tasks", concurrency);
    let total = ivec::len(filtered_tests);
    let run_idx = 0u;
    let wait_idx = 0u;
    let futures = ~[];

    while wait_idx < total {
        while ivec::len(futures) < concurrency && run_idx < total {
            futures += ~[run_test(filtered_tests.(run_idx), to_task)];
            run_idx += 1u;
        }

        let future = futures.(0);
        let result = future.wait();
        callback(te_result(future.test, result));
        futures = ivec::slice(futures, 1u, ivec::len(futures));
        wait_idx += 1u;
    }
}

fn get_concurrency() -> uint {
    alt getenv("RUST_THREADS") {
      option::some(t) {
        let threads = uint::parse_buf(str::bytes(t), 10u);
        threads > 0u ? threads : 1u
      }
      option::none. { 1u }
    }
}

fn filter_tests(opts: &test_opts, tests: &test_desc[]) -> test_desc[] {
    let filtered = tests;

    // Remove tests that don't match the test filter
    filtered =
        if option::is_none(opts.filter) {
            filtered
        } else {
            let filter_str =
                alt opts.filter {
                  option::some(f) { f }
                  option::none. { "" }
                };

            let filter =
                bind fn (test: &test_desc, filter_str: str) ->
                        option::t[test_desc] {
                         if str::find(test.name, filter_str) >= 0 {
                             ret option::some(test);
                         } else { ret option::none; }
                     }(_, filter_str);

            ivec::filter_map(filter, filtered)
        };

    // Maybe pull out the ignored test and unignore them
    filtered =
        if !opts.run_ignored {
            filtered
        } else {
            let filter =
                fn (test: &test_desc) -> option::t[test_desc] {
                    if test.ignore {
                        ret option::some({name: test.name,
                                          fn: test.fn,
                                          ignore: false});
                    } else { ret option::none; }
                };

            ivec::filter_map(filter, filtered)
        };

    // Sort the tests alphabetically
    filtered =
        {
            fn lteq(t1: &test_desc, t2: &test_desc) -> bool {
                str::lteq(t1.name, t2.name)
            }
            sort::merge_sort(lteq, filtered)
        };

    ret filtered;
}

type test_future =
    {test: test_desc, fnref: @fn() , wait: fn() -> test_result };

fn run_test(test: &test_desc, to_task: &test_to_task) -> test_future {
    // FIXME: Because of the unsafe way we're passing the test function
    // to the test task, we need to make sure we keep a reference to that
    // function around for longer than the lifetime of the task. To that end
    // we keep the function boxed in the test future.
    let fnref = @test.fn;
    if !test.ignore {
        let test_task = to_task(*fnref);
        ret {test: test,
             fnref: fnref,
             wait:
                 bind fn (test_task: &task) -> test_result {
                          alt task::join(test_task) {
                            task::tr_success. { tr_ok }
                            task::tr_failure. { tr_failed }
                          }
                      }(test_task)};
    } else {
        ret {test: test,
             fnref: fnref,
             wait: fn () -> test_result { tr_ignored }};
    }
}

native "rust" mod rustrt {
    fn hack_allow_leaks();
}

// We need to run our tests in another task in order to trap test failures.
// But, at least currently, functions can't be used as spawn arguments so
// we've got to treat our test functions as unsafe pointers.  This function
// only works with functions that don't contain closures.
fn default_test_to_task(f: &fn()) -> task {
    fn run_task(fptr: *mutable fn() ) {
        configure_test_task();
        // Run the test
        (*fptr)()
    }
    let fptr = ptr::addr_of(f);
    ret spawn run_task(fptr);
}

// Call from within a test task to make sure it's set up correctly
fn configure_test_task() {
    // If this task fails we don't want that failure to propagate to the
    // test runner or else we couldn't keep running tests
    task::unsupervise();

    // FIXME (236): Hack supreme - unwinding doesn't work yet so if this
    // task fails memory will not be freed correctly. This turns off the
    // sanity checks in the runtime's memory region for the task, so that
    // the test runner can continue.
    rustrt::hack_allow_leaks();
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Base test concurrency on the number of scheduler threads.<commit_after>// Support code for rustc's built in test runner generator. Currently,
// none of this is meant for users. It is intended to support the
// simplest interface possible for representing and running tests
// while providing a base that other test frameworks may build off of.

import sort = sort::ivector;
import generic_os::getenv;

export test_name;
export test_fn;
export test_desc;
export test_main;
export test_result;
export test_opts;
export tr_ok;
export tr_failed;
export tr_ignored;
export run_tests_console;
export run_tests_console_;
export run_test;
export filter_tests;
export parse_opts;
export test_to_task;
export default_test_to_task;
export configure_test_task;

native "rust" mod rustrt {
    fn hack_allow_leaks();
    fn sched_threads() -> uint;
}

// The name of a test. By convention this follows the rules for rust
// paths, i.e it should be a series of identifiers seperated by double
// colons. This way if some test runner wants to arrange the tests
// heirarchically it may.
type test_name = str;

// A function that runs a test. If the function returns successfully,
// the test succeeds; if the function fails then the test fails. We
// may need to come up with a more clever definition of test in order
// to support isolation of tests into tasks.
type test_fn = fn() ;

// The definition of a single test. A test runner will run a list of
// these.
type test_desc = {name: test_name, fn: test_fn, ignore: bool};

// The default console test runner. It accepts the command line
// arguments and a vector of test_descs (generated at compile time).
fn test_main(args: &vec[str], tests: &test_desc[]) {
    let ivec_args =
        { let iargs = ~[]; for arg: str  in args { iargs += ~[arg] } iargs };
    check (ivec::is_not_empty(ivec_args));
    let opts =
        alt parse_opts(ivec_args) {
          either::left(o) { o }
          either::right(m) { fail m }
        };
    if !run_tests_console(opts, tests) { fail "Some tests failed"; }
}

type test_opts = {filter: option::t[str], run_ignored: bool};

type opt_res = either::t[test_opts, str];

// Parses command line arguments into test options
fn parse_opts(args: &str[]) : ivec::is_not_empty(args) -> opt_res {

    // FIXME (#649): Shouldn't have to check here
    check (ivec::is_not_empty(args));
    let args_ = ivec::tail(args);
    let opts = ~[getopts::optflag("ignored")];
    let match =
        alt getopts::getopts_ivec(args_, opts) {
          getopts::success(m) { m }
          getopts::failure(f) { ret either::right(getopts::fail_str(f)) }
        };

    let filter =
        if vec::len(match.free) > 0u {
            option::some(match.free.(0))
        } else { option::none };

    let run_ignored = getopts::opt_present(match, "ignored");

    let test_opts = {filter: filter, run_ignored: run_ignored};

    ret either::left(test_opts);
}

tag test_result { tr_ok; tr_failed; tr_ignored; }

// To get isolation and concurrency tests have to be run in their own tasks.
// In cases where test functions and closures it is not ok to just dump them
// into a task and run them, so this transformation gives the caller a chance
// to create the test task.
type test_to_task = fn(&fn()) -> task ;

// A simple console test runner
fn run_tests_console(opts: &test_opts, tests: &test_desc[]) -> bool {
    run_tests_console_(opts, tests, default_test_to_task)
}

fn run_tests_console_(opts: &test_opts, tests: &test_desc[],
                      to_task: &test_to_task) -> bool {

    type test_state = @{
        out: io::writer,
        use_color: bool,
        mutable total: uint,
        mutable passed: uint,
        mutable failed: uint,
        mutable ignored: uint,
        mutable failures: test_desc[]
    };

    fn callback(event: testevent, st: test_state) {
        alt event {
          te_filtered(filtered_tests) {
            st.total = ivec::len(filtered_tests);
            st.out.write_line(#fmt("\nrunning %u tests", st.total));
          }
          te_result(test, result) {
            st.out.write_str(#fmt("test %s ... ", test.name));
            alt result {
              tr_ok. {
                st.passed += 1u;
                write_ok(st.out, st.use_color);
                st.out.write_line("");
              }
              tr_failed. {
                st.failed += 1u;
                write_failed(st.out, st.use_color);
                st.out.write_line("");
                st.failures += ~[test];
              }
              tr_ignored. {
                st.ignored += 1u;
                write_ignored(st.out, st.use_color);
                st.out.write_line("");
              }
            }
          }
        }
    }

    let st = @{
        out: io::stdout(),
        use_color: use_color(),
        mutable total: 0u,
        mutable passed: 0u,
        mutable failed: 0u,
        mutable ignored: 0u,
        mutable failures: ~[]
    };

    run_tests(opts, tests, to_task,
              bind callback(_, st));

    assert st.passed + st.failed + st.ignored == st.total;
    let success = st.failed == 0u;

    if !success {
        st.out.write_line("\nfailures:");
        for test: test_desc in st.failures {
            let testname = test.name; // Satisfy alias analysis
            st.out.write_line(#fmt("    %s", testname));
        }
    }

    st.out.write_str(#fmt("\nresult: "));
    if success {
        write_ok(st.out, st.use_color);
    } else { write_failed(st.out, st.use_color); }
    st.out.write_str(#fmt(". %u passed; %u failed; %u ignored\n\n",
                       st.passed, st.failed, st.ignored));

    ret success;

    fn write_ok(out: &io::writer, use_color: bool) {
        write_pretty(out, "ok", term::color_green, use_color);
    }

    fn write_failed(out: &io::writer, use_color: bool) {
        write_pretty(out, "FAILED", term::color_red, use_color);
    }

    fn write_ignored(out: &io::writer, use_color: bool) {
        write_pretty(out, "ignored", term::color_yellow, use_color);
    }

    fn write_pretty(out: &io::writer, word: &str, color: u8,
                    use_color: bool) {
        if use_color && term::color_supported() {
            term::fg(out.get_buf_writer(), color);
        }
        out.write_str(word);
        if use_color && term::color_supported() {
            term::reset(out.get_buf_writer());
        }
    }
}

fn use_color() -> bool {
    ret get_concurrency() == 1u;
}

tag testevent {
    te_filtered(test_desc[]);
    te_result(test_desc, test_result);
}

fn run_tests(opts: &test_opts, tests: &test_desc[],
             to_task: &test_to_task, callback: fn(testevent)) {

    let filtered_tests = filter_tests(opts, tests);

    callback(te_filtered(filtered_tests));

    // It's tempting to just spawn all the tests at once but that doesn't
    // provide a great user experience because you might sit waiting for the
    // result of a particular test for an unusually long amount of time.
    let concurrency = get_concurrency();
    log #fmt("using %u test tasks", concurrency);
    let total = ivec::len(filtered_tests);
    let run_idx = 0u;
    let wait_idx = 0u;
    let futures = ~[];

    while wait_idx < total {
        while ivec::len(futures) < concurrency && run_idx < total {
            futures += ~[run_test(filtered_tests.(run_idx), to_task)];
            run_idx += 1u;
        }

        let future = futures.(0);
        let result = future.wait();
        callback(te_result(future.test, result));
        futures = ivec::slice(futures, 1u, ivec::len(futures));
        wait_idx += 1u;
    }
}

fn get_concurrency() -> uint { rustrt::sched_threads() }

fn filter_tests(opts: &test_opts, tests: &test_desc[]) -> test_desc[] {
    let filtered = tests;

    // Remove tests that don't match the test filter
    filtered =
        if option::is_none(opts.filter) {
            filtered
        } else {
            let filter_str =
                alt opts.filter {
                  option::some(f) { f }
                  option::none. { "" }
                };

            let filter =
                bind fn (test: &test_desc, filter_str: str) ->
                        option::t[test_desc] {
                         if str::find(test.name, filter_str) >= 0 {
                             ret option::some(test);
                         } else { ret option::none; }
                     }(_, filter_str);

            ivec::filter_map(filter, filtered)
        };

    // Maybe pull out the ignored test and unignore them
    filtered =
        if !opts.run_ignored {
            filtered
        } else {
            let filter =
                fn (test: &test_desc) -> option::t[test_desc] {
                    if test.ignore {
                        ret option::some({name: test.name,
                                          fn: test.fn,
                                          ignore: false});
                    } else { ret option::none; }
                };

            ivec::filter_map(filter, filtered)
        };

    // Sort the tests alphabetically
    filtered =
        {
            fn lteq(t1: &test_desc, t2: &test_desc) -> bool {
                str::lteq(t1.name, t2.name)
            }
            sort::merge_sort(lteq, filtered)
        };

    ret filtered;
}

type test_future =
    {test: test_desc, fnref: @fn() , wait: fn() -> test_result };

fn run_test(test: &test_desc, to_task: &test_to_task) -> test_future {
    // FIXME: Because of the unsafe way we're passing the test function
    // to the test task, we need to make sure we keep a reference to that
    // function around for longer than the lifetime of the task. To that end
    // we keep the function boxed in the test future.
    let fnref = @test.fn;
    if !test.ignore {
        let test_task = to_task(*fnref);
        ret {test: test,
             fnref: fnref,
             wait:
                 bind fn (test_task: &task) -> test_result {
                          alt task::join(test_task) {
                            task::tr_success. { tr_ok }
                            task::tr_failure. { tr_failed }
                          }
                      }(test_task)};
    } else {
        ret {test: test,
             fnref: fnref,
             wait: fn () -> test_result { tr_ignored }};
    }
}

// We need to run our tests in another task in order to trap test failures.
// But, at least currently, functions can't be used as spawn arguments so
// we've got to treat our test functions as unsafe pointers.  This function
// only works with functions that don't contain closures.
fn default_test_to_task(f: &fn()) -> task {
    fn run_task(fptr: *mutable fn() ) {
        configure_test_task();
        // Run the test
        (*fptr)()
    }
    let fptr = ptr::addr_of(f);
    ret spawn run_task(fptr);
}

// Call from within a test task to make sure it's set up correctly
fn configure_test_task() {
    // If this task fails we don't want that failure to propagate to the
    // test runner or else we couldn't keep running tests
    task::unsupervise();

    // FIXME (236): Hack supreme - unwinding doesn't work yet so if this
    // task fails memory will not be freed correctly. This turns off the
    // sanity checks in the runtime's memory region for the task, so that
    // the test runner can continue.
    rustrt::hack_allow_leaks();
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::BTreeMap;
use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use dbus::{Connection, NameFlag};
use dbus::tree::{Factory, Tree, Property, MethodFn, MethodErr, EmitsChangedSignal};
use dbus::MessageItem;

use froyo::Froyo;
use blockdev::BlockMember;
use types::FroyoResult;

#[derive(Debug, Clone)]
pub struct DbusContext<'a> {
    name_prop: Arc<Property<MethodFn<'a>>>,
    pub remaining_prop: Arc<Property<MethodFn<'a>>>,
    pub total_prop: Arc<Property<MethodFn<'a>>>,
    pub status_prop: Arc<Property<MethodFn<'a>>>,
    pub running_status_prop: Arc<Property<MethodFn<'a>>>,
    pub block_devices_prop: Arc<Property<MethodFn<'a>>>,
}

impl<'a> DbusContext<'a> {
    pub fn update_one(prop: &Arc<Property<MethodFn<'a>>>, m: MessageItem)
                      -> FroyoResult<()> {
        prop.set_value(m);
        // TODO: result is signals we need to be sending for PropertyChanged???
        Ok(())
    }

    pub fn get_block_devices_msgitem(block_devs: &BTreeMap<String, BlockMember>)
                                     -> MessageItem {
        let mut msg_vec = Vec::new();
        for bd in block_devs.values() {
            let (bd_path, bd_status) = match *bd {
                BlockMember::Present(ref bd) =>
                // TODO: in-use vs not-in-use
                    (RefCell::borrow(&bd).path.to_string_lossy().into_owned(), 0u32),
                BlockMember::Absent(ref sbd) =>
                    (sbd.path.to_string_lossy().into_owned(), 3u32),
            };

            let entry = MessageItem::Struct(vec![bd_path.into(), bd_status.into()]);
            msg_vec.push(entry);
        }

        MessageItem::new_array(msg_vec)
            .expect("Froyodev with no blockdev members???")
    }
}

pub fn get_tree<'a>(c: &Connection, froyos: &mut Rc<RefCell<Vec<Rc<RefCell<Froyo<'a>>>>>>)
                       -> FroyoResult<Tree<MethodFn<'a>>> {
    c.register_name("org.freedesktop.Froyo1", NameFlag::ReplaceExisting as u32).unwrap();

    let f = Factory::new_fn();

    let froyos_closed_over = froyos.clone();
    let mut froyos = RefCell::borrow_mut(&froyos);

    let create_method = f.method("Create", move |m,_,_| {
        let mut items = m.get_items();
        if items.len() < 3 {
            return Err(MethodErr::no_arg())
        }

        let force: bool = try!(items.pop().ok_or_else(MethodErr::no_arg)
                               .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));
        let blockdevs = match try!(items.pop().ok_or_else(MethodErr::no_arg)) {
            MessageItem::Array(x, _) => x,
            x => return Err(MethodErr::invalid_arg(&x)),
        };
        let blockdevs = blockdevs.into_iter()
            .map(|x| PathBuf::from(x.inner::<&str>().unwrap()))
            .collect::<Vec<_>>();

        let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                        .and_then(|i| i.inner::<&str>()
                                  .map_err(|_| MethodErr::invalid_arg(&i))
                                  .map(|i| i.to_owned())));

        let froyo = match Froyo::new(&name, &blockdevs, force) {
            Ok(x) => x,
            Err(_) => return Err(MethodErr::failed(&"Froyo create failed")),
        };
        try!(froyo.save_state()
             .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

        let s = format!("/org/freedesktop/froyo/{}", froyo.id);
        let mr = m.method_return().append(s);

        RefCell::borrow_mut(&froyos_closed_over).push(Rc::new(RefCell::new(froyo)));

        Ok(vec![mr])
    })
        .in_arg(("name", "s"))
        .in_arg(("blockdevs", "as"))
        .in_arg(("force", "b"))
        .out_arg(("obj_path", "s"));

    let tree = f.tree();
    let tree = tree
        .add(f.object_path("/org/freedesktop/froyo")
             .introspectable()
             .object_manager()
             .add(f.interface("org.freedesktop.FroyoService1")
                  .add_m(create_method)
             ));

    let tree = froyos
        .iter_mut()
        .fold(tree, |tree, froyo| {

            let mut iface = f.interface("org.freedesktop.FroyoDevice1");
            let name_p = iface.add_p_ref(f.property(
                "Name", RefCell::borrow(&*froyo).name.to_owned()));
            let p_closed_over = name_p.clone();
            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("SetName", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                                    .and_then(|i| i.inner::<&str>()
                                              .map_err(|_| MethodErr::invalid_arg(&i))
                                              .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    froyo.name = name.clone();
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

                    try!(p_closed_over.set_value(name.into())
                         .map_err(|_| MethodErr::invalid_arg(&"name")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_name", "s")));

            let rem_p = iface.add_p_ref(f.property("RemainingSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let tot_p = iface.add_p_ref(f.property("TotalSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let status_p = iface.add_p_ref(f.property("Status", 0u32));
            let running_status_p = iface.add_p_ref(f.property("RunningStatus", 0u32));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("AddBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 2 {
                        return Err(MethodErr::no_arg())
                    }

                    let force: bool = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(
                                |i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));

                    let new_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.add_block_device(Path::new(&new_dev), force)
                        .map_err(|_| MethodErr::failed(&"Adding block device failed")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_device_path", "s"))
                    .in_arg(("force", "b")));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("RemoveBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let removing_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.remove_block_device(Path::new(&removing_dev))
                        .map_err(|_| MethodErr::failed(&"Removing block device failed")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_device_path", "s")));

            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("Reshape", move |m,_,_| {
                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.reshape()
                        .map_err(|_| MethodErr::failed(&"Reshape failed")));
                    Ok(vec![m.method_return()])
                }));

            let mut froyo = RefCell::borrow_mut(froyo);

            // Need to actually get values b/c I can't figure out how to
            // get a 0-length array of struct
            let bdev_msg = DbusContext::get_block_devices_msgitem(&froyo.block_devs);
            let block_devices_p = iface.add_p_ref(f.property("BlockDevices", bdev_msg));

            let path = format!("/org/freedesktop/froyo/{}", froyo.id);
            froyo.dbus_context = Some(DbusContext {
                name_prop: name_p,
                remaining_prop: rem_p,
                total_prop: tot_p,
                status_prop: status_p,
                running_status_prop: running_status_p,
                block_devices_prop: block_devices_p,
            });

            tree.add(f.object_path(path)
                     .introspectable()
                     .add(iface))
        });

    for froyo in &*froyos {
        try!(RefCell::borrow(&froyo).update_dbus());
    }

    tree.set_registered(&c, true).unwrap();

    Ok(tree)
}

<commit_msg>dbus: Call save_state in Add/RemoveDevice calls<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::BTreeMap;
use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use dbus::{Connection, NameFlag};
use dbus::tree::{Factory, Tree, Property, MethodFn, MethodErr, EmitsChangedSignal};
use dbus::MessageItem;

use froyo::Froyo;
use blockdev::BlockMember;
use types::FroyoResult;

#[derive(Debug, Clone)]
pub struct DbusContext<'a> {
    name_prop: Arc<Property<MethodFn<'a>>>,
    pub remaining_prop: Arc<Property<MethodFn<'a>>>,
    pub total_prop: Arc<Property<MethodFn<'a>>>,
    pub status_prop: Arc<Property<MethodFn<'a>>>,
    pub running_status_prop: Arc<Property<MethodFn<'a>>>,
    pub block_devices_prop: Arc<Property<MethodFn<'a>>>,
}

impl<'a> DbusContext<'a> {
    pub fn update_one(prop: &Arc<Property<MethodFn<'a>>>, m: MessageItem)
                      -> FroyoResult<()> {
        prop.set_value(m);
        // TODO: result is signals we need to be sending for PropertyChanged???
        Ok(())
    }

    pub fn get_block_devices_msgitem(block_devs: &BTreeMap<String, BlockMember>)
                                     -> MessageItem {
        let mut msg_vec = Vec::new();
        for bd in block_devs.values() {
            let (bd_path, bd_status) = match *bd {
                BlockMember::Present(ref bd) =>
                // TODO: in-use vs not-in-use
                    (RefCell::borrow(&bd).path.to_string_lossy().into_owned(), 0u32),
                BlockMember::Absent(ref sbd) =>
                    (sbd.path.to_string_lossy().into_owned(), 3u32),
            };

            let entry = MessageItem::Struct(vec![bd_path.into(), bd_status.into()]);
            msg_vec.push(entry);
        }

        MessageItem::new_array(msg_vec)
            .expect("Froyodev with no blockdev members???")
    }
}

pub fn get_tree<'a>(c: &Connection, froyos: &mut Rc<RefCell<Vec<Rc<RefCell<Froyo<'a>>>>>>)
                       -> FroyoResult<Tree<MethodFn<'a>>> {
    c.register_name("org.freedesktop.Froyo1", NameFlag::ReplaceExisting as u32).unwrap();

    let f = Factory::new_fn();

    let froyos_closed_over = froyos.clone();
    let mut froyos = RefCell::borrow_mut(&froyos);

    let create_method = f.method("Create", move |m,_,_| {
        let mut items = m.get_items();
        if items.len() < 3 {
            return Err(MethodErr::no_arg())
        }

        let force: bool = try!(items.pop().ok_or_else(MethodErr::no_arg)
                               .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));
        let blockdevs = match try!(items.pop().ok_or_else(MethodErr::no_arg)) {
            MessageItem::Array(x, _) => x,
            x => return Err(MethodErr::invalid_arg(&x)),
        };
        let blockdevs = blockdevs.into_iter()
            .map(|x| PathBuf::from(x.inner::<&str>().unwrap()))
            .collect::<Vec<_>>();

        let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                        .and_then(|i| i.inner::<&str>()
                                  .map_err(|_| MethodErr::invalid_arg(&i))
                                  .map(|i| i.to_owned())));

        let froyo = match Froyo::new(&name, &blockdevs, force) {
            Ok(x) => x,
            Err(_) => return Err(MethodErr::failed(&"Froyo create failed")),
        };
        try!(froyo.save_state()
             .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

        let s = format!("/org/freedesktop/froyo/{}", froyo.id);
        let mr = m.method_return().append(s);

        RefCell::borrow_mut(&froyos_closed_over).push(Rc::new(RefCell::new(froyo)));

        Ok(vec![mr])
    })
        .in_arg(("name", "s"))
        .in_arg(("blockdevs", "as"))
        .in_arg(("force", "b"))
        .out_arg(("obj_path", "s"));

    let tree = f.tree();
    let tree = tree
        .add(f.object_path("/org/freedesktop/froyo")
             .introspectable()
             .object_manager()
             .add(f.interface("org.freedesktop.FroyoService1")
                  .add_m(create_method)
             ));

    let tree = froyos
        .iter_mut()
        .fold(tree, |tree, froyo| {

            let mut iface = f.interface("org.freedesktop.FroyoDevice1");
            let name_p = iface.add_p_ref(f.property(
                "Name", RefCell::borrow(&*froyo).name.to_owned()));
            let p_closed_over = name_p.clone();
            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("SetName", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                                    .and_then(|i| i.inner::<&str>()
                                              .map_err(|_| MethodErr::invalid_arg(&i))
                                              .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    froyo.name = name.clone();
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

                    try!(p_closed_over.set_value(name.into())
                         .map_err(|_| MethodErr::invalid_arg(&"name")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_name", "s")));

            let rem_p = iface.add_p_ref(f.property("RemainingSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let tot_p = iface.add_p_ref(f.property("TotalSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let status_p = iface.add_p_ref(f.property("Status", 0u32));
            let running_status_p = iface.add_p_ref(f.property("RunningStatus", 0u32));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("AddBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 2 {
                        return Err(MethodErr::no_arg())
                    }

                    let force: bool = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(
                                |i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));

                    let new_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.add_block_device(Path::new(&new_dev), force)
                        .map_err(|_| MethodErr::failed(&"Adding block device failed")));
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_device_path", "s"))
                    .in_arg(("force", "b")));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("RemoveBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let removing_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.remove_block_device(Path::new(&removing_dev))
                        .map_err(|_| MethodErr::failed(&"Removing block device failed")));
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_device_path", "s")));

            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("Reshape", move |m,_,_| {
                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.reshape()
                        .map_err(|_| MethodErr::failed(&"Reshape failed")));
                    Ok(vec![m.method_return()])
                }));

            let mut froyo = RefCell::borrow_mut(froyo);

            // Need to actually get values b/c I can't figure out how to
            // get a 0-length array of struct
            let bdev_msg = DbusContext::get_block_devices_msgitem(&froyo.block_devs);
            let block_devices_p = iface.add_p_ref(f.property("BlockDevices", bdev_msg));

            let path = format!("/org/freedesktop/froyo/{}", froyo.id);
            froyo.dbus_context = Some(DbusContext {
                name_prop: name_p,
                remaining_prop: rem_p,
                total_prop: tot_p,
                status_prop: status_p,
                running_status_prop: running_status_p,
                block_devices_prop: block_devices_p,
            });

            tree.add(f.object_path(path)
                     .introspectable()
                     .add(iface))
        });

    for froyo in &*froyos {
        try!(RefCell::borrow(&froyo).update_dbus());
    }

    tree.set_registered(&c, true).unwrap();

    Ok(tree)
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2014 Jeremy Letang (letang.jeremy@gmail.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.

extern crate collections;

use std::vec::Vec;
use collections::HashMap;
use std::fmt;

#[deriving(Clone, Show, Eq, Ord)]
pub struct ItemId(Vec<i32>);

#[deriving(Clone, Show, Eq, Ord)]
pub enum TagType {
    Block,
    Inline,
    PlainText,
    Header,
    HamlComment,
    HtmlComment,
    Root
}

#[deriving(Clone, Eq)]
pub struct DomTree {
    priv root: Item,
    priv cur_elt_id: ItemId,
    priv next_id: ItemId
}

#[deriving(Clone, Eq, Show)]
pub struct Item {
    priv parent: ItemId,
    priv childs: Vec<Item>,
    priv attributes: HashMap<~str, Vec<~str>>,
    priv tag: ~str,
    priv content: ~str,
    priv tag_type: TagType
}

impl DomTree {
    pub fn new() -> DomTree {
        DomTree {
            root: Item::root(),
            cur_elt_id: ItemId(vec!(0)),
            next_id: ItemId(vec!(0))
        }
    }

    pub fn get_elt<'a>(&'a self, elt_id: ItemId) -> Option<&'a Item> {
        let ItemId(mut tree_path) = elt_id.clone();
        match tree_path.shift() {
            Some(_) => rec_get_elt(&self.root, tree_path),
            None => None
        }
    }

    pub fn set_current_elt(&mut self, id: ItemId) {
        self.cur_elt_id = id
    }

    pub fn get_current_elt(&self) -> ItemId {
        self.cur_elt_id.clone()
    }

    pub fn insert(&mut self, mut elt: Item) -> Option<ItemId> {
        let ItemId(mut n) = self.cur_elt_id.clone();
        elt.parent = self.cur_elt_id.clone();
         match n.shift() {
            Some(_) => {
                let tmp_pos = rec_insert_elt(&mut self.root, elt, n);
                let ItemId(mut r) = self.cur_elt_id.clone();
                r.push(tmp_pos);
                self.cur_elt_id = ItemId(r.clone());
                Some(ItemId(r))
            },
            None => None
        }
    }

    pub fn insert_and_back(&mut self, elt: Item) -> Option<ItemId> {
        self.insert(elt);
        self.back();
        Some(self.cur_elt_id.clone())
    }

    pub fn back(&mut self) {
        let ItemId(mut tree_path) = self.cur_elt_id.clone();
        tree_path.pop();
        self.cur_elt_id = ItemId(tree_path);
    }

    pub fn get_current_lvl(&self) -> u32 {
        let ItemId(tree_path) = self.cur_elt_id.clone();
        (tree_path.len() - 1) as u32
    }
}

fn rec_get_elt<'a>(elt: &'a Item,
                   mut tree_path: Vec<i32>) -> Option<&'a Item> {
    match tree_path.shift() {
        Some(idx) => rec_get_elt(elt.get_childs().get(idx as uint), tree_path),
        None => Some(elt)
    }
}

fn rec_insert_elt(elt: &mut Item,
                  new_elt: Item,
                  mut tree_path: Vec<i32>) -> i32 {
    match tree_path.shift() {
        Some(idx) => rec_insert_elt(elt.get_mut_childs().get_mut(idx as uint),
                                    new_elt,
                                    tree_path),
        None => elt.add_child(new_elt)
    }
}

impl Item {
    pub fn root() -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: HashMap::new(),
            tag: ~"",
            content: ~"",
            tag_type: Root
        }
    }

    pub fn haml_comment() -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: HashMap::new(),
            tag: ~"",
            content: ~"",
            tag_type: HamlComment
        }
    }

    pub fn block(tag: ~str,
                 attributes: HashMap<~str, Vec<~str>>) -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: attributes,
            tag: if tag == ~"" { ~"div" } else { tag },
            content: ~"",
            tag_type: Block
        }
    }

    pub fn header(header: ~str) -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: HashMap::new(),
            tag: ~"",
            content: header,
            tag_type: Header
        }
    }

    pub fn plain_text(text: ~str) -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: HashMap::new(),
            tag: ~"",
            content: text,
            tag_type: PlainText
        }
    }

    pub fn inline(tag: ~str,
                  attributes: HashMap<~str, Vec<~str>>,
                  content: ~str) -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: attributes,
            tag: if tag == ~"" { ~"div" } else { tag },
            content: content,
            tag_type: Inline
        }
    }

    pub fn had_child(&self) -> bool {
        self.childs.len() != 0
    }

    pub fn get_childs<'a>(&'a self) -> &'a Vec<Item> {
        &self.childs
    }

    pub fn get_mut_childs<'a>(&'a mut self) -> &'a mut Vec<Item> {
        &mut self.childs
    }

    pub fn get_parent_id(&self) -> ItemId {
        self.parent.clone()
    }

    pub fn add_child(&mut self, elt: Item) -> i32 {
        self.childs.push(elt);
        (self.childs.len() - 1) as i32
    }
}

fn rec_show(elt: &Item,
            f: &mut fmt::Formatter,
            indent: ~str) -> fmt::Result {
    let mut res: fmt::Result = Ok(());
    for e in elt.get_childs().iter() {
        try!(write!(f.buf, "{}", indent));
        match e.tag_type {
            PlainText => try!(write!(f.buf, "{}\n", e.content)),
            Inline    => try!(write!(f.buf, "<{}>{}</{}>\n", e.tag, e.content, e.tag)),
            Block     => {
                if e.get_childs().len() == 0 { try!(write!(f.buf, "<{}>", e.tag)); }
                else { try!(write!(f.buf, "<{}>\n", e.tag)); }
                res = rec_show(e, f, indent + "  ");
                if e.get_childs().len() == 0 { try!(write!(f.buf, "</{}>\n", e.tag)); }
                else { try!(write!(f.buf, "{}</{}>\n", indent, e.tag)); }
            },
            _         => {}
        }
    }
    res
}

impl fmt::Show for DomTree {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        rec_show(&self.root, f, ~"")
    }
}
<commit_msg>display the attributes<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2014 Jeremy Letang (letang.jeremy@gmail.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.

extern crate collections;

use std::vec::Vec;
use collections::HashMap;
use std::fmt;

#[deriving(Clone, Show, Eq, Ord)]
pub struct ItemId(Vec<i32>);

#[deriving(Clone, Show, Eq, Ord)]
pub enum TagType {
    Block,
    Inline,
    PlainText,
    Header,
    HamlComment,
    HtmlComment,
    Root
}

#[deriving(Clone, Eq)]
pub struct DomTree {
    priv root: Item,
    priv cur_elt_id: ItemId,
    priv next_id: ItemId
}

#[deriving(Clone, Eq, Show)]
pub struct Item {
    priv parent: ItemId,
    priv childs: Vec<Item>,
    priv attributes: HashMap<~str, Vec<~str>>,
    priv tag: ~str,
    priv content: ~str,
    priv tag_type: TagType
}

impl DomTree {
    pub fn new() -> DomTree {
        DomTree {
            root: Item::root(),
            cur_elt_id: ItemId(vec!(0)),
            next_id: ItemId(vec!(0))
        }
    }

    pub fn get_elt<'a>(&'a self, elt_id: ItemId) -> Option<&'a Item> {
        let ItemId(mut tree_path) = elt_id.clone();
        match tree_path.shift() {
            Some(_) => rec_get_elt(&self.root, tree_path),
            None => None
        }
    }

    pub fn set_current_elt(&mut self, id: ItemId) {
        self.cur_elt_id = id
    }

    pub fn get_current_elt(&self) -> ItemId {
        self.cur_elt_id.clone()
    }

    pub fn insert(&mut self, mut elt: Item) -> Option<ItemId> {
        let ItemId(mut n) = self.cur_elt_id.clone();
        elt.parent = self.cur_elt_id.clone();
         match n.shift() {
            Some(_) => {
                let tmp_pos = rec_insert_elt(&mut self.root, elt, n);
                let ItemId(mut r) = self.cur_elt_id.clone();
                r.push(tmp_pos);
                self.cur_elt_id = ItemId(r.clone());
                Some(ItemId(r))
            },
            None => None
        }
    }

    pub fn insert_and_back(&mut self, elt: Item) -> Option<ItemId> {
        self.insert(elt);
        self.back();
        Some(self.cur_elt_id.clone())
    }

    pub fn back(&mut self) {
        let ItemId(mut tree_path) = self.cur_elt_id.clone();
        tree_path.pop();
        self.cur_elt_id = ItemId(tree_path);
    }

    pub fn get_current_lvl(&self) -> u32 {
        let ItemId(tree_path) = self.cur_elt_id.clone();
        (tree_path.len() - 1) as u32
    }
}

fn rec_get_elt<'a>(elt: &'a Item,
                   mut tree_path: Vec<i32>) -> Option<&'a Item> {
    match tree_path.shift() {
        Some(idx) => rec_get_elt(elt.get_childs().get(idx as uint), tree_path),
        None => Some(elt)
    }
}

fn rec_insert_elt(elt: &mut Item,
                  new_elt: Item,
                  mut tree_path: Vec<i32>) -> i32 {
    match tree_path.shift() {
        Some(idx) => rec_insert_elt(elt.get_mut_childs().get_mut(idx as uint),
                                    new_elt,
                                    tree_path),
        None => elt.add_child(new_elt)
    }
}

impl Item {
    pub fn root() -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: HashMap::new(),
            tag: ~"",
            content: ~"",
            tag_type: Root
        }
    }

    pub fn haml_comment() -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: HashMap::new(),
            tag: ~"",
            content: ~"",
            tag_type: HamlComment
        }
    }

    pub fn block(tag: ~str,
                 attributes: HashMap<~str, Vec<~str>>) -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: attributes,
            tag: if tag == ~"" { ~"div" } else { tag },
            content: ~"",
            tag_type: Block
        }
    }

    pub fn header(header: ~str) -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: HashMap::new(),
            tag: ~"",
            content: header,
            tag_type: Header
        }
    }

    pub fn plain_text(text: ~str) -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: HashMap::new(),
            tag: ~"",
            content: text,
            tag_type: PlainText
        }
    }

    pub fn inline(tag: ~str,
                  attributes: HashMap<~str, Vec<~str>>,
                  content: ~str) -> Item {
        Item {
            parent: ItemId(vec!(0)),
            childs: Vec::new(),
            attributes: attributes,
            tag: if tag == ~"" { ~"div" } else { tag },
            content: content,
            tag_type: Inline
        }
    }

    pub fn had_child(&self) -> bool {
        self.childs.len() != 0
    }

    pub fn get_childs<'a>(&'a self) -> &'a Vec<Item> {
        &self.childs
    }

    pub fn get_mut_childs<'a>(&'a mut self) -> &'a mut Vec<Item> {
        &mut self.childs
    }

    pub fn get_parent_id(&self) -> ItemId {
        self.parent.clone()
    }

    pub fn add_child(&mut self, elt: Item) -> i32 {
        self.childs.push(elt);
        (self.childs.len() - 1) as i32
    }
}

fn format_attribut(attributes: &HashMap<~str, Vec<~str>>) -> ~str {
    let mut fmt = ~"";
    for (at, values) in attributes.iter() {
        fmt.push_str(format!(" {}=\'", at));
        for v in values.iter() {
            fmt.push_str(format!("{} ", v));
        }
        if values.len() > 0 { fmt.pop_char(); }
        fmt.push_str("\'");
    }
    fmt
}

fn rec_show(elt: &Item,
            f: &mut fmt::Formatter,
            indent: ~str) -> fmt::Result {
    let mut res: fmt::Result = Ok(());
    for e in elt.get_childs().iter() {
        try!(write!(f.buf, "{}", indent));
        let f_at = format_attribut(&e.attributes);
        match e.tag_type {
            PlainText => try!(write!(f.buf, "{}\n", e.content)),
            Inline    => try!(write!(f.buf, "<{}{}>{}</{}>\n", e.tag, f_at, e.content, e.tag)),
            Block     => {
                if e.get_childs().len() == 0 { try!(write!(f.buf, "<{}{}>", e.tag, f_at)); }
                else { try!(write!(f.buf, "<{}{}>\n", e.tag, f_at)); }
                res = rec_show(e, f, indent + "  ");
                if e.get_childs().len() == 0 { try!(write!(f.buf, "</{}>\n", e.tag)); }
                else { try!(write!(f.buf, "{}</{}>\n", indent, e.tag)); }
            },
            _         => {}
        }
    }
    res
}

impl fmt::Show for DomTree {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        rec_show(&self.root, f, ~"")
    }
}
<|endoftext|>


<commit_before>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::TranspositionTable;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    stop_when: TimeManagement,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    started_thinking_at: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64,
    searched_depth: u8,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            stop_when: TimeManagement::Infinite,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            started_thinking_at: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            searched_depth: 0,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We do not support the "searchmoves" parameter.

            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.is_thinking = true;
            self.is_pondering = ponder;
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };
            self.started_thinking_at = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.searched_depth = 0;

            self.commands
                .send(search::Command::Search {
                    search_id: 0,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.searched_depth >= d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            // let rep_type = match report {
            //     search::Report::Progress { searched_nodes, .. } => format!("progress {}", searched_nodes),
            //     search::Report::Done { .. } => "done".to_string(),
            // };
            // self.replies.push(EngineReply::Info(vec![
            //     ("string".to_string(), rep_type),
            // ]));
            if self.is_thinking {
                match report {
                    search::Report::Progress { searched_nodes, depth, .. } => {
                        self.searched_nodes = searched_nodes;
                        let duration = self.started_thinking_at.elapsed().unwrap();
                        self.searched_time = 1000 * duration.as_secs() +
                                             (duration.subsec_nanos() / 1000000) as u64;
                        if self.searched_depth < depth {
                            self.searched_depth = depth;
                            let mut p = self.position.clone();
                            let mut v = MoveStack::new();
                            let mut pv = String::from("");
                            let mut pv_length = 0;
                            let mut value = None;
                            'depthloop: for _ in 0..depth {
                                let m = match self.tt.probe(p.hash()) {
                                    Some(entry) => {
                                        if value.is_none() {
                                            value = Some(entry.value());
                                        }
                                        entry.move16()
                                    }
                                    None => 0,
                                };
                                if m != 0 {
                                    p.generate_moves(&mut v);
                                    while let Some(x) = v.pop() {
                                        if x.digest() == m && p.do_move(x) {
                                            pv_length += 1;
                                            pv.push_str(&x.notation());
                                            pv.push(' ');
                                            v.clear();
                                            continue 'depthloop;
                                        }
                                    }
                                }
                                break;
                            }
                            if searched_nodes > 0 && pv_length >= depth {
                                self.replies.push(EngineReply::Info(vec![
                                    ("depth".to_string(), format!("{}", depth)),
                                    ("score".to_string(), format!("cp {}", value.unwrap_or(666))),
                                    ("nodes".to_string(), format!("{}", searched_nodes)),
                                    ("time".to_string(), format!("{}", self.searched_time)),
                                    ("pv".to_string(), format!("{}", pv)),
                                ]));
                            }
                        }
                    }
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<commit_msg>renamed started_thinking_at field<commit_after>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::TranspositionTable;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    stop_when: TimeManagement,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64,
    searched_depth: u8,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            stop_when: TimeManagement::Infinite,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            searched_depth: 0,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We do not support the "searchmoves" parameter.

            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.is_thinking = true;
            self.is_pondering = ponder;
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };
            self.thinking_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.searched_depth = 0;

            self.commands
                .send(search::Command::Search {
                    search_id: 0,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time >= t,
                TimeManagement::MoveTimeHint(t) => self.searched_time >= t,
                TimeManagement::Nodes(n) => self.searched_nodes >= n,
                TimeManagement::Depth(d) => self.searched_depth >= d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            // let rep_type = match report {
            //     search::Report::Progress { searched_nodes, .. } => format!("progress {}", searched_nodes),
            //     search::Report::Done { .. } => "done".to_string(),
            // };
            // self.replies.push(EngineReply::Info(vec![
            //     ("string".to_string(), rep_type),
            // ]));
            if self.is_thinking {
                match report {
                    search::Report::Progress { searched_nodes, depth, .. } => {
                        self.searched_nodes = searched_nodes;
                        let duration = self.thinking_since.elapsed().unwrap();
                        self.searched_time = 1000 * duration.as_secs() +
                                             (duration.subsec_nanos() / 1000000) as u64;
                        if self.searched_depth < depth {
                            self.searched_depth = depth;
                            let mut p = self.position.clone();
                            let mut v = MoveStack::new();
                            let mut pv = String::from("");
                            let mut pv_length = 0;
                            let mut value = None;
                            'depthloop: for _ in 0..depth {
                                let m = match self.tt.probe(p.hash()) {
                                    Some(entry) => {
                                        if value.is_none() {
                                            value = Some(entry.value());
                                        }
                                        entry.move16()
                                    }
                                    None => 0,
                                };
                                if m != 0 {
                                    p.generate_moves(&mut v);
                                    while let Some(x) = v.pop() {
                                        if x.digest() == m && p.do_move(x) {
                                            pv_length += 1;
                                            pv.push_str(&x.notation());
                                            pv.push(' ');
                                            v.clear();
                                            continue 'depthloop;
                                        }
                                    }
                                }
                                break;
                            }
                            if searched_nodes > 0 && pv_length >= depth {
                                self.replies.push(EngineReply::Info(vec![
                                    ("depth".to_string(), format!("{}", depth)),
                                    ("score".to_string(), format!("cp {}", value.unwrap_or(666))),
                                    ("nodes".to_string(), format!("{}", searched_nodes)),
                                    ("time".to_string(), format!("{}", self.searched_time)),
                                    ("pv".to_string(), format!("{}", pv)),
                                ]));
                            }
                        }
                    }
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use aggregate::{Aggregate, AggregateCall};
use answer::Answer;
use data::Data;
use expr::Expr;
use query::Query;
use row::Row;
use source::{Source, SourceError};

#[derive(Clone, Debug, PartialEq)]
pub enum ExecuteError {
    SourceError(String),
    InvalidOrderClause(Expr),
}

impl From<SourceError> for ExecuteError {
    fn from(e: SourceError) -> Self {
        ExecuteError::SourceError(e.description)
    }
}

struct Executor {
    query: Query,
    aggregate_calls: Vec<AggregateCall>,
    order_indices: Vec<usize>,
}

impl Executor {
    fn new(query: Query) -> Result<Self, ExecuteError> {
        let aggregates = query.select.iter()
            .filter_map(|expr| expr.get_aggregate_call())
            .collect();

        let mut order_indices = Vec::new();
        for expr in query.order.iter() {
            let position = query.select.iter().position(|select| select == expr);
            let index = match position {
                Some(i) => i,
                None => return Err(ExecuteError::InvalidOrderClause(expr.clone())),
            };
            order_indices.push(index);
        }

        Ok(Executor {
            query: query,
            aggregate_calls: aggregates,
            order_indices: order_indices,
        })
    }

    fn execute(&self, source: Source) -> Result<Answer, ExecuteError> {
        let mut answer = if self.aggregate_calls.is_empty() {
            self.execute_non_aggregate(source)?
        } else {
            self.execute_aggregate(source)?
        };

        answer.sort(&self.order_indices);
        Ok(answer)
    }

    fn execute_non_aggregate(&self, source: Source) -> Result<Answer, SourceError> {
        let mut rows = Vec::new();
        for input_row in source {
            let input_row = input_row?;
            let mut row = Vec::new();
            for field in self.query.select.iter() {
                row.push(field.eval(&input_row));
            }
            rows.push(row);
        }

        Ok(Answer {
            columns: self.get_columns(),
            rows: rows,
        })
    }

    fn execute_aggregate(&self, source: Source) -> Result<Answer, SourceError> {
        let mut groups = HashMap::new();

        for row in source {
            let row = row?;
            let group = self.get_group(&row);
            let group_aggregates = groups
                .entry(group.clone())
                .or_insert_with(|| self.make_aggregates());

            for (call, aggregate) in group_aggregates.iter_mut() {
                aggregate.apply(call.argument.eval(&row));
            }
        }

        let mut rows = Vec::new();
        for (group, group_aggregates) in groups {
            let mut row = Row::new();
            for (call, aggregate) in group_aggregates {
                row.fields.insert(Expr::AggregateCall(call), aggregate.final_value());
            }
            for (index, val) in group.iter().enumerate() {
                row.fields.insert(self.query.group[index].clone(), val.clone());
            }

            let mut output_row = Vec::new();
            for field in self.query.select.iter() {
                output_row.push(field.eval(&row));
            }
            rows.push(output_row);
        }

        Ok(Answer {
            columns: self.get_columns(),
            rows: rows,
        })
    }

    fn get_columns(&self) -> Vec<String> {
        let mut columns = Vec::new();
        for field in self.query.select.iter() {
            columns.push(format!("{}", field));
        }
        columns
    }

    fn get_group(&self, row: &Row) -> Vec<Data> {
        let mut group = Vec::new();
        for field in self.query.group.iter() {
            group.push(field.eval(row));
        }
        group
    }

    fn make_aggregates(&self) -> HashMap<AggregateCall, Aggregate> {
        let mut aggregates = HashMap::new();
        for call in self.aggregate_calls.iter() {
            aggregates.insert(call.clone(), call.function.aggregate());
        }
        aggregates
    }
}

pub fn execute(query: Query, source: Source) -> Result<Answer, ExecuteError> {
    Executor::new(query)?.execute(source)
}

#[cfg(test)]
mod tests {
    use super::*;
    use aggregate::{AggregateCall, AggregateFunction};
    use data::Data;
    use expr::Expr;
    use row::make_rows;

    #[test]
    fn aggregate_query() {
        let source = make_rows(
            vec!["a"],
            vec![
                data_vec![1],
                data_vec![2],
                data_vec![3],
                data_vec![4],
                data_vec![5],
            ],
        );

        let call = AggregateCall{
            function: AggregateFunction::Sum,
            argument: Box::new(Expr::Column(String::from("a"))),
        };

        let query = Query {
            select: vec![Expr::AggregateCall(call)],
            from: String::new(),
            group: vec![],
            order: vec![],
        };

        let actual = execute(query, Box::new(source.into_iter())).unwrap();
        let expected = Answer {
            columns: vec![String::from(r#"sum("a")"#)],
            rows: vec![data_vec![15]],
        };

        assert_eq!(expected, actual);
    }

    #[test]
    fn non_aggregate_query() {
        let source = make_rows(
            vec!["a"],
            vec![
                data_vec![1],
                data_vec![2],
                data_vec![3],
                data_vec![4],
                data_vec![5],
            ],
        );

        let query = Query {
            select: vec![Expr::Column(String::from("a"))],
            from: String::new(),
            group: vec![],
            order: vec![],
        };

        let actual = execute(query, Box::new(source.clone().into_iter())).unwrap();
        let expected = Answer {
            columns: vec![String::from(r#""a""#)],
            rows: vec![
                data_vec![1],
                data_vec![2],
                data_vec![3],
                data_vec![4],
                data_vec![5],
            ],
        };

        assert_eq!(expected, actual);
    }

    #[test]
    fn invalid_order_clause() {
        let source = make_rows(
            vec!["a"],
            vec![],
        );

        let query = Query {
            select: vec![],
            from: String::new(),
            group: vec![],
            order: vec![Expr::Column(String::from("a"))],
        };
        let actual = execute(query, Box::new(source.clone().into_iter()));
        let expected = Err(ExecuteError::InvalidOrderClause(Expr::Column(String::from("a"))));
        assert_eq!(expected, actual);
    }
}
<commit_msg>Remove unnecessary clone<commit_after>use std::collections::HashMap;
use aggregate::{Aggregate, AggregateCall};
use answer::Answer;
use data::Data;
use expr::Expr;
use query::Query;
use row::Row;
use source::{Source, SourceError};

#[derive(Clone, Debug, PartialEq)]
pub enum ExecuteError {
    SourceError(String),
    InvalidOrderClause(Expr),
}

impl From<SourceError> for ExecuteError {
    fn from(e: SourceError) -> Self {
        ExecuteError::SourceError(e.description)
    }
}

struct Executor {
    query: Query,
    aggregate_calls: Vec<AggregateCall>,
    order_indices: Vec<usize>,
}

impl Executor {
    fn new(query: Query) -> Result<Self, ExecuteError> {
        let aggregates = query.select.iter()
            .filter_map(|expr| expr.get_aggregate_call())
            .collect();

        let mut order_indices = Vec::new();
        for expr in query.order.iter() {
            let position = query.select.iter().position(|select| select == expr);
            let index = match position {
                Some(i) => i,
                None => return Err(ExecuteError::InvalidOrderClause(expr.clone())),
            };
            order_indices.push(index);
        }

        Ok(Executor {
            query: query,
            aggregate_calls: aggregates,
            order_indices: order_indices,
        })
    }

    fn execute(&self, source: Source) -> Result<Answer, ExecuteError> {
        let mut answer = if self.aggregate_calls.is_empty() {
            self.execute_non_aggregate(source)?
        } else {
            self.execute_aggregate(source)?
        };

        answer.sort(&self.order_indices);
        Ok(answer)
    }

    fn execute_non_aggregate(&self, source: Source) -> Result<Answer, SourceError> {
        let mut rows = Vec::new();
        for input_row in source {
            let input_row = input_row?;
            let mut row = Vec::new();
            for field in self.query.select.iter() {
                row.push(field.eval(&input_row));
            }
            rows.push(row);
        }

        Ok(Answer {
            columns: self.get_columns(),
            rows: rows,
        })
    }

    fn execute_aggregate(&self, source: Source) -> Result<Answer, SourceError> {
        let mut groups = HashMap::new();

        for row in source {
            let row = row?;
            let group_aggregates = groups
                .entry(self.get_group(&row))
                .or_insert_with(|| self.make_aggregates());

            for (call, aggregate) in group_aggregates.iter_mut() {
                aggregate.apply(call.argument.eval(&row));
            }
        }

        let mut rows = Vec::new();
        for (group, group_aggregates) in groups {
            let mut row = Row::new();
            for (call, aggregate) in group_aggregates {
                row.fields.insert(Expr::AggregateCall(call), aggregate.final_value());
            }
            for (index, val) in group.iter().enumerate() {
                row.fields.insert(self.query.group[index].clone(), val.clone());
            }

            let mut output_row = Vec::new();
            for field in self.query.select.iter() {
                output_row.push(field.eval(&row));
            }
            rows.push(output_row);
        }

        Ok(Answer {
            columns: self.get_columns(),
            rows: rows,
        })
    }

    fn get_columns(&self) -> Vec<String> {
        let mut columns = Vec::new();
        for field in self.query.select.iter() {
            columns.push(format!("{}", field));
        }
        columns
    }

    fn get_group(&self, row: &Row) -> Vec<Data> {
        let mut group = Vec::new();
        for field in self.query.group.iter() {
            group.push(field.eval(row));
        }
        group
    }

    fn make_aggregates(&self) -> HashMap<AggregateCall, Aggregate> {
        let mut aggregates = HashMap::new();
        for call in self.aggregate_calls.iter() {
            aggregates.insert(call.clone(), call.function.aggregate());
        }
        aggregates
    }
}

pub fn execute(query: Query, source: Source) -> Result<Answer, ExecuteError> {
    Executor::new(query)?.execute(source)
}

#[cfg(test)]
mod tests {
    use super::*;
    use aggregate::{AggregateCall, AggregateFunction};
    use data::Data;
    use expr::Expr;
    use row::make_rows;

    #[test]
    fn aggregate_query() {
        let source = make_rows(
            vec!["a"],
            vec![
                data_vec![1],
                data_vec![2],
                data_vec![3],
                data_vec![4],
                data_vec![5],
            ],
        );

        let call = AggregateCall{
            function: AggregateFunction::Sum,
            argument: Box::new(Expr::Column(String::from("a"))),
        };

        let query = Query {
            select: vec![Expr::AggregateCall(call)],
            from: String::new(),
            group: vec![],
            order: vec![],
        };

        let actual = execute(query, Box::new(source.into_iter())).unwrap();
        let expected = Answer {
            columns: vec![String::from(r#"sum("a")"#)],
            rows: vec![data_vec![15]],
        };

        assert_eq!(expected, actual);
    }

    #[test]
    fn non_aggregate_query() {
        let source = make_rows(
            vec!["a"],
            vec![
                data_vec![1],
                data_vec![2],
                data_vec![3],
                data_vec![4],
                data_vec![5],
            ],
        );

        let query = Query {
            select: vec![Expr::Column(String::from("a"))],
            from: String::new(),
            group: vec![],
            order: vec![],
        };

        let actual = execute(query, Box::new(source.clone().into_iter())).unwrap();
        let expected = Answer {
            columns: vec![String::from(r#""a""#)],
            rows: vec![
                data_vec![1],
                data_vec![2],
                data_vec![3],
                data_vec![4],
                data_vec![5],
            ],
        };

        assert_eq!(expected, actual);
    }

    #[test]
    fn invalid_order_clause() {
        let source = make_rows(
            vec!["a"],
            vec![],
        );

        let query = Query {
            select: vec![],
            from: String::new(),
            group: vec![],
            order: vec![Expr::Column(String::from("a"))],
        };
        let actual = execute(query, Box::new(source.clone().into_iter()));
        let expected = Err(ExecuteError::InvalidOrderClause(Expr::Column(String::from("a"))));
        assert_eq!(expected, actual);
    }
}
<|endoftext|>