<commit_before>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const WHITELIST: &[&str] = &[
    "E0183",
    "E0227",
    "E0279",
    "E0280",
    "E0311",
    "E0313",
    "E0314",
    "E0315",
    "E0377",
    "E0456",
    "E0461",
    "E0462",
    "E0464",
    "E0465",
    "E0472",
    "E0473",
    "E0474",
    "E0475",
    "E0476",
    "E0479",
    "E0480",
    "E0481",
    "E0482",
    "E0483",
    "E0484",
    "E0485",
    "E0486",
    "E0487",
    "E0488",
    "E0489",
    "E0514",
    "E0519",
    "E0523",
    "E0554",
    "E0570",
    "E0640",
    "E0717",
    "E0727",
    "E0729",
];

fn check_error_code_explanation(
    f: &str,
    error_codes: &mut HashMap<String, bool>,
    err_code: String,
) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') {
            error_codes.insert(err_code, true);
            return;
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            error_codes.get_mut(&err_code).map(|x| *x = true);
            return;
        }
    }
}

macro_rules! some_or_continue {
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => continue,
        }
    );
}

fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>, path: &Path) {
    let mut reached_no_explanation = false;

    for line in f.lines() {
        let s = line.trim();
        if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
            if let Some(err_code) = s.splitn(2, ':').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) {
                    error_codes.insert(err_code.clone(), false);
                }
                // Now we extract the tests from the markdown file!
                let md = some_or_continue!(s.splitn(2, "include_str!(\"").skip(1).next());
                let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
                let path = some_or_continue!(path.parent()).join(md_file_name);
                match read_to_string(&path) {
                    Ok(content) => {
                        check_error_code_explanation(
                            &content,
                            error_codes,
                            err_code,
                        );
                    }
                    Err(e) => {
                        eprintln!("Couldn't read `{}`: {}", path.display(), e);
                    }
                }
            }
        } else if reached_no_explanation && s.starts_with('E') {
            if let Some(err_code) = s.splitn(2, ',').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) { // this check should *never* fail!
                    error_codes.insert(err_code, false);
                }
            }
        } else if s == ";" {
            reached_no_explanation = true;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            if let Some(err_code) = s.splitn(2, ']').next() {
                if let Some(err_code) = err_code.splitn(2, '[').skip(1).next() {
                    let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
                    *nb = true;
                }
            }
        }
    }
}

pub fn check(path: &Path, bad: &mut bool) {
    println!("Checking which error codes lack tests...");
    let mut error_codes: HashMap<String, bool> = HashMap::new();
    super::walk(path,
                &mut |path| super::filter_dirs(path),
                &mut |entry, contents| {
        let file_name = entry.file_name();
        if file_name == "error_codes.rs" {
            extract_error_codes(contents, &mut error_codes, entry.path());
        } else if entry.path().extension() == Some(OsStr::new("stderr")) {
            extract_error_codes_from_tests(contents, &mut error_codes);
        }
    });
    println!("Found {} error codes", error_codes.len());

    let mut errors = Vec::new();
    for (err_code, nb) in &error_codes {
        if !*nb && !WHITELIST.contains(&err_code.as_str()) {
            errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<commit_msg>Compiler internal error codes need no documentation<commit_after>//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.

use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;

// A few of those error codes can't be tested but all the others can and *should* be tested!
const WHITELIST: &[&str] = &[
    "E0183",
    "E0227",
    "E0279",
    "E0280",
    "E0311",
    "E0313",
    "E0314",
    "E0315",
    "E0377",
    "E0456",
    "E0461",
    "E0462",
    "E0464",
    "E0465",
    "E0472",
    "E0473",
    "E0474",
    "E0475",
    "E0476",
    "E0479",
    "E0480",
    "E0481",
    "E0482",
    "E0483",
    "E0484",
    "E0485",
    "E0486",
    "E0487",
    "E0488",
    "E0489",
    "E0514",
    "E0519",
    "E0523",
    "E0553",
    "E0554",
    "E0570",
    "E0629",
    "E0630",
    "E0640",
    "E0717",
    "E0727",
    "E0729",
];

fn check_error_code_explanation(
    f: &str,
    error_codes: &mut HashMap<String, bool>,
    err_code: String,
) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') {
            error_codes.insert(err_code, true);
            return;
        } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
            error_codes.get_mut(&err_code).map(|x| *x = true);
            return;
        }
    }
}

macro_rules! some_or_continue {
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => continue,
        }
    );
}

fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>, path: &Path) {
    let mut reached_no_explanation = false;

    for line in f.lines() {
        let s = line.trim();
        if !reached_no_explanation && s.starts_with('E') && s.contains("include_str!(\"") {
            if let Some(err_code) = s.splitn(2, ':').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) {
                    error_codes.insert(err_code.clone(), false);
                }
                // Now we extract the tests from the markdown file!
                let md = some_or_continue!(s.splitn(2, "include_str!(\"").skip(1).next());
                let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
                let path = some_or_continue!(path.parent()).join(md_file_name);
                match read_to_string(&path) {
                    Ok(content) => {
                        check_error_code_explanation(
                            &content,
                            error_codes,
                            err_code,
                        );
                    }
                    Err(e) => {
                        eprintln!("Couldn't read `{}`: {}", path.display(), e);
                    }
                }
            }
        } else if reached_no_explanation && s.starts_with('E') {
            if let Some(err_code) = s.splitn(2, ',').next() {
                let err_code = err_code.to_owned();
                if !error_codes.contains_key(&err_code) { // this check should *never* fail!
                    error_codes.insert(err_code, false);
                }
            }
        } else if s == ";" {
            reached_no_explanation = true;
        }
    }
}

fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
    for line in f.lines() {
        let s = line.trim();
        if s.starts_with("error[E") || s.starts_with("warning[E") {
            if let Some(err_code) = s.splitn(2, ']').next() {
                if let Some(err_code) = err_code.splitn(2, '[').skip(1).next() {
                    let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
                    *nb = true;
                }
            }
        }
    }
}

pub fn check(path: &Path, bad: &mut bool) {
    println!("Checking which error codes lack tests...");
    let mut error_codes: HashMap<String, bool> = HashMap::new();
    super::walk(path,
                &mut |path| super::filter_dirs(path),
                &mut |entry, contents| {
        let file_name = entry.file_name();
        if file_name == "error_codes.rs" {
            extract_error_codes(contents, &mut error_codes, entry.path());
        } else if entry.path().extension() == Some(OsStr::new("stderr")) {
            extract_error_codes_from_tests(contents, &mut error_codes);
        }
    });
    println!("Found {} error codes", error_codes.len());

    let mut errors = Vec::new();
    for (err_code, nb) in &error_codes {
        if !*nb && !WHITELIST.contains(&err_code.as_str()) {
            errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
        }
    }
    errors.sort();
    for err in &errors {
        eprintln!("{}", err);
    }
    println!("Found {} error codes with no tests", errors.len());
    if !errors.is_empty() {
        *bad = true;
    }
    println!("Done!");
}
<|endoftext|>


<commit_before>pub fn read_file_data(path: &PathBuf) -> Vec<u8> {
    let mut data = Vec::new();
    let mut f = File::open(path).ok().unwrap();
    f.read_to_end(&mut data).unwrap();
    data
}

const VALIDATOR_CONFIG: Option<ValidatingParserConfig> = Some(ValidatingParserConfig {
    operator_config: OperatorValidatorConfig {
        enable_threads: true,
        enable_reference_types: true,
        enable_simd: true,
        enable_bulk_memory: true,
    },
    mutable_global_imports: true,
});

#[macro_use]
extern crate criterion;
extern crate wasmparser;

use criterion::Criterion;
use wasmparser::{
    validate, OperatorValidatorConfig, Parser, ParserState, ValidatingParser,
    ValidatingParserConfig, WasmDecoder,
};

use std::fs::{read_dir, File};
use std::io::Read;
use std::path::PathBuf;

fn read_all_wasm<'a, T>(mut d: T)
where
    T: WasmDecoder<'a>,
{
    loop {
        match *d.read() {
            ParserState::Error(e) => panic!("unexpected error {:?}", e),
            ParserState::EndWasm => return,
            _ => (),
        }
    }
}

fn it_works_benchmark(c: &mut Criterion) {
    let mut data: Vec<Vec<u8>> = vec![];
    for entry in read_dir("tests").unwrap() {
        let dir = entry.unwrap();
        if !dir.file_type().unwrap().is_file() {
            continue;
        }
        data.push(read_file_data(&dir.path()));
    }
    c.bench_function("it works benchmark", move |b| {
        for d in &mut data {
            b.iter(|| read_all_wasm(Parser::new(d.as_slice())));
        }
    });
}

fn validator_not_fails_benchmark(c: &mut Criterion) {
    let mut data: Vec<Vec<u8>> = vec![];
    for entry in read_dir("tests").unwrap() {
        let dir = entry.unwrap();
        if !dir.file_type().unwrap().is_file() {
            continue;
        }
        data.push(read_file_data(&dir.path()));
    }
    c.bench_function("validator no fails benchmark", move |b| {
        for d in &mut data {
            b.iter(|| read_all_wasm(ValidatingParser::new(d.as_slice(), VALIDATOR_CONFIG)));
        }
    });
}

fn validate_benchmark(c: &mut Criterion) {
    let mut data: Vec<Vec<u8>> = vec![vec![]];
    for entry in read_dir("tests").unwrap() {
        let dir = entry.unwrap();
        if !dir.file_type().unwrap().is_file() {
            continue;
        }
        data.push(read_file_data(&dir.path()));
    }
    c.bench_function("validate benchmark", move |b| {
        for d in &mut data {
            b.iter(|| validate(&d, VALIDATOR_CONFIG));
        }
    });
}

criterion_group!(
    benchmark,
    it_works_benchmark,
    validator_not_fails_benchmark,
    validate_benchmark
);
criterion_main!(benchmark);
<commit_msg>fix error in benchmarks, added the required  field (#111)<commit_after>pub fn read_file_data(path: &PathBuf) -> Vec<u8> {
    let mut data = Vec::new();
    let mut f = File::open(path).ok().unwrap();
    f.read_to_end(&mut data).unwrap();
    data
}

const VALIDATOR_CONFIG: Option<ValidatingParserConfig> = Some(ValidatingParserConfig {
    operator_config: OperatorValidatorConfig {
        enable_threads: true,
        enable_reference_types: true,
        enable_simd: true,
        enable_bulk_memory: true,
        enable_multi_value: true,
    },
    mutable_global_imports: true,
});

#[macro_use]
extern crate criterion;
extern crate wasmparser;

use criterion::Criterion;
use wasmparser::{
    validate, OperatorValidatorConfig, Parser, ParserState, ValidatingParser,
    ValidatingParserConfig, WasmDecoder,
};

use std::fs::{read_dir, File};
use std::io::Read;
use std::path::PathBuf;

fn read_all_wasm<'a, T>(mut d: T)
where
    T: WasmDecoder<'a>,
{
    loop {
        match *d.read() {
            ParserState::Error(e) => panic!("unexpected error {:?}", e),
            ParserState::EndWasm => return,
            _ => (),
        }
    }
}

fn it_works_benchmark(c: &mut Criterion) {
    let mut data: Vec<Vec<u8>> = vec![];
    for entry in read_dir("tests").unwrap() {
        let dir = entry.unwrap();
        if !dir.file_type().unwrap().is_file() {
            continue;
        }
        data.push(read_file_data(&dir.path()));
    }
    c.bench_function("it works benchmark", move |b| {
        for d in &mut data {
            b.iter(|| read_all_wasm(Parser::new(d.as_slice())));
        }
    });
}

fn validator_not_fails_benchmark(c: &mut Criterion) {
    let mut data: Vec<Vec<u8>> = vec![];
    for entry in read_dir("tests").unwrap() {
        let dir = entry.unwrap();
        if !dir.file_type().unwrap().is_file() {
            continue;
        }
        data.push(read_file_data(&dir.path()));
    }
    c.bench_function("validator no fails benchmark", move |b| {
        for d in &mut data {
            b.iter(|| read_all_wasm(ValidatingParser::new(d.as_slice(), VALIDATOR_CONFIG)));
        }
    });
}

fn validate_benchmark(c: &mut Criterion) {
    let mut data: Vec<Vec<u8>> = vec![vec![]];
    for entry in read_dir("tests").unwrap() {
        let dir = entry.unwrap();
        if !dir.file_type().unwrap().is_file() {
            continue;
        }
        data.push(read_file_data(&dir.path()));
    }
    c.bench_function("validate benchmark", move |b| {
        for d in &mut data {
            b.iter(|| validate(&d, VALIDATOR_CONFIG));
        }
    });
}

criterion_group!(
    benchmark,
    it_works_benchmark,
    validator_not_fails_benchmark,
    validate_benchmark
);
criterion_main!(benchmark);
<|endoftext|>


<commit_before>use prolog::ast::*;
use prolog::machine::machine_state::MachineState;

use std::collections::HashSet;
use std::vec::Vec;

pub struct HeapCellPreOrderIterator<'a> {
    machine_st: &'a MachineState,
    state_stack: Vec<Addr>
}

impl<'a> HeapCellPreOrderIterator<'a> {
    pub fn new(machine_st: &'a MachineState, a: Addr) -> Self
    {
        HeapCellPreOrderIterator {
            machine_st, state_stack: vec![a]
        }
    }

    fn follow_heap(&mut self, h: usize) -> Addr
    {
        match &self.machine_st.heap[h] {
            &HeapCellValue::NamedStr(arity, _, _) => {
                for idx in (1 .. arity + 1).rev() {
                    self.state_stack.push(Addr::HeapCell(h + idx));
                }

                Addr::HeapCell(h)
            },
            &HeapCellValue::Addr(ref a) =>
                self.follow(a.clone())
        }
    }

    // called under the assumption that the location at r is about to
    // be visited, and so any follow up states need to be added to
    // state_stack. returns the dereferenced Addr from Ref.
    fn follow(&mut self, addr: Addr) -> Addr
    {
        let da = self.machine_st.store(self.machine_st.deref(addr));

        match &da {
            &Addr::Con(_) => da,
            &Addr::Lis(a) => {
                self.state_stack.push(Addr::HeapCell(a + 1));
                self.state_stack.push(Addr::HeapCell(a));

                da
            },
            &Addr::HeapCell(_) | &Addr::StackCell(_, _) =>
                da,
            &Addr::Str(s) =>
                self.follow_heap(s) // record terms of structure.
        }
    }
}

impl<'a> Iterator for HeapCellPreOrderIterator<'a> {
    type Item = HeapCellValue;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(a) = self.state_stack.pop() {
            match self.follow(a) {
                Addr::HeapCell(h) => Some(self.machine_st.heap[h].clone()),
                Addr::StackCell(fr, sc) => {
                    let heap_val = HeapCellValue::Addr(self.machine_st.and_stack[fr][sc].clone());
                    Some(heap_val)
                },
                da => Some(HeapCellValue::Addr(da))
            }
        } else {
            None
        }
    }
}

pub struct HeapCellPostOrderIterator<'a> {
    pre_iter:     HeapCellPreOrderIterator<'a>,
    parent_stack: Vec<(usize, HeapCellValue)> // number of children, parent node.
}

impl<'a> HeapCellPostOrderIterator<'a> {
    pub fn new(pre_iter: HeapCellPreOrderIterator<'a>) -> Self {
        HeapCellPostOrderIterator {
            pre_iter,
            parent_stack: vec![]
        }
    }
}

impl<'a> Iterator for HeapCellPostOrderIterator<'a> {
    type Item = HeapCellValue;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some((child_count, node)) = self.parent_stack.pop() {
                if child_count == 0 {
                    return Some(node);
                }

                self.parent_stack.push((child_count - 1, node));
            }

            if let Some(item) = self.pre_iter.next() {
                match item {
                    HeapCellValue::NamedStr(arity, name, fix) =>
                        self.parent_stack.push((arity, HeapCellValue::NamedStr(arity, name, fix))),
                    HeapCellValue::Addr(Addr::Lis(a)) =>
                        self.parent_stack.push((2, HeapCellValue::Addr(Addr::Lis(a)))),
                    child_node => {
                        return Some(child_node);
                    }
                }
            } else {
                return None;
            }
        }
    }
}

impl MachineState {
    pub fn post_order_iter<'a>(&'a self, a: Addr) -> HeapCellPostOrderIterator<'a> {
        HeapCellPostOrderIterator::new(HeapCellPreOrderIterator::new(self, a))
    }

    pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr) -> HeapCellAcyclicIterator<HeapCellPreOrderIterator<'a>>
    {
        HeapCellAcyclicIterator::new(HeapCellPreOrderIterator::new(self, a))
    }
}

pub trait MutStackHeapCellIterator {
    fn stack(&mut self) -> &mut Vec<Addr>;
}

impl<'a> MutStackHeapCellIterator for HeapCellPreOrderIterator<'a> {
    fn stack(&mut self) -> &mut Vec<Addr> {
        &mut self.state_stack
    }
}

pub struct HeapCellAcyclicIterator<HeapCellIter> {
    iter: HeapCellIter,
    seen: HashSet<Addr>
}

impl<HeapCellIter: MutStackHeapCellIterator> HeapCellAcyclicIterator<HeapCellIter>
{
    pub fn new(mut iter: HeapCellIter) -> Self {
        let mut seen = HashSet::new();

        if let Some(addr) = iter.stack().last() {
            seen.insert(addr.clone());
        }

        HeapCellAcyclicIterator { iter, seen }
    }
}

impl<HeapCellIter> Iterator for HeapCellAcyclicIterator<HeapCellIter>
    where HeapCellIter: Iterator<Item=HeapCellValue>
                      + MutStackHeapCellIterator
{
    type Item = HeapCellValue;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(hcv) = self.iter.next() {
            if let Some(addr) = self.iter.stack().pop() {
                if self.seen.contains(&addr) {
                    continue;
                } else {
                    self.iter.stack().push(addr.clone());
                    self.seen.insert(addr);
                }
            }

            return Some(hcv);
        }

        None
    }
}
<commit_msg>correct acyclic iteration.<commit_after>use prolog::ast::*;
use prolog::machine::machine_state::MachineState;

use std::collections::HashSet;
use std::vec::Vec;

pub struct HeapCellPreOrderIterator<'a> {
    machine_st: &'a MachineState,
    state_stack: Vec<Addr>
}

impl<'a> HeapCellPreOrderIterator<'a> {
    pub fn new(machine_st: &'a MachineState, a: Addr) -> Self
    {
        HeapCellPreOrderIterator {
            machine_st, state_stack: vec![a]
        }
    }

    fn follow_heap(&mut self, h: usize) -> Addr
    {
        match &self.machine_st.heap[h] {
            &HeapCellValue::NamedStr(arity, _, _) => {
                for idx in (1 .. arity + 1).rev() {
                    self.state_stack.push(Addr::HeapCell(h + idx));
                }

                Addr::HeapCell(h)
            },
            &HeapCellValue::Addr(ref a) =>
                self.follow(a.clone())
        }
    }

    // called under the assumption that the location at r is about to
    // be visited, and so any follow up states need to be added to
    // state_stack. returns the dereferenced Addr from Ref.
    fn follow(&mut self, addr: Addr) -> Addr
    {
        let da = self.machine_st.store(self.machine_st.deref(addr));

        match &da {
            &Addr::Con(_) => da,
            &Addr::Lis(a) => {
                self.state_stack.push(Addr::HeapCell(a + 1));
                self.state_stack.push(Addr::HeapCell(a));

                da
            },
            &Addr::HeapCell(_) | &Addr::StackCell(_, _) =>
                da,
            &Addr::Str(s) =>
                self.follow_heap(s) // record terms of structure.
        }
    }
}

impl<'a> Iterator for HeapCellPreOrderIterator<'a> {
    type Item = HeapCellValue;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(a) = self.state_stack.pop() {
            match self.follow(a) {
                Addr::HeapCell(h) => Some(self.machine_st.heap[h].clone()),
                Addr::StackCell(fr, sc) => {
                    let heap_val = HeapCellValue::Addr(self.machine_st.and_stack[fr][sc].clone());
                    Some(heap_val)
                },
                da => Some(HeapCellValue::Addr(da))
            }
        } else {
            None
        }
    }
}

pub struct HeapCellPostOrderIterator<'a> {
    pre_iter:     HeapCellPreOrderIterator<'a>,
    parent_stack: Vec<(usize, HeapCellValue)> // number of children, parent node.
}

impl<'a> HeapCellPostOrderIterator<'a> {
    pub fn new(pre_iter: HeapCellPreOrderIterator<'a>) -> Self {
        HeapCellPostOrderIterator {
            pre_iter,
            parent_stack: vec![]
        }
    }
}

impl<'a> Iterator for HeapCellPostOrderIterator<'a> {
    type Item = HeapCellValue;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some((child_count, node)) = self.parent_stack.pop() {
                if child_count == 0 {
                    return Some(node);
                }

                self.parent_stack.push((child_count - 1, node));
            }

            if let Some(item) = self.pre_iter.next() {
                match item {
                    HeapCellValue::NamedStr(arity, name, fix) =>
                        self.parent_stack.push((arity, HeapCellValue::NamedStr(arity, name, fix))),
                    HeapCellValue::Addr(Addr::Lis(a)) =>
                        self.parent_stack.push((2, HeapCellValue::Addr(Addr::Lis(a)))),
                    child_node => {
                        return Some(child_node);
                    }
                }
            } else {
                return None;
            }
        }
    }
}

impl MachineState {
    pub fn post_order_iter<'a>(&'a self, a: Addr) -> HeapCellPostOrderIterator<'a> {
        HeapCellPostOrderIterator::new(HeapCellPreOrderIterator::new(self, a))
    }

    pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr) -> HeapCellAcyclicIterator<HeapCellPreOrderIterator<'a>>
    {
        HeapCellAcyclicIterator::new(HeapCellPreOrderIterator::new(self, a))
    }
}

pub trait MutStackHeapCellIterator {
    fn stack(&mut self) -> &mut Vec<Addr>;
}

impl<'a> MutStackHeapCellIterator for HeapCellPreOrderIterator<'a> {
    fn stack(&mut self) -> &mut Vec<Addr> {
        &mut self.state_stack
    }
}

pub struct HeapCellAcyclicIterator<HeapCellIter> {
    iter: HeapCellIter,
    seen: HashSet<Addr>
}

impl<HeapCellIter: MutStackHeapCellIterator> HeapCellAcyclicIterator<HeapCellIter>
{
    pub fn new(iter: HeapCellIter) -> Self {
        HeapCellAcyclicIterator { iter, seen: HashSet::new() }
    }
}

impl<HeapCellIter> Iterator for HeapCellAcyclicIterator<HeapCellIter>
    where HeapCellIter: Iterator<Item=HeapCellValue>
                      + MutStackHeapCellIterator
{
    type Item = HeapCellValue;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(addr) = self.iter.stack().pop() {
            if self.seen.contains(&addr) {
                continue;
            } else {
                self.iter.stack().push(addr.clone());
                self.seen.insert(addr);
                break;
            }
        }

        self.iter.next()
    }
}
<|endoftext|>


<commit_before>use math::*;

pub type MeshIndex = u32;

/// The raw data representing a mesh in memory.
///
/// Meshes are represented as list of vertex positions and a list of faces.
/// Each face is represented as 3 indices into the vertex array.
#[derive(Debug)]
pub struct Mesh {
    vertex_data: Vec<f32>,
    indices:     Vec<MeshIndex>,

    position: VertexAttribute,
    normal:   Option<VertexAttribute>,
    texcoord: Vec<VertexAttribute>,
}

impl Mesh {
    pub fn vertex_data(&self) -> &[f32] {
        &*self.vertex_data
    }

    pub fn indices(&self) -> &[MeshIndex] {
        &*self.indices
    }

    pub fn position(&self) -> VertexAttribute {
        self.position
    }

    pub fn normal(&self) -> Option<VertexAttribute> {
        self.normal
    }

    pub fn texcoord(&self) -> &[VertexAttribute] {
        &*self.texcoord
    }
}

/// Represents a single vertex in a mesh with all of its supported attributes.
#[derive(Debug, Clone)]
pub struct Vertex {
    pub position: Point,
    pub normal: Option<Vector3>,

    /// Support an arbitrary number of texture units. The actual maximum is dependent on hardware
    /// and so is not limited by polygon directly. If the number of
    pub texcoord: Vec<Vector2>,
}

impl Vertex {
    pub fn new(position: Point) -> Vertex {
        Vertex {
            position: position,
            normal: None,
            texcoord: Vec::new(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct VertexAttribute {
    pub offset: usize,
    pub stride: usize,
}

#[derive(Debug, Clone, Copy)]
pub enum BuildMeshError {
    IndexOutOfBounds {
        vertex_count: MeshIndex,
        index: MeshIndex,
    },

    /// Indicates that one or more attributes had a count that did not match the total number of
    /// vertices.
    IncorrectAttributeCount,
}

#[derive(Debug, Clone)]
pub struct MeshBuilder {
    position_data: Vec<Point>,
    normal_data: Vec<Vector3>,
    texcoord_data: Vec<Vec<Vector2>>,

    indices:  Vec<u32>,
}

/// Provides a safe interface for building a mesh from raw vertex data.
///
/// `MeshBuilder` supports two methods for specifying mesh data: Providing a series of `Vertex`
/// objects, or directly setting the data for each vertex attribute at once. Once all vertex data
/// has been set calling `build()` will validate the mesh data and produce a `Mesh` object.
///
/// The mesh builder performs validity tests when building the mesh and mesh compilation can fail
/// if the mesh data is invalid in some way. The following validity tests are performed:
///
/// - Check for different data count for different attributes (e.g. if the position attribute data
///   for a different number of elements than the normal attribute).
/// - Any of the indicies would be out of bounds for the given vertex data.
impl MeshBuilder {
    pub fn new() -> MeshBuilder {
        MeshBuilder {
            position_data: Vec::new(),
            normal_data:   Vec::new(),
            texcoord_data: Vec::new(),
            indices:       Vec::new(),
        }
    }

    pub fn add_vertex(&mut self, vertex: Vertex) {
        self.position_data.push(vertex.position);

        if let Some(normal) = vertex.normal {
            self.normal_data.push(normal);
        }

        // Ensure there is a list for each texcoord.
        while self.texcoord_data.len() < vertex.texcoord.len() {
            self.texcoord_data.push(Vec::new());
        }

        // Add each texcoord to its corresponding list.
        let iter = vertex.texcoord.iter().zip(self.texcoord_data.iter_mut());
        for (texcoord, texcoord_data) in iter {
            texcoord_data.push(*texcoord);
        }
    }

    pub fn add_index(mut self, index: MeshIndex) -> MeshBuilder {
        self.indices.push(index);
        self
    }

    pub fn set_position_data(mut self, position_data: &[Point]) -> MeshBuilder {
        self.position_data.clear();
        self.position_data.extend(position_data);
        self
    }

    pub fn set_normal_data(mut self, normal_data: &[Vector3]) -> MeshBuilder {
        self.normal_data.clear();
        self.normal_data.extend(normal_data);
        self
    }

    pub fn set_texcoord_data(mut self, texcoord_data: &[&[Vector2]]) -> MeshBuilder {
        if texcoord_data.len() == 0 {
            return self;
        }

        for (src, dest) in texcoord_data.iter().zip(self.texcoord_data.iter_mut()) {
            dest.clear();
            dest.extend(*src);
        }

        self
    }

    pub fn set_indices(mut self, indices: &[u32]) -> MeshBuilder {
        self.indices.clear();
        self.indices.extend(indices);
        self
    }

    pub fn build(self) -> Result<Mesh, BuildMeshError> {
        // The vertex count is defined by the position data, since position is the only required
        // vertex attribute.
        let vertex_count = self.position_data.len();

        // Check that there is enough attribute data for each one.
        if self.normal_data.len() != 0 && self.normal_data.len() != vertex_count {
            return Err(BuildMeshError::IncorrectAttributeCount);
        }

        for texcoord_set in &self.texcoord_data {
            if texcoord_set.len() != vertex_count {
                return Err(BuildMeshError::IncorrectAttributeCount);
            }
        }

        // Make sure all indices at least point to a valid vertex.
        for index in self.indices.iter().cloned() {
            if index >= vertex_count as MeshIndex {
                return Err(BuildMeshError::IndexOutOfBounds {
                    vertex_count: vertex_count as MeshIndex,
                    index: index,
                });
            }
        }

        // TODO: Check for degenerate triangles? Actually, should that be a failure or a warning?

        let float_count = {
            let mut float_count =
                self.position_data.len() * 4
              + self.normal_data.len() * 3;
            for texcoord_set in &self.texcoord_data {
                float_count += texcoord_set.len() * 2;
            }

            float_count
        };

        // Create the mesh.
        let mut vertex_data = Vec::<f32>::with_capacity(float_count);

        // Setup position data.
        let position_attrib = VertexAttribute {
            offset: 0,
            stride: 4,
        };
        vertex_data.extend(Point::as_ref(&*self.position_data));

        // Setup normal data.
        let normal_attrib = if self.normal_data.len() > 0 {
            let attrib = VertexAttribute {
                offset: vertex_data.len(),
                stride: 3,
            };
            vertex_data.extend(Vector3::as_ref(&*self.normal_data));

            Some(attrib)
        } else {
            None
        };

        // Setup texcoord data.
        let mut texcoord_attribs = Vec::new();
        for texcoord_set in &self.texcoord_data {
            texcoord_attribs.push(VertexAttribute {
                offset: vertex_data.len(),
                stride: 2,
            });
            vertex_data.extend(Vector2::as_ref(&*texcoord_set));
        }

        // By our powers combined! We are! A mesh.
        Ok(Mesh {
            vertex_data: vertex_data,
            indices: self.indices,

            position: position_attrib,
            normal: normal_attrib,
            texcoord: texcoord_attribs,
        })
    }
}
<commit_msg>fix(polygon::Mesh): derive Clone impl for Mesh<commit_after>use math::*;

pub type MeshIndex = u32;

/// The raw data representing a mesh in memory.
///
/// Meshes are represented as list of vertex positions and a list of faces.
/// Each face is represented as 3 indices into the vertex array.
#[derive(Debug, Clone)]
pub struct Mesh {
    vertex_data: Vec<f32>,
    indices:     Vec<MeshIndex>,

    position: VertexAttribute,
    normal:   Option<VertexAttribute>,
    texcoord: Vec<VertexAttribute>,
}

impl Mesh {
    pub fn vertex_data(&self) -> &[f32] {
        &*self.vertex_data
    }

    pub fn indices(&self) -> &[MeshIndex] {
        &*self.indices
    }

    pub fn position(&self) -> VertexAttribute {
        self.position
    }

    pub fn normal(&self) -> Option<VertexAttribute> {
        self.normal
    }

    pub fn texcoord(&self) -> &[VertexAttribute] {
        &*self.texcoord
    }
}

/// Represents a single vertex in a mesh with all of its supported attributes.
#[derive(Debug, Clone)]
pub struct Vertex {
    pub position: Point,
    pub normal: Option<Vector3>,

    /// Support an arbitrary number of texture units. The actual maximum is dependent on hardware
    /// and so is not limited by polygon directly. If the number of
    pub texcoord: Vec<Vector2>,
}

impl Vertex {
    pub fn new(position: Point) -> Vertex {
        Vertex {
            position: position,
            normal: None,
            texcoord: Vec::new(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct VertexAttribute {
    pub offset: usize,
    pub stride: usize,
}

#[derive(Debug, Clone, Copy)]
pub enum BuildMeshError {
    IndexOutOfBounds {
        vertex_count: MeshIndex,
        index: MeshIndex,
    },

    /// Indicates that one or more attributes had a count that did not match the total number of
    /// vertices.
    IncorrectAttributeCount,
}

#[derive(Debug, Clone)]
pub struct MeshBuilder {
    position_data: Vec<Point>,
    normal_data: Vec<Vector3>,
    texcoord_data: Vec<Vec<Vector2>>,

    indices:  Vec<u32>,
}

/// Provides a safe interface for building a mesh from raw vertex data.
///
/// `MeshBuilder` supports two methods for specifying mesh data: Providing a series of `Vertex`
/// objects, or directly setting the data for each vertex attribute at once. Once all vertex data
/// has been set calling `build()` will validate the mesh data and produce a `Mesh` object.
///
/// The mesh builder performs validity tests when building the mesh and mesh compilation can fail
/// if the mesh data is invalid in some way. The following validity tests are performed:
///
/// - Check for different data count for different attributes (e.g. if the position attribute data
///   for a different number of elements than the normal attribute).
/// - Any of the indicies would be out of bounds for the given vertex data.
impl MeshBuilder {
    pub fn new() -> MeshBuilder {
        MeshBuilder {
            position_data: Vec::new(),
            normal_data:   Vec::new(),
            texcoord_data: Vec::new(),
            indices:       Vec::new(),
        }
    }

    pub fn add_vertex(&mut self, vertex: Vertex) {
        self.position_data.push(vertex.position);

        if let Some(normal) = vertex.normal {
            self.normal_data.push(normal);
        }

        // Ensure there is a list for each texcoord.
        while self.texcoord_data.len() < vertex.texcoord.len() {
            self.texcoord_data.push(Vec::new());
        }

        // Add each texcoord to its corresponding list.
        let iter = vertex.texcoord.iter().zip(self.texcoord_data.iter_mut());
        for (texcoord, texcoord_data) in iter {
            texcoord_data.push(*texcoord);
        }
    }

    pub fn add_index(mut self, index: MeshIndex) -> MeshBuilder {
        self.indices.push(index);
        self
    }

    pub fn set_position_data(mut self, position_data: &[Point]) -> MeshBuilder {
        self.position_data.clear();
        self.position_data.extend(position_data);
        self
    }

    pub fn set_normal_data(mut self, normal_data: &[Vector3]) -> MeshBuilder {
        self.normal_data.clear();
        self.normal_data.extend(normal_data);
        self
    }

    pub fn set_texcoord_data(mut self, texcoord_data: &[&[Vector2]]) -> MeshBuilder {
        if texcoord_data.len() == 0 {
            return self;
        }

        for (src, dest) in texcoord_data.iter().zip(self.texcoord_data.iter_mut()) {
            dest.clear();
            dest.extend(*src);
        }

        self
    }

    pub fn set_indices(mut self, indices: &[u32]) -> MeshBuilder {
        self.indices.clear();
        self.indices.extend(indices);
        self
    }

    pub fn build(self) -> Result<Mesh, BuildMeshError> {
        // The vertex count is defined by the position data, since position is the only required
        // vertex attribute.
        let vertex_count = self.position_data.len();

        // Check that there is enough attribute data for each one.
        if self.normal_data.len() != 0 && self.normal_data.len() != vertex_count {
            return Err(BuildMeshError::IncorrectAttributeCount);
        }

        for texcoord_set in &self.texcoord_data {
            if texcoord_set.len() != vertex_count {
                return Err(BuildMeshError::IncorrectAttributeCount);
            }
        }

        // Make sure all indices at least point to a valid vertex.
        for index in self.indices.iter().cloned() {
            if index >= vertex_count as MeshIndex {
                return Err(BuildMeshError::IndexOutOfBounds {
                    vertex_count: vertex_count as MeshIndex,
                    index: index,
                });
            }
        }

        // TODO: Check for degenerate triangles? Actually, should that be a failure or a warning?

        let float_count = {
            let mut float_count =
                self.position_data.len() * 4
              + self.normal_data.len() * 3;
            for texcoord_set in &self.texcoord_data {
                float_count += texcoord_set.len() * 2;
            }

            float_count
        };

        // Create the mesh.
        let mut vertex_data = Vec::<f32>::with_capacity(float_count);

        // Setup position data.
        let position_attrib = VertexAttribute {
            offset: 0,
            stride: 4,
        };
        vertex_data.extend(Point::as_ref(&*self.position_data));

        // Setup normal data.
        let normal_attrib = if self.normal_data.len() > 0 {
            let attrib = VertexAttribute {
                offset: vertex_data.len(),
                stride: 3,
            };
            vertex_data.extend(Vector3::as_ref(&*self.normal_data));

            Some(attrib)
        } else {
            None
        };

        // Setup texcoord data.
        let mut texcoord_attribs = Vec::new();
        for texcoord_set in &self.texcoord_data {
            texcoord_attribs.push(VertexAttribute {
                offset: vertex_data.len(),
                stride: 2,
            });
            vertex_data.extend(Vector2::as_ref(&*texcoord_set));
        }

        // By our powers combined! We are! A mesh.
        Ok(Mesh {
            vertex_data: vertex_data,
            indices: self.indices,

            position: position_attrib,
            normal: normal_attrib,
            texcoord: texcoord_attribs,
        })
    }
}
<|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 crate::LigError;
use gaze::steps::{take_string, take_while_str, NoMatch};
use gaze::Gaze;
use ligature::{validate_identifier_characters, Attribute, Bytes, Entity, Statement, Value};

#[derive(Debug, Clone, Copy)]
pub enum LigToken {
    OpenAngle,
    CloseAngle,
    AtSign,
    Whitespace,
    NewLine,
    DoubleQuote,
    Dot,
    Identifier,
    Integer,
}

/// Reads an Entity from the given &str.
/// Will return an error if there is anything other than an Entity + whitespace in the input.
pub fn read_entity(input: &str) -> Result<Entity, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&entity_step)
        .map_err(|_| LigError("Could not read Entity.".into()))
}

pub fn read_attribute(input: &str) -> Result<Attribute, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&attribute_step)
        .map_err(|_| LigError("Could not read Attribute.".into()))
}

/// Reads a value from a passed str.
/// Ignores white space but will return an Err if there is any input besides an encoded value.
pub fn read_value(input: &str) -> Result<Value, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&value_step)
        .map_err(|_| LigError("Could not read Value.".into()))
}

pub fn read(input: &str) -> Result<Vec<Statement>, LigError> {
    todo!()
}

fn entity_step(gaze: &mut Gaze<&str>) -> Result<Entity, NoMatch> {
    println!("a");
    gaze.attempt(&take_string("<"))?; //.map_err(|_| LigError("Could not read Entity.".into()))?;
    println!("b");
    let res = gaze.attempt(&take_while_str(&|c: &str| {
        validate_identifier_characters(c)
    }))?;
    println!("c");
    gaze.attempt(&take_string(">"))?; //.map_err(|_| LigError("Could not read Entity.".into()))?;
    println!("d");
    Ok(Entity::new(&res).map_err(|_| NoMatch)?)
}

fn attribute_step(gaze: &mut Gaze<&str>) -> Result<Attribute, NoMatch> {
    gaze.attempt(&take_string("@<"))?; //.map_err(|_| LigError("Could not read Attribute.".into()))?;
    let res = gaze.attempt(&take_while_str(&|c: &str| {
        validate_identifier_characters(c)
    }))?;
    gaze.attempt(&take_string(">"))?; //.map_err(|_| LigError("Could not read Attribute.".into()))?;
    Ok(Attribute::new(&res).map_err(|_| NoMatch)?)
}

fn value_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    let next_char = gaze.peek();
    match next_char {
        None => Err(NoMatch), //Err(LigError("Could not match Value".into())),
        Some(c) => {
            if c == "<" {
                Ok(Value::Entity(gaze.attempt(&entity_step)?))
            } else if is_digit(&c) {
                gaze.attempt(&number_step)
            } else if c == "\"" {
                gaze.attempt(&string_step)
            } else {
                Err(NoMatch)//Err(LigError("Could not match Value".into()))
            }
        }
    }
}

fn is_digit(s: &str) -> bool {
    s == "0"
        || s == "1"
        || s == "2"
        || s == "3"
        || s == "4"
        || s == "5"
        || s == "6"
        || s == "7"
        || s == "8"
        || s == "9"
}

fn number_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    let is_hex = gaze.attempt(&take_string("0x"));
    match is_hex {
        Ok(_) => {
            return gaze.attempt(&hex_step);
        },
        Err(_) => {
            let integer = gaze.attempt(&integer_step)?;
            let is_float = gaze.attempt(&take_string("."));
            match is_float {
                Ok(_) => todo!(),
                Err(_) => todo!(),
            }
            //if not read integer
            //then check for decimal point
            //handle float if decimal point exists
        },
    }
}

fn hex_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    todo!()
}

fn integer_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    todo!()
}

fn bytes_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    todo!()
}

fn string_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    todo!()
}

fn statement_step(gaze: &mut Gaze<&str>) -> Result<Statement, NoMatch> {
    todo!()
}
<commit_msg>preparing for 0.1.0<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 crate::LigError;
use gaze::steps::{take_string, take_while_str, NoMatch};
use gaze::Gaze;
use ligature::{validate_identifier_characters, Attribute, Entity, Statement, Value};

#[derive(Debug, Clone, Copy)]
pub enum LigToken {
    OpenAngle,
    CloseAngle,
    AtSign,
    Whitespace,
    NewLine,
    DoubleQuote,
    Dot,
    Identifier,
    Integer,
}

/// Reads an Entity from the given &str.
/// Will return an error if there is anything other than an Entity + whitespace in the input.
pub fn read_entity(input: &str) -> Result<Entity, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&entity_step)
        .map_err(|_| LigError("Could not read Entity.".into()))
}

pub fn read_attribute(input: &str) -> Result<Attribute, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&attribute_step)
        .map_err(|_| LigError("Could not read Attribute.".into()))
}

/// Reads a value from a passed str.
/// Ignores white space but will return an Err if there is any input besides an encoded value.
pub fn read_value(input: &str) -> Result<Value, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&value_step)
        .map_err(|_| LigError("Could not read Value.".into()))
}

pub fn read(input: &str) -> Result<Vec<Statement>, LigError> {
    todo!()
}

fn entity_step(gaze: &mut Gaze<&str>) -> Result<Entity, NoMatch> {
    println!("a");
    gaze.attempt(&take_string("<"))?; //.map_err(|_| LigError("Could not read Entity.".into()))?;
    println!("b");
    let res = gaze.attempt(&take_while_str(&|c: &str| {
        validate_identifier_characters(c)
    }))?;
    println!("c");
    gaze.attempt(&take_string(">"))?; //.map_err(|_| LigError("Could not read Entity.".into()))?;
    println!("d");
    Ok(Entity::new(&res).map_err(|_| NoMatch)?)
}

fn attribute_step(gaze: &mut Gaze<&str>) -> Result<Attribute, NoMatch> {
    gaze.attempt(&take_string("@<"))?; //.map_err(|_| LigError("Could not read Attribute.".into()))?;
    let res = gaze.attempt(&take_while_str(&|c: &str| {
        validate_identifier_characters(c)
    }))?;
    gaze.attempt(&take_string(">"))?; //.map_err(|_| LigError("Could not read Attribute.".into()))?;
    Ok(Attribute::new(&res).map_err(|_| NoMatch)?)
}

fn value_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    let next_char = gaze.peek();
    match next_char {
        None => Err(NoMatch), //Err(LigError("Could not match Value".into())),
        Some(c) => {
            if c == "<" {
                Ok(Value::Entity(gaze.attempt(&entity_step)?))
            } else if is_digit(&c) {
                gaze.attempt(&number_step)
            } else if c == "\"" {
                gaze.attempt(&string_step)
            } else {
                Err(NoMatch)//Err(LigError("Could not match Value".into()))
            }
        }
    }
}

fn is_digit(s: &str) -> bool {
    s == "0"
        || s == "1"
        || s == "2"
        || s == "3"
        || s == "4"
        || s == "5"
        || s == "6"
        || s == "7"
        || s == "8"
        || s == "9"
}

fn number_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    let is_hex = gaze.attempt(&take_string("0x"));
    match is_hex {
        Ok(_) => {
            return gaze.attempt(&hex_step);
        },
        Err(_) => {
            let integer = gaze.attempt(&integer_step)?;
            let is_float = gaze.attempt(&take_string("."));
            match is_float {
                Ok(_) => todo!(),
                Err(_) => todo!(),
            }
            //if not read integer
            //then check for decimal point
            //handle float if decimal point exists
        },
    }
}

fn hex_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    todo!()
}

fn integer_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    todo!()
}

fn bytes_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    todo!()
}

fn string_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    todo!()
}

fn statement_step(gaze: &mut Gaze<&str>) -> Result<Statement, NoMatch> {
    todo!()
}
<|endoftext|>


<commit_before>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;
extern crate github;
extern crate url;

use self::url::Url;
use self::github::notification::PushNotification;
use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub struct PendingBuild {
    pub clone_url: Url,
    pub branch: String
}

/// Type A is some key
pub trait Database : Sync + Send {
    fn add_pending(&self, entry: PushNotification);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<PendingBuild>;

    fn add_test_results(&self, entry: &PendingBuild, results: BuildResult);
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate time;
    extern crate pg_typeprovider;
    extern crate github;

    use super::PendingBuild;

    use self::github::notification::PushNotification;

    use self::time::Timespec;
    use self::pg_typeprovider::util::Joinable;

    use std::sync::Mutex;

    use super::postgres::{Connection, GenericConnection, SslMode, ToSql};

    use builder::BuildResult;
    use super::EntryStatus::{Pending, InProgress, Done};
    use super::Database;

    pg_table!(builds)

    pub struct PostgresDatabase {
        db: Mutex<Connection>
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(loc, &SslMode::None).ok().map(|db| {
                PostgresDatabase {
                    db: Mutex::new(db)
                }
            })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    db.with_connection(|conn| {
                        conn.execute(
                            "DELETE FROM users", &[]).unwrap();
                        conn.execute(
                            "DELETE FROM builds", &[]).unwrap();
                    })
                },
                None => ()
            };
            retval
        }

        pub fn with_connection<A>(&self, f: |&Connection| -> A) -> A {
            f(&*self.db.lock())
        }
    }

    fn get_one_build(conn: &Connection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &Connection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    impl Database for PostgresDatabase {
        fn add_pending(&self, entry: PushNotification) {
            // self.with_connection(|conn| {
            //     let trans = conn.transaction().unwrap();

            // self.with_connection(|conn| entry.insert(conn));
        }

        fn get_pending(&self) -> Option<PendingBuild> {
            None
            // self.with_connection(|conn| {
            //     loop {
            //         match get_one_build(conn) {
            //             Some(b) => {
            //                 if try_lock_build(conn, &b) {
            //                     return Some(b);
            //                 }
            //             },
            //             None => { return None; }
            //         }
            //     }
            // })
        }

        fn add_test_results(&self, entry: &PendingBuild, results: BuildResult) {
            // TODO: we do a copy here because we cannot move into a closure,
            // even though this is what we want.  I spent around 2 hours trying
            // to get around this, but to no avail.

            // let res = results.consume_to_json().to_string();
            // let s = res.as_slice();
            // self.with_connection(|conn| {
            //     let num_updated =
            //         BuildUpdate::new()
            //         .status_to((&Done).to_int())
            //         .results_to(s.to_string())
            //         .where_id(entry.id)
            //         .update(conn);
            //     assert_eq!(num_updated, 1);
            // });
        }
    }
}
<commit_msg>Have get_pending compiling.  Still need more code.<commit_after>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;
extern crate github;
extern crate url;

use self::url::Url;
use self::github::notification::PushNotification;
use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub struct PendingBuild {
    pub clone_url: Url,
    pub branch: String
}

/// Type A is some key
pub trait Database : Sync + Send {
    fn add_pending(&self, entry: PushNotification);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<PendingBuild>;

    fn add_test_results(&self, entry: &PendingBuild, results: BuildResult);
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate time;
    extern crate pg_typeprovider;
    extern crate github;
    extern crate url;

    use self::url::Url;

    use super::PendingBuild;

    use self::github::notification::PushNotification;

    use self::time::Timespec;
    use self::pg_typeprovider::util::Joinable;

    use std::sync::Mutex;

    use super::postgres::{Connection, GenericConnection, SslMode, ToSql};

    use builder::BuildResult;
    use super::EntryStatus::{Pending, InProgress, Done};
    use super::Database;

    pg_table!(builds)
    pg_table!(commits)

    pub struct PostgresDatabase {
        db: Mutex<Connection>
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(loc, &SslMode::None).ok().map(|db| {
                PostgresDatabase {
                    db: Mutex::new(db)
                }
            })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    db.with_connection(|conn| {
                        conn.execute(
                            "DELETE FROM users", &[]).unwrap();
                        conn.execute(
                            "DELETE FROM builds", &[]).unwrap();
                    })
                },
                None => ()
            };
            retval
        }

        pub fn with_connection<A>(&self, f: |&Connection| -> A) -> A {
            f(&*self.db.lock())
        }
    }

    fn get_one_build(conn: &GenericConnection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &GenericConnection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    trait ToPendingBuild {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild;
    }

    impl ToPendingBuild for Build {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild {
            let commit = CommitSearch::new()
                .where_id(self.commit_id)
                .search(conn, Some(1))
                .pop()
                .unwrap(); // should be in there
            PendingBuild {
                clone_url: Url::parse(commit.clone_url.as_slice()).unwrap(),
                branch: commit.branch_name
            }
        }
    }

    impl Database for PostgresDatabase {
        fn add_pending(&self, entry: PushNotification) {
            // self.with_connection(|conn| {
            //     let trans = conn.transaction().unwrap();

            // self.with_connection(|conn| entry.insert(conn));
        }

        fn get_pending(&self) -> Option<PendingBuild> {
            self.with_connection(|conn| {
                loop {
                    let trans = conn.transaction().unwrap();
                    match get_one_build(&trans) {
                        Some(b) => {
                            let res = try_lock_build(&trans, &b);
                            assert!(res);
                            if trans.commit().is_ok() {
                                return Some(b.to_pending_build(conn))
                            }
                        },
                        None => { return None; }
                    }
                }
            })
        }

        fn add_test_results(&self, entry: &PendingBuild, results: BuildResult) {
            // TODO: we do a copy here because we cannot move into a closure,
            // even though this is what we want.  I spent around 2 hours trying
            // to get around this, but to no avail.

            // let res = results.consume_to_json().to_string();
            // let s = res.as_slice();
            // self.with_connection(|conn| {
            //     let num_updated =
            //         BuildUpdate::new()
            //         .status_to((&Done).to_int())
            //         .results_to(s.to_string())
            //         .where_id(entry.id)
            //         .update(conn);
            //     assert_eq!(num_updated, 1);
            // });
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::min;
use std::rc::Rc;
use ncurses::chtype;

use theme::ColorPair;
use Cursive;
use align::*;
use view::{View, IdView, SizeRequest, DimensionRequest};
use event::{Event, EventResult, Key};
use vec::Vec2;
use printer::Printer;
use super::scroll::ScrollBase;

struct Item<T> {
    label: String,
    value: Rc<T>,
}

impl<T> Item<T> {
    fn new(label: &str, value: T) -> Self {
        Item {
            label: label.to_string(),
            value: Rc::new(value),
        }
    }
}

/// View to select an item among a list.
///
/// It contains a list of values of type T, with associated labels.
pub struct SelectView<T = String> {
    items: Vec<Item<T>>,
    focus: usize,
    scrollbase: ScrollBase,
    select_cb: Option<Rc<Box<Fn(&mut Cursive, &T)>>>,
    align: Align,
}

impl<T: 'static> SelectView<T> {
    /// Creates a new empty SelectView.
    pub fn new() -> Self {
        SelectView {
            items: Vec::new(),
            focus: 0,
            scrollbase: ScrollBase::new(),
            select_cb: None,
            align: Align::top_left(),
        }
    }

    pub fn set_on_select<F>(&mut self, cb: F)
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.select_cb = Some(Rc::new(Box::new(cb)));
    }

    /// Sets a function to be called when an item is selected (when ENTER is pressed).
    pub fn on_select<F>(mut self, cb: F) -> Self
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.set_on_select(cb);

        self
    }

    /// Sets the alignment for this view.
    pub fn align(mut self, align: Align) -> Self {
        self.align = align;

        self
    }

    /// Sets the vertical alignment for this view.
    /// (If the ciew is given too much space vertically.)
    pub fn v_align(mut self, v: VAlign) -> Self {
        self.align.v = v;

        self
    }

    /// Sets the horizontal alignment for this view.
    pub fn h_align(mut self, h: HAlign) -> Self {
        self.align.h = h;

        self
    }

    /// Returns the value of the currently selected item. Panics if the list is empty.
    pub fn selection(&self) -> Rc<T> {
        self.items[self.focus].value.clone()
    }

    /// Adds a item to the list, with given label and value.
    pub fn add_item(&mut self, label: &str, value: T) {
        self.items.push(Item::new(label, value));
    }

    /// Chainable variant of add_item
    pub fn item(mut self, label: &str, value: T) -> Self {
        self.add_item(label, value);

        self
    }

    /// Wraps this view into an IdView with the given id.
    pub fn with_id(self, label: &str) -> IdView<Self> {
        IdView::new(label, self)
    }
}

impl SelectView<String> {
    /// For String-based SelectView, this is a convenient method to use the label as value.
    pub fn add_item_str(&mut self, label: &str) {
        self.add_item(label, label.to_string());
    }

    /// Chainable variant of add_item_str
    pub fn item_str(self, label: &str) -> Self {
        self.item(label, label.to_string())
    }
}

impl<T: 'static> View for SelectView<T> {
    fn draw(&mut self, printer: &Printer) {

        let h = self.items.len();
        let offset = self.align.v.get_offset(h, printer.size.y);
        let printer = &printer.sub_printer(Vec2::new(0, offset), printer.size, true);

        self.scrollbase.draw(printer, |printer, i| {
            let style = if i == self.focus {
                if printer.focused {
                    ColorPair::Highlight
                } else {
                    ColorPair::HighlightInactive
                }
            } else {
                ColorPair::Primary
            };
            printer.with_color(style, |printer| {
                let l = self.items[i].label.chars().count();
                let x = self.align.h.get_offset(l, printer.size.x);
                printer.print_hline((0, 0), x, ' ' as chtype);
                printer.print((x, 0), &self.items[i].label);
                printer.print_hline((x + l, 0), printer.size.x - l - x, ' ' as chtype);
            });
        });
    }

    fn get_min_size(&self, req: SizeRequest) -> Vec2 {
        let w = self.items.iter().map(|item| item.label.len()).max().unwrap_or(1);
        let h = self.items.len();

        let scrolling = if let DimensionRequest::Fixed(r_h) = req.h {
            r_h < h
        } else if let DimensionRequest::AtMost(r_h) = req.h {
            r_h < h
        } else {
            false
        };

        // Add 2 spaces for the scrollbar if we need
        let w = if scrolling {
            w + 2
        } else {
            w
        };

        Vec2::new(w, h)
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::KeyEvent(Key::Up) if self.focus > 0 => self.focus -= 1,
            Event::KeyEvent(Key::Down) if self.focus + 1 < self.items.len() => self.focus += 1,
            Event::KeyEvent(Key::PageUp) => self.focus -= min(self.focus, 10),
            Event::KeyEvent(Key::PageDown) => {
                self.focus = min(self.focus + 10, self.items.len() - 1)
            }
            Event::KeyEvent(Key::Home) => self.focus = 0,
            Event::KeyEvent(Key::End) => self.focus = self.items.len() - 1,
            Event::KeyEvent(Key::Enter) if self.select_cb.is_some() => {
                if let Some(ref cb) = self.select_cb {
                    let cb = cb.clone();
                    let v = self.selection();
                    return EventResult::Consumed(Some(Rc::new(Box::new(move |s| cb(s, &*v)))));
                }
            }
            Event::CharEvent(c) => {
                // Starting from the current focus, find the first item that match the char.
                // Cycle back to the beginning of the list when we reach the end.
                // This is achieved by chaining twice the iterator
                let iter = self.items.iter().chain(self.items.iter());
                if let Some((i, _)) = iter.enumerate()
                                          .skip(self.focus + 1)
                                          .find(|&(_, item)| item.label.starts_with(c)) {
                    // Apply modulo in case we have a hit from the chained iterator
                    self.focus = i % self.items.len();
                }
            }
            _ => return EventResult::Ignored,
        }

        self.scrollbase.scroll_to(self.focus);

        EventResult::Consumed(None)
    }

    fn take_focus(&mut self) -> bool {
        true
    }

    fn layout(&mut self, size: Vec2) {
        self.scrollbase.set_heights(size.y, self.items.len());
    }
}
<commit_msg>Fix typo in doc<commit_after>use std::cmp::min;
use std::rc::Rc;
use ncurses::chtype;

use theme::ColorPair;
use Cursive;
use align::*;
use view::{View, IdView, SizeRequest, DimensionRequest};
use event::{Event, EventResult, Key};
use vec::Vec2;
use printer::Printer;
use super::scroll::ScrollBase;

struct Item<T> {
    label: String,
    value: Rc<T>,
}

impl<T> Item<T> {
    fn new(label: &str, value: T) -> Self {
        Item {
            label: label.to_string(),
            value: Rc::new(value),
        }
    }
}

/// View to select an item among a list.
///
/// It contains a list of values of type T, with associated labels.
pub struct SelectView<T = String> {
    items: Vec<Item<T>>,
    focus: usize,
    scrollbase: ScrollBase,
    select_cb: Option<Rc<Box<Fn(&mut Cursive, &T)>>>,
    align: Align,
}

impl<T: 'static> SelectView<T> {
    /// Creates a new empty SelectView.
    pub fn new() -> Self {
        SelectView {
            items: Vec::new(),
            focus: 0,
            scrollbase: ScrollBase::new(),
            select_cb: None,
            align: Align::top_left(),
        }
    }

    pub fn set_on_select<F>(&mut self, cb: F)
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.select_cb = Some(Rc::new(Box::new(cb)));
    }

    /// Sets a function to be called when an item is selected (when ENTER is pressed).
    pub fn on_select<F>(mut self, cb: F) -> Self
        where F: Fn(&mut Cursive, &T) + 'static
    {
        self.set_on_select(cb);

        self
    }

    /// Sets the alignment for this view.
    pub fn align(mut self, align: Align) -> Self {
        self.align = align;

        self
    }

    /// Sets the vertical alignment for this view.
    /// (If the view is given too much space vertically.)
    pub fn v_align(mut self, v: VAlign) -> Self {
        self.align.v = v;

        self
    }

    /// Sets the horizontal alignment for this view.
    pub fn h_align(mut self, h: HAlign) -> Self {
        self.align.h = h;

        self
    }

    /// Returns the value of the currently selected item. Panics if the list is empty.
    pub fn selection(&self) -> Rc<T> {
        self.items[self.focus].value.clone()
    }

    /// Adds a item to the list, with given label and value.
    pub fn add_item(&mut self, label: &str, value: T) {
        self.items.push(Item::new(label, value));
    }

    /// Chainable variant of add_item
    pub fn item(mut self, label: &str, value: T) -> Self {
        self.add_item(label, value);

        self
    }

    /// Wraps this view into an IdView with the given id.
    pub fn with_id(self, label: &str) -> IdView<Self> {
        IdView::new(label, self)
    }
}

impl SelectView<String> {
    /// For String-based SelectView, this is a convenient method to use the label as value.
    pub fn add_item_str(&mut self, label: &str) {
        self.add_item(label, label.to_string());
    }

    /// Chainable variant of add_item_str
    pub fn item_str(self, label: &str) -> Self {
        self.item(label, label.to_string())
    }
}

impl<T: 'static> View for SelectView<T> {
    fn draw(&mut self, printer: &Printer) {

        let h = self.items.len();
        let offset = self.align.v.get_offset(h, printer.size.y);
        let printer = &printer.sub_printer(Vec2::new(0, offset), printer.size, true);

        self.scrollbase.draw(printer, |printer, i| {
            let style = if i == self.focus {
                if printer.focused {
                    ColorPair::Highlight
                } else {
                    ColorPair::HighlightInactive
                }
            } else {
                ColorPair::Primary
            };
            printer.with_color(style, |printer| {
                let l = self.items[i].label.chars().count();
                let x = self.align.h.get_offset(l, printer.size.x);
                printer.print_hline((0, 0), x, ' ' as chtype);
                printer.print((x, 0), &self.items[i].label);
                printer.print_hline((x + l, 0), printer.size.x - l - x, ' ' as chtype);
            });
        });
    }

    fn get_min_size(&self, req: SizeRequest) -> Vec2 {
        let w = self.items.iter().map(|item| item.label.len()).max().unwrap_or(1);
        let h = self.items.len();

        let scrolling = if let DimensionRequest::Fixed(r_h) = req.h {
            r_h < h
        } else if let DimensionRequest::AtMost(r_h) = req.h {
            r_h < h
        } else {
            false
        };

        // Add 2 spaces for the scrollbar if we need
        let w = if scrolling {
            w + 2
        } else {
            w
        };

        Vec2::new(w, h)
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::KeyEvent(Key::Up) if self.focus > 0 => self.focus -= 1,
            Event::KeyEvent(Key::Down) if self.focus + 1 < self.items.len() => self.focus += 1,
            Event::KeyEvent(Key::PageUp) => self.focus -= min(self.focus, 10),
            Event::KeyEvent(Key::PageDown) => {
                self.focus = min(self.focus + 10, self.items.len() - 1)
            }
            Event::KeyEvent(Key::Home) => self.focus = 0,
            Event::KeyEvent(Key::End) => self.focus = self.items.len() - 1,
            Event::KeyEvent(Key::Enter) if self.select_cb.is_some() => {
                if let Some(ref cb) = self.select_cb {
                    let cb = cb.clone();
                    let v = self.selection();
                    return EventResult::Consumed(Some(Rc::new(Box::new(move |s| cb(s, &*v)))));
                }
            }
            Event::CharEvent(c) => {
                // Starting from the current focus, find the first item that match the char.
                // Cycle back to the beginning of the list when we reach the end.
                // This is achieved by chaining twice the iterator
                let iter = self.items.iter().chain(self.items.iter());
                if let Some((i, _)) = iter.enumerate()
                                          .skip(self.focus + 1)
                                          .find(|&(_, item)| item.label.starts_with(c)) {
                    // Apply modulo in case we have a hit from the chained iterator
                    self.focus = i % self.items.len();
                }
            }
            _ => return EventResult::Ignored,
        }

        self.scrollbase.scroll_to(self.focus);

        EventResult::Consumed(None)
    }

    fn take_focus(&mut self) -> bool {
        true
    }

    fn layout(&mut self, size: Vec2) {
        self.scrollbase.set_heights(size.y, self.items.len());
    }
}
<|endoftext|>