<commit_before>use bson;
use mongodb::client::MongoClient;

#[test]
fn database_names() {
    let client = MongoClient::with_uri("mongodb://localhost:27018").unwrap();
    let state_results = client.database_names().ok().expect("Failed to execute database_names.");
    for name in state_results {
        if name != "local" {
            client.drop_database(&name[..]).ok().expect("Failed to drop database from server.");
        }
    }

    let base_results = client.database_names().ok().expect("Failed to execute database_names.");
    assert_eq!(1, base_results.len());
    assert_eq!("local", base_results[0]);

    // Build dbs
    let db1 = client.db("new_db");
    let db2 = client.db("new_db_2");
    db1.collection("test1").insert_one(bson::Document::new(), None)
        .ok().expect("Failed to insert placeholder document into collection");
    db2.collection("test2").insert_one(bson::Document::new(), None)
        .ok().expect("Failed to insert placeholder document into collection");

    // Check new dbs
    let results = client.database_names().ok().expect("Failed to execute database_names.");
    assert_eq!(3, results.len());
    assert_eq!("local", results[0]);
    assert_eq!("new_db", results[1]);
    assert_eq!("new_db_2", results[2]);
}

#[test]
fn is_master() {
    let client = MongoClient::with_uri("mongodb://localhost:27017").unwrap();
    let res = client.is_master().ok().expect("Failed to execute is_master.");
    assert!(res);
}
<commit_msg>unordered client testing<commit_after>use bson;
use mongodb::client::MongoClient;

#[test]
fn database_names() {
    let client = MongoClient::with_uri("mongodb://localhost:27018").unwrap();
    let state_results = client.database_names().ok().expect("Failed to execute database_names.");
    for name in state_results {
        if name != "local" {
            client.drop_database(&name[..]).ok().expect("Failed to drop database from server.");
        }
    }

    let base_results = client.database_names().ok().expect("Failed to execute database_names.");
    assert_eq!(1, base_results.len());
    assert_eq!("local", base_results[0]);

    // Build dbs
    let db1 = client.db("new_db");
    let db2 = client.db("new_db_2");
    db1.collection("test1").insert_one(bson::Document::new(), None)
        .ok().expect("Failed to insert placeholder document into collection");
    db2.collection("test2").insert_one(bson::Document::new(), None)
        .ok().expect("Failed to insert placeholder document into collection");

    // Check new dbs
    let results = client.database_names().ok().expect("Failed to execute database_names.");
    assert_eq!(3, results.len());
    assert!(results.contains(&"local".to_owned()));
    assert!(results.contains(&"new_db".to_owned()));
    assert!(results.contains(&"new_db_2".to_owned()));
}

#[test]
fn is_master() {
    let client = MongoClient::with_uri("mongodb://localhost:27017").unwrap();
    let res = client.is_master().ok().expect("Failed to execute is_master.");
    assert!(res);
}
<|endoftext|>


<commit_before>use std::io::Read;

use bytes::BytesMut;
use log::debug;

use mio::net::{TcpListener, TcpStream};
use mio::{Events, Interests, Poll, Token};

mod util;

use util::{any_local_address, init};

use self::TestState::{AfterRead, Initial};

const SERVER: Token = Token(0);
const CLIENT: Token = Token(1);

#[derive(Debug, PartialEq)]
enum TestState {
    Initial,
    AfterRead,
}

struct TestHandler {
    srv: TcpListener,
    cli: TcpStream,
    state: TestState,
    shutdown: bool,
}

impl TestHandler {
    fn new(srv: TcpListener, cli: TcpStream) -> TestHandler {
        TestHandler {
            srv,
            cli,
            state: Initial,
            shutdown: false,
        }
    }

    fn handle_read(&mut self, poll: &mut Poll, tok: Token) {
        debug!("readable; tok={:?}", tok);

        match tok {
            SERVER => {
                debug!("server connection ready for accept");
                let _ = self.srv.accept().unwrap();
            }
            CLIENT => {
                debug!("client readable");

                match self.state {
                    Initial => {
                        let mut buf = [0; 4096];
                        debug!("GOT={:?}", self.cli.read(&mut buf[..]));
                        self.state = AfterRead;
                    }
                    AfterRead => {}
                }

                let mut buf = BytesMut::with_capacity(1024);

                match self.cli.read(&mut buf) {
                    Ok(0) => self.shutdown = true,
                    Ok(_) => panic!("the client socket should not be readable"),
                    Err(e) => panic!("Unexpected error {:?}", e),
                }
            }
            _ => panic!("received unknown token {:?}", tok),
        }
        poll.registry()
            .reregister(&self.cli, CLIENT, Interests::READABLE)
            .unwrap();
    }

    fn handle_write(&mut self, poll: &mut Poll, tok: Token) {
        match tok {
            SERVER => panic!("received writable for token 0"),
            CLIENT => {
                debug!("client connected");
                poll.registry()
                    .reregister(&self.cli, CLIENT, Interests::READABLE)
                    .unwrap();
            }
            _ => panic!("received unknown token {:?}", tok),
        }
    }
}

#[test]
pub fn close_on_drop() {
    init();
    debug!("Starting TEST_CLOSE_ON_DROP");
    let mut poll = Poll::new().unwrap();

    // == Create & setup server socket
    let srv = TcpListener::bind(any_local_address()).unwrap();
    let addr = srv.local_addr().unwrap();

    poll.registry()
        .register(&srv, SERVER, Interests::READABLE)
        .unwrap();

    // == Create & setup client socket
    let sock = TcpStream::connect(addr).unwrap();

    poll.registry()
        .register(&sock, CLIENT, Interests::WRITABLE)
        .unwrap();

    // == Create storage for events
    let mut events = Events::with_capacity(1024);

    // == Setup test handler
    let mut handler = TestHandler::new(srv, sock);

    // == Run test
    while !handler.shutdown {
        poll.poll(&mut events, None).unwrap();

        for event in &events {
            if event.is_readable() {
                handler.handle_read(&mut poll, event.token());
            }

            if event.is_writable() {
                handler.handle_write(&mut poll, event.token());
            }
        }
    }
    assert!(handler.state == AfterRead, "actual={:?}", handler.state);
}
<commit_msg>Replace Byte with Vec in close_on_drop test<commit_after>use std::io::Read;

use log::debug;

use mio::net::{TcpListener, TcpStream};
use mio::{Events, Interests, Poll, Token};

mod util;

use util::{any_local_address, init};

use self::TestState::{AfterRead, Initial};

const SERVER: Token = Token(0);
const CLIENT: Token = Token(1);

#[derive(Debug, PartialEq)]
enum TestState {
    Initial,
    AfterRead,
}

struct TestHandler {
    srv: TcpListener,
    cli: TcpStream,
    state: TestState,
    shutdown: bool,
}

impl TestHandler {
    fn new(srv: TcpListener, cli: TcpStream) -> TestHandler {
        TestHandler {
            srv,
            cli,
            state: Initial,
            shutdown: false,
        }
    }

    fn handle_read(&mut self, poll: &mut Poll, tok: Token) {
        debug!("readable; tok={:?}", tok);

        match tok {
            SERVER => {
                debug!("server connection ready for accept");
                let _ = self.srv.accept().unwrap();
            }
            CLIENT => {
                debug!("client readable");

                match self.state {
                    Initial => {
                        let mut buf = [0; 4096];
                        debug!("GOT={:?}", self.cli.read(&mut buf[..]));
                        self.state = AfterRead;
                    }
                    AfterRead => {}
                }

                let mut buf = Vec::with_capacity(1024);

                match self.cli.read(&mut buf) {
                    Ok(0) => self.shutdown = true,
                    Ok(_) => panic!("the client socket should not be readable"),
                    Err(e) => panic!("Unexpected error {:?}", e),
                }
            }
            _ => panic!("received unknown token {:?}", tok),
        }
        poll.registry()
            .reregister(&self.cli, CLIENT, Interests::READABLE)
            .unwrap();
    }

    fn handle_write(&mut self, poll: &mut Poll, tok: Token) {
        match tok {
            SERVER => panic!("received writable for token 0"),
            CLIENT => {
                debug!("client connected");
                poll.registry()
                    .reregister(&self.cli, CLIENT, Interests::READABLE)
                    .unwrap();
            }
            _ => panic!("received unknown token {:?}", tok),
        }
    }
}

#[test]
pub fn close_on_drop() {
    init();
    debug!("Starting TEST_CLOSE_ON_DROP");
    let mut poll = Poll::new().unwrap();

    // == Create & setup server socket
    let srv = TcpListener::bind(any_local_address()).unwrap();
    let addr = srv.local_addr().unwrap();

    poll.registry()
        .register(&srv, SERVER, Interests::READABLE)
        .unwrap();

    // == Create & setup client socket
    let sock = TcpStream::connect(addr).unwrap();

    poll.registry()
        .register(&sock, CLIENT, Interests::WRITABLE)
        .unwrap();

    // == Create storage for events
    let mut events = Events::with_capacity(1024);

    // == Setup test handler
    let mut handler = TestHandler::new(srv, sock);

    // == Run test
    while !handler.shutdown {
        poll.poll(&mut events, None).unwrap();

        for event in &events {
            if event.is_readable() {
                handler.handle_read(&mut poll, event.token());
            }

            if event.is_writable() {
                handler.handle_write(&mut poll, event.token());
            }
        }
    }
    assert!(handler.state == AfterRead, "actual={:?}", handler.state);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate galvanic_assert;

use galvanic_assert::*;
use galvanic_assert::matchers::*;

mod assert_panic {
    use super::*;

    #[test]
    fn should_assert_a_panic() {
        assert_that!(panic!("panic"), panics);
    }

    #[test]
    fn should_fail_to_assert_a_panic() {
        let panicked = std::panic::catch_unwind(|| {
            assert_that!(1+1, panics);
        });
        assert!(panicked.is_err());
    }
}

mod invariants {
    use super::*;

    #[test]
    fn assertion_should_succeed() {
        assert_that!(1, assertion_always_succeeds());
    }

    #[test]
    fn assertion_should_fail() {
        assert_that!(
            assert_that!(1, assertion_always_fails()),
            panics
        );
    }
}

mod eq {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, eq(1));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, eq(2)), panics
        );
    }
}

mod less_than {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, less_than(2));
        assert_that!(1, lt(2));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, less_than(1)),
            panics
        );
        assert_that!(
            assert_that!(1, lt(1)),
            panics
        );
    }
}

mod greater_than {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, greater_than(0));
        assert_that!(1, gt(0));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, greater_than(1)),
            panics
        );
        assert_that!(
            assert_that!(1, gt(1)),
            panics
        );
    }
}

mod less_than_or_equal {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, less_than_or_equal(2));
        assert_that!(1, less_than_or_equal(1));
        assert_that!(1, leq(2));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, less_than_or_equal(0)),
            panics
        );
        assert_that!(
            assert_that!(1, leq(0)),
            panics
        );
    }
}

mod greater_than_or_equal {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, greater_than_or_equal(0));
        assert_that!(1, greater_than_or_equal(1));
        assert_that!(1, geq(0));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, greater_than_or_equal(2)),
            panics
        );
        assert_that!(
            assert_that!(1, geq(2)),
            panics
        );
    }
}
<commit_msg>add tests for no()<commit_after>#[macro_use]
extern crate galvanic_assert;

use galvanic_assert::*;
use galvanic_assert::matchers::*;

mod assert_panic {
    use super::*;

    #[test]
    fn should_assert_a_panic() {
        assert_that!(panic!("panic"), panics);
    }

    #[test]
    fn should_fail_to_assert_a_panic() {
        let panicked = std::panic::catch_unwind(|| {
            assert_that!(1+1, panics);
        });
        assert!(panicked.is_err());
    }
}

mod invariants {
    use super::*;

    #[test]
    fn assertion_should_succeed() {
        assert_that!(1, assertion_always_succeeds());
    }

    #[test]
    fn assertion_should_fail() {
        assert_that!(
            assert_that!(1, assertion_always_fails()),
            panics
        );
    }
}

mod not {
    use super::*;

    #[test]
    fn should_invert_success() {
        assert_that!(1, not(assertion_always_fails()));
    }

    #[test]
    fn should_invert_fail() {
        assert_that!(
            assert_that!(1, not(assertion_always_succeeds())),
            panics
        );
    }
}

mod eq {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, eq(1));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, eq(2)), panics
        );
    }
}

mod less_than {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, less_than(2));
        assert_that!(1, lt(2));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, less_than(1)),
            panics
        );
        assert_that!(
            assert_that!(1, lt(1)),
            panics
        );
    }
}

mod greater_than {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, greater_than(0));
        assert_that!(1, gt(0));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, greater_than(1)),
            panics
        );
        assert_that!(
            assert_that!(1, gt(1)),
            panics
        );
    }
}

mod less_than_or_equal {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, less_than_or_equal(2));
        assert_that!(1, less_than_or_equal(1));
        assert_that!(1, leq(2));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, less_than_or_equal(0)),
            panics
        );
        assert_that!(
            assert_that!(1, leq(0)),
            panics
        );
    }
}

mod greater_than_or_equal {
    use super::*;

    #[test]
    fn should_match() {
        assert_that!(1, greater_than_or_equal(0));
        assert_that!(1, greater_than_or_equal(1));
        assert_that!(1, geq(0));
    }

    #[test]
    fn should_fail() {
        assert_that!(
            assert_that!(1, greater_than_or_equal(2)),
            panics
        );
        assert_that!(
            assert_that!(1, geq(2)),
            panics
        );
    }
}
<|endoftext|>


<commit_before><commit_msg>Remove needless use statement<commit_after><|endoftext|>


<commit_before>pub mod lexer;
use lexer::Lexer;
use lexer::Literal;
use lexer::Position;
use std::collections::HashSet;

pub struct NameSyntax {
    pub start: Position,
    pub end: Position,
    pub name: String,
}

pub enum StatementSyntax {
   Calculation(CalculationSyntax),
   Let(LetSyntax),
}

pub struct LetSyntax {
    pub start: Position,
    pub end: Position,
    pub binding: BindingSyntax,
}

pub struct BindingSyntax {
    pub start: Position,
    pub end: Position,
    pub name: String,
    pub type_annotation: Option<TypeAnnotationSyntax>,
    pub calculation: CalculationSyntax,
}

pub struct CalculationSyntax {
    pub start: Position,
    pub end: Position,
    pub operation: OperationSyntax,
}

pub struct OperationSyntax {
    pub start: Position,
    pub end: Position,
    pub op: Vec<OperandSyntax>,
}

pub struct OperandSyntax {
    pub start: Position,
    pub end: Position,
    pub primary: ExpressionSyntax,
}

pub enum ExpressionSyntax {
   Constant(ConstantSyntax),
}

pub struct ConstantSyntax {
    pub start: Position,
    pub end: Position,
    pub literal: Literal,
}

pub struct TypeAnnotationSyntax {
    pub start: Position,
    pub end: Position,
    pub type_spec: TypeSpecSyntax,
}

pub enum TypeSpecSyntax {
   Type(TypeSyntax),
}

pub struct TypeSyntax {
    pub start: Position,
    pub end: Position,
    pub name: NameSyntax,
}

pub struct ParserError {
    pub file_name: String,
    pub line: usize,
    pub column: usize,
}

pub struct Parser<'a> {
    lexer: Lexer<'a>,
    _keywords: HashSet<String>,
}

impl<'a> Parser<'a> {
    pub fn new(deck: &'a str) -> Parser {
        let mut keywords = HashSet::new();
        keywords.insert(String::from("intrinsic"));
        keywords.insert(String::from("let"));
        Parser {
            lexer: Lexer::new(deck),
            _keywords: keywords,
        }
    }

    pub fn parse_name(&mut self) -> Result<Option<NameSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let name = self.lexer.parse_identifier();
        match &name {
            Some(name) =>
            if !self.is_identifier(name) {            return Ok(None)

           },
           _ =>            return Ok(None)
,
        }

        let end: Position = self.lexer.get_position();

        let ret = NameSyntax {
            start: start,
            end: end,
            name: name.unwrap(),
            };

        Ok(Some(ret))
    }

    pub fn parse_statement(&mut self) -> Result<Option<StatementSyntax>, ParserError> {
        {
            let node = self.parse_calculation()?;
            if let Some(node) = node {
                return Ok(Some(StatementSyntax::Calculation(node)));
            }
        }
        {
            let node = self.parse_let()?;
            if let Some(node) = node {
                return Ok(Some(StatementSyntax::Let(node)));
            }
        }
        return Ok(None)
    }

    pub fn parse_let(&mut self) -> Result<Option<LetSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let success_let_1 = self.lexer.parse_keyword("let".to_string());
        if !success_let_1 {
            return Ok(None)
        }

        let binding = self.parse_binding()?;
        if let None = binding {
            return Err(ParserError { file_name: "".to_string(), line: self.lexer.line, column: self.lexer.column })
        }

        let end: Position = self.lexer.get_position();

        let ret = LetSyntax {
            start: start,
            end: end,
            binding: binding.unwrap(),
            };

        Ok(Some(ret))
    }

    pub fn parse_binding(&mut self) -> Result<Option<BindingSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let name = self.lexer.parse_identifier();
        match &name {
            Some(name) =>
            if !self.is_identifier(name) {            return Ok(None)

           },
           _ =>            return Ok(None)
,
        }

        let type_annotation = self.parse_typeannotation()?;

        let calculation = self.parse_calculation()?;
        if let None = calculation {
            return Err(ParserError { file_name: "".to_string(), line: self.lexer.line, column: self.lexer.column })
        }

        let end: Position = self.lexer.get_position();

        let ret = BindingSyntax {
            start: start,
            end: end,
            name: name.unwrap(),
            type_annotation: type_annotation,
            calculation: calculation.unwrap(),
            };

        Ok(Some(ret))
    }

    pub fn parse_calculation(&mut self) -> Result<Option<CalculationSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let operation = self.parse_operation()?;
        if let None = operation {
            return Ok(None)
        }

        let success_semicolon_2 = self.lexer.parse_punctuation(";".to_string());
        if !success_semicolon_2 {
            ()
        }

        let end: Position = self.lexer.get_position();

        let ret = CalculationSyntax {
            start: start,
            end: end,
            operation: operation.unwrap(),
            };

        Ok(Some(ret))
    }

    pub fn parse_operation(&mut self) -> Result<Option<OperationSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let op = self.parse_operand_list()?;
        if let None = op {
            return Ok(None)
        }

        let end: Position = self.lexer.get_position();

        let ret = OperationSyntax {
            start: start,
            end: end,
            op: op.unwrap(),
            };

        Ok(Some(ret))
    }

    pub fn parse_operand_list(&mut self) -> Result<Option<Vec<OperandSyntax>>, ParserError> {
        let mut array: Option<Vec<OperandSyntax>> = Option::None;
        loop {
            let node = self.parse_operand()?;
            if let Some(node) = node {
                if let None = array {
                    array = Some(Vec::new())
                };
                match &mut array {
                    Some(a) => a.push(node),
                    None => (),
                }
            } else {
                break;
            }
        }

        Ok(array)
    }

    pub fn parse_operand(&mut self) -> Result<Option<OperandSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let primary = self.parse_expression()?;
        if let None = primary {
            return Ok(None)
        }

        let end: Position = self.lexer.get_position();

        let ret = OperandSyntax {
            start: start,
            end: end,
            primary: primary.unwrap(),
            };

        Ok(Some(ret))
    }

    pub fn parse_expression(&mut self) -> Result<Option<ExpressionSyntax>, ParserError> {
        {
            let node = self.parse_constant()?;
            if let Some(node) = node {
                return Ok(Some(ExpressionSyntax::Constant(node)));
            }
        }
        return Ok(None)
    }

    pub fn parse_constant(&mut self) -> Result<Option<ConstantSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let literal = self.lexer.parse_literal();
        if let None = literal {
            return Ok(None)
        }

        let end: Position = self.lexer.get_position();

        let ret = ConstantSyntax {
            start: start,
            end: end,
            literal: literal.unwrap(),
            };

        Ok(Some(ret))
    }

    pub fn parse_typeannotation(&mut self) -> Result<Option<TypeAnnotationSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let success_colon_1 = self.lexer.parse_punctuation(":".to_string());
        if !success_colon_1 {
            return Ok(None)
        }

        let type_spec = self.parse_typespec()?;
        if let None = type_spec {
            return Err(ParserError { file_name: "".to_string(), line: self.lexer.line, column: self.lexer.column })
        }

        let end: Position = self.lexer.get_position();

        let ret = TypeAnnotationSyntax {
            start: start,
            end: end,
            type_spec: type_spec.unwrap(),
            };

        Ok(Some(ret))
    }

    pub fn parse_typespec(&mut self) -> Result<Option<TypeSpecSyntax>, ParserError> {
        {
            let node = self.parse_type()?;
            if let Some(node) = node {
                return Ok(Some(TypeSpecSyntax::Type(node)));
            }
        }
        return Ok(None)
    }

    pub fn parse_type(&mut self) -> Result<Option<TypeSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let name = self.parse_name()?;
        if let None = name {
            return Ok(None)
        }

        let end: Position = self.lexer.get_position();

        let ret = TypeSyntax {
            start: start,
            end: end,
            name: name.unwrap(),
            };

        Ok(Some(ret))
    }

    pub fn is_at_end(&mut self) -> bool {
        self.lexer.is_at_end()
    }

    pub fn get_current_line(&self) -> usize {
        self.lexer.get_position().line
    }

    pub fn get_current_column(&self) -> usize {
        self.lexer.get_position().column
    }

    pub fn get_previous_line(&self) -> usize {
        self.lexer.get_previous_position().line
    }

    pub fn get_previous_column(&self) -> usize {
        self.lexer.get_previous_position().column
    }
    fn is_identifier(&self, id: &String) -> bool {
        if self._keywords.contains(id) {
            false
        } else {
            true
        }
    }
}
<commit_msg>rustfmt on parser.rs<commit_after>pub mod lexer;
use lexer::Lexer;
use lexer::Literal;
use lexer::Position;
use std::collections::HashSet;

pub struct NameSyntax {
    pub start: Position,
    pub end: Position,
    pub name: String,
}

pub enum StatementSyntax {
    Calculation(CalculationSyntax),
    Let(LetSyntax),
}

pub struct LetSyntax {
    pub start: Position,
    pub end: Position,
    pub binding: BindingSyntax,
}

pub struct BindingSyntax {
    pub start: Position,
    pub end: Position,
    pub name: String,
    pub type_annotation: Option<TypeAnnotationSyntax>,
    pub calculation: CalculationSyntax,
}

pub struct CalculationSyntax {
    pub start: Position,
    pub end: Position,
    pub operation: OperationSyntax,
}

pub struct OperationSyntax {
    pub start: Position,
    pub end: Position,
    pub op: Vec<OperandSyntax>,
}

pub struct OperandSyntax {
    pub start: Position,
    pub end: Position,
    pub primary: ExpressionSyntax,
}

pub enum ExpressionSyntax {
    Constant(ConstantSyntax),
}

pub struct ConstantSyntax {
    pub start: Position,
    pub end: Position,
    pub literal: Literal,
}

pub struct TypeAnnotationSyntax {
    pub start: Position,
    pub end: Position,
    pub type_spec: TypeSpecSyntax,
}

pub enum TypeSpecSyntax {
    Type(TypeSyntax),
}

pub struct TypeSyntax {
    pub start: Position,
    pub end: Position,
    pub name: NameSyntax,
}

pub struct ParserError {
    pub file_name: String,
    pub line: usize,
    pub column: usize,
}

pub struct Parser<'a> {
    lexer: Lexer<'a>,
    _keywords: HashSet<String>,
}

impl<'a> Parser<'a> {
    pub fn new(deck: &'a str) -> Parser {
        let mut keywords = HashSet::new();
        keywords.insert(String::from("intrinsic"));
        keywords.insert(String::from("let"));
        Parser {
            lexer: Lexer::new(deck),
            _keywords: keywords,
        }
    }

    pub fn parse_name(&mut self) -> Result<Option<NameSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let name = self.lexer.parse_identifier();
        match &name {
            Some(name) => {
                if !self.is_identifier(name) {
                    return Ok(None);
                }
            }
            _ => return Ok(None),
        }

        let end: Position = self.lexer.get_position();

        let ret = NameSyntax {
            start: start,
            end: end,
            name: name.unwrap(),
        };

        Ok(Some(ret))
    }

    pub fn parse_statement(&mut self) -> Result<Option<StatementSyntax>, ParserError> {
        {
            let node = self.parse_calculation()?;
            if let Some(node) = node {
                return Ok(Some(StatementSyntax::Calculation(node)));
            }
        }
        {
            let node = self.parse_let()?;
            if let Some(node) = node {
                return Ok(Some(StatementSyntax::Let(node)));
            }
        }
        return Ok(None);
    }

    pub fn parse_let(&mut self) -> Result<Option<LetSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let success_let_1 = self.lexer.parse_keyword("let".to_string());
        if !success_let_1 {
            return Ok(None);
        }

        let binding = self.parse_binding()?;
        if let None = binding {
            return Err(ParserError {
                file_name: "".to_string(),
                line: self.lexer.line,
                column: self.lexer.column,
            });
        }

        let end: Position = self.lexer.get_position();

        let ret = LetSyntax {
            start: start,
            end: end,
            binding: binding.unwrap(),
        };

        Ok(Some(ret))
    }

    pub fn parse_binding(&mut self) -> Result<Option<BindingSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let name = self.lexer.parse_identifier();
        match &name {
            Some(name) => {
                if !self.is_identifier(name) {
                    return Ok(None);
                }
            }
            _ => return Ok(None),
        }

        let type_annotation = self.parse_typeannotation()?;

        let calculation = self.parse_calculation()?;
        if let None = calculation {
            return Err(ParserError {
                file_name: "".to_string(),
                line: self.lexer.line,
                column: self.lexer.column,
            });
        }

        let end: Position = self.lexer.get_position();

        let ret = BindingSyntax {
            start: start,
            end: end,
            name: name.unwrap(),
            type_annotation: type_annotation,
            calculation: calculation.unwrap(),
        };

        Ok(Some(ret))
    }

    pub fn parse_calculation(&mut self) -> Result<Option<CalculationSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let operation = self.parse_operation()?;
        if let None = operation {
            return Ok(None);
        }

        let success_semicolon_2 = self.lexer.parse_punctuation(";".to_string());
        if !success_semicolon_2 {
            ()
        }

        let end: Position = self.lexer.get_position();

        let ret = CalculationSyntax {
            start: start,
            end: end,
            operation: operation.unwrap(),
        };

        Ok(Some(ret))
    }

    pub fn parse_operation(&mut self) -> Result<Option<OperationSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let op = self.parse_operand_list()?;
        if let None = op {
            return Ok(None);
        }

        let end: Position = self.lexer.get_position();

        let ret = OperationSyntax {
            start: start,
            end: end,
            op: op.unwrap(),
        };

        Ok(Some(ret))
    }

    pub fn parse_operand_list(&mut self) -> Result<Option<Vec<OperandSyntax>>, ParserError> {
        let mut array: Option<Vec<OperandSyntax>> = Option::None;
        loop {
            let node = self.parse_operand()?;
            if let Some(node) = node {
                if let None = array {
                    array = Some(Vec::new())
                };
                match &mut array {
                    Some(a) => a.push(node),
                    None => (),
                }
            } else {
                break;
            }
        }

        Ok(array)
    }

    pub fn parse_operand(&mut self) -> Result<Option<OperandSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let primary = self.parse_expression()?;
        if let None = primary {
            return Ok(None);
        }

        let end: Position = self.lexer.get_position();

        let ret = OperandSyntax {
            start: start,
            end: end,
            primary: primary.unwrap(),
        };

        Ok(Some(ret))
    }

    pub fn parse_expression(&mut self) -> Result<Option<ExpressionSyntax>, ParserError> {
        {
            let node = self.parse_constant()?;
            if let Some(node) = node {
                return Ok(Some(ExpressionSyntax::Constant(node)));
            }
        }
        return Ok(None);
    }

    pub fn parse_constant(&mut self) -> Result<Option<ConstantSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let literal = self.lexer.parse_literal();
        if let None = literal {
            return Ok(None);
        }

        let end: Position = self.lexer.get_position();

        let ret = ConstantSyntax {
            start: start,
            end: end,
            literal: literal.unwrap(),
        };

        Ok(Some(ret))
    }

    pub fn parse_typeannotation(&mut self) -> Result<Option<TypeAnnotationSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let success_colon_1 = self.lexer.parse_punctuation(":".to_string());
        if !success_colon_1 {
            return Ok(None);
        }

        let type_spec = self.parse_typespec()?;
        if let None = type_spec {
            return Err(ParserError {
                file_name: "".to_string(),
                line: self.lexer.line,
                column: self.lexer.column,
            });
        }

        let end: Position = self.lexer.get_position();

        let ret = TypeAnnotationSyntax {
            start: start,
            end: end,
            type_spec: type_spec.unwrap(),
        };

        Ok(Some(ret))
    }

    pub fn parse_typespec(&mut self) -> Result<Option<TypeSpecSyntax>, ParserError> {
        {
            let node = self.parse_type()?;
            if let Some(node) = node {
                return Ok(Some(TypeSpecSyntax::Type(node)));
            }
        }
        return Ok(None);
    }

    pub fn parse_type(&mut self) -> Result<Option<TypeSyntax>, ParserError> {
        let start: Position = self.lexer.get_previous_position();

        let name = self.parse_name()?;
        if let None = name {
            return Ok(None);
        }

        let end: Position = self.lexer.get_position();

        let ret = TypeSyntax {
            start: start,
            end: end,
            name: name.unwrap(),
        };

        Ok(Some(ret))
    }

    pub fn is_at_end(&mut self) -> bool {
        self.lexer.is_at_end()
    }

    pub fn get_current_line(&self) -> usize {
        self.lexer.get_position().line
    }

    pub fn get_current_column(&self) -> usize {
        self.lexer.get_position().column
    }

    pub fn get_previous_line(&self) -> usize {
        self.lexer.get_previous_position().line
    }

    pub fn get_previous_column(&self) -> usize {
        self.lexer.get_previous_position().column
    }
    fn is_identifier(&self, id: &String) -> bool {
        if self._keywords.contains(id) {
            false
        } else {
            true
        }
    }
}
<|endoftext|>


<commit_before>//! Utilities to do with connections between a Minecraft or Forge client and Netherrack

extern crate chrono;
use self::chrono::duration::Duration;

/*use super::packet::{ PacketHeader,
                     ID_HANDSHAKE_CTS_HANDSHAKING,
                     ID_STATUS_CTS_REQUEST, ID_STATUS_CTS_PING
                   };*/

use std::collections::VecDeque;

use std::net::{Shutdown, TcpStream};

use std::io::Read;

//use super::deque_buffer::DequeBuffer;

/// An enum describing the current connection's state
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ConnectionState {

    /// Connection is in handshake mode
    HANDSHAKE,

    /// Connection is in login mode
    LOGIN,

    /// Connection is in status mode
    STATUS,

    /// Connection is in play mode
    PLAY

}

/// A struct defining a game connection
#[derive(Debug)]
pub struct GameConnection {

    /// The TCP stream bound to this GameConnection
    pub stream: TcpStream,

    /// The state of this GameConnection
    pub state: ConnectionState,

    /// Whether the client has the Forge Mod Loader injected into it
    ///
    /// This may change in 1.9 as FML is being destroyed and Forge is becoming the norm
    pub forge_enabled: bool,

    /// A boolean declaring if this GameConnection is connected
    pub connected: bool

}

impl GameConnection {

    /// Creates a new GameConnection
    pub fn new(stream: TcpStream) -> GameConnection {
        GameConnection {
            stream: stream,
            state: ConnectionState::HANDSHAKE,
            forge_enabled: false,
            connected: true //HURR DURR
        }
    }

    pub fn disconnect(&mut self) {
        //TODO: Check to see if in ConnectionState::PLAY, otherwise
        let result = self.stream.shutdown(Shutdown::Both);

        if result.is_ok() == false {
            error!("Error disconnecting: {:?}", result);
        }

        self.connected = false;

    }

    /// Starts listening on the connection
    pub fn start_listening(&mut self) {

        trace!("Got a new client from {}!", self.stream.peer_addr().unwrap());

        let mut timeout_duration = Duration::zero();

        loop {

            if timeout_duration.num_seconds() >= 10 {
                info!("Client at {} timed out, dropping connection", self.stream.peer_addr().unwrap());
                self.disconnect();
                break;
            }

            if !self.connected {
                debug!("Client at {} was forcibly disconnected, maybe for a good reason", self.stream.peer_addr().unwrap());
                break;
            }

            timeout_duration = timeout_duration + Duration::milliseconds(2);
            ::std::thread::sleep_ms(2);
        /*
            let mut raw_buffer = vec![0u8; 512];

            match self.stream.read(&mut raw_buffer) {

                Err(error) => {
                    // Connection dropped out or something broke
                    // In the future, we use the customized NetherrackStream to see if we're in GAME mode, and if so, get the attached player and start saving their information and get rid of them
                    // For now, we just exit the loop
                    error!("Got an error! {}", error);

                    self.disconnect();
                    break;
                }
                Ok(count) => {
                    if count == 0 {
                        //Nothing in stream, let's sleep for a few milliseconds and try again
                        timeout_duration = timeout_duration + Duration::milliseconds(2);
                        ::std::thread::sleep_ms(2);
                    } else {

                        timeout_duration = Duration::zero();

                        // Now truncate the raw buffer - Don't worry, it will reallocate to 512 on the next networking loop
                        raw_buffer.truncate(count);

                        let mut backing: VecDeque<u8> = VecDeque::<u8>::with_capacity(count);

                        for b in raw_buffer {

                            backing.push_back(b);
                        }

                        let mut buffer = DequeBuffer::from(backing);

                        //drop(raw_buffer);

                        let length_result = buffer.read_unsigned_varint_32();
                        let id_result = buffer.read_unsigned_varint_32();

                        if length_result.is_err() || id_result.is_err() {
                            error!("Error while reading packet ID or length! Oh no, dropping connection");

                            self.disconnect();
                            break;
                        }

                        let packet_header = PacketHeader::new(length_result.unwrap(), id_result.unwrap());

                        if self.state == ConnectionState::HANDSHAKE {

                            match packet_header.id {

                                ID_HANDSHAKE_CTS_HANDSHAKING => {
                                    super::packet::incoming::handshake::HandshakePacket::decode(packet_header, self, &mut buffer).handle(self);
                                },
                                _ => debug!("Incoming handshake packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length)

                            }

                        } else if self.state == ConnectionState::LOGIN {

                            match packet_header.id {

                                _ => debug!("Incoming login packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length)

                            }

                        } else if self.state == ConnectionState::STATUS {

                            match packet_header.id {

                                ID_STATUS_CTS_REQUEST => {
                                    super::packet::incoming::status::StatusRequestPacket::decode(packet_header, self, &mut buffer).handle(self);
                                },
                                ID_STATUS_CTS_PING => {
                                    super::packet::incoming::status::ListPingPacket::decode(packet_header, self, &mut buffer).handle(self);
                                },
                                _ => debug!("Incoming play packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length)

                            }

                        } else if self.state == ConnectionState::PLAY {

                            match packet_header.id {

                                _ => debug!("Incoming play packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length)

                            }

                        }

                        // This is a memory leak. We need to get the time of last packet and check to see if it's more than 20 seconds ago
                    }
                }

            }*/

        }

    }

}
<commit_msg>Yield disconnected network threads on disconnect<commit_after>//! Utilities to do with connections between a Minecraft or Forge client and Netherrack

extern crate chrono;
use self::chrono::duration::Duration;

/*use super::packet::{ PacketHeader,
                     ID_HANDSHAKE_CTS_HANDSHAKING,
                     ID_STATUS_CTS_REQUEST, ID_STATUS_CTS_PING
                   };*/

use std::net::{Shutdown, TcpStream};

use std::io::Read;

//use super::deque_buffer::DequeBuffer;

/// An enum describing the current connection's state
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ConnectionState {

    /// Connection is in handshake mode
    HANDSHAKE,

    /// Connection is in login mode
    LOGIN,

    /// Connection is in status mode
    STATUS,

    /// Connection is in play mode
    PLAY

}

/// A struct defining a game connection
#[derive(Debug)]
pub struct GameConnection {

    /// The TCP stream bound to this GameConnection
    pub stream: TcpStream,

    /// The state of this GameConnection
    pub state: ConnectionState,

    /// Whether the client has the Forge Mod Loader injected into it
    ///
    /// This may change in 1.9 as FML is being destroyed and Forge is becoming the norm
    pub forge_enabled: bool,

    /// A boolean declaring if this GameConnection is connected
    pub connected: bool

}

impl GameConnection {

    /// Creates a new GameConnection
    pub fn new(stream: TcpStream) -> GameConnection {
        GameConnection {
            stream: stream,
            state: ConnectionState::HANDSHAKE,
            forge_enabled: false,
            connected: true //HURR DURR
        }
    }

    pub fn disconnect(&mut self) {
        //TODO: Check to see if in ConnectionState::PLAY, otherwise
        let result = self.stream.shutdown(Shutdown::Both);

        if result.is_ok() == false {
            error!("Error disconnecting: {:?}", result);
        }

        self.connected = false;

        ::std::thread::yield_now();

    }

    /// Starts listening on the connection
    pub fn start_listening(&mut self) {

        trace!("Got a new client from {}!", self.stream.peer_addr().unwrap());

        let mut timeout_duration = Duration::zero();

        loop {

            if timeout_duration.num_seconds() >= 1 {
                info!("Client at {} timed out, dropping connection", self.stream.peer_addr().unwrap());
                self.disconnect();
                break;
            }

            if !self.connected {
                debug!("Client at {} was forcibly disconnected, maybe for a good reason", self.stream.peer_addr().unwrap());
                break;
            }

            timeout_duration = timeout_duration + Duration::milliseconds(2);
            ::std::thread::sleep_ms(2);
        /*
            let mut raw_buffer = vec![0u8; 512];

            match self.stream.read(&mut raw_buffer) {

                Err(error) => {
                    // Connection dropped out or something broke
                    // In the future, we use the customized NetherrackStream to see if we're in GAME mode, and if so, get the attached player and start saving their information and get rid of them
                    // For now, we just exit the loop
                    error!("Got an error! {}", error);

                    self.disconnect();
                    break;
                }
                Ok(count) => {
                    if count == 0 {
                        //Nothing in stream, let's sleep for a few milliseconds and try again
                        timeout_duration = timeout_duration + Duration::milliseconds(2);
                        ::std::thread::sleep_ms(2);
                    } else {

                        timeout_duration = Duration::zero();

                        // Now truncate the raw buffer - Don't worry, it will reallocate to 512 on the next networking loop
                        raw_buffer.truncate(count);

                        let mut backing: VecDeque<u8> = VecDeque::<u8>::with_capacity(count);

                        for b in raw_buffer {

                            backing.push_back(b);
                        }

                        let mut buffer = DequeBuffer::from(backing);

                        //drop(raw_buffer);

                        let length_result = buffer.read_unsigned_varint_32();
                        let id_result = buffer.read_unsigned_varint_32();

                        if length_result.is_err() || id_result.is_err() {
                            error!("Error while reading packet ID or length! Oh no, dropping connection");

                            self.disconnect();
                            break;
                        }

                        let packet_header = PacketHeader::new(length_result.unwrap(), id_result.unwrap());

                        if self.state == ConnectionState::HANDSHAKE {

                            match packet_header.id {

                                ID_HANDSHAKE_CTS_HANDSHAKING => {
                                    super::packet::incoming::handshake::HandshakePacket::decode(packet_header, self, &mut buffer).handle(self);
                                },
                                _ => debug!("Incoming handshake packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length)

                            }

                        } else if self.state == ConnectionState::LOGIN {

                            match packet_header.id {

                                _ => debug!("Incoming login packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length)

                            }

                        } else if self.state == ConnectionState::STATUS {

                            match packet_header.id {

                                ID_STATUS_CTS_REQUEST => {
                                    super::packet::incoming::status::StatusRequestPacket::decode(packet_header, self, &mut buffer).handle(self);
                                },
                                ID_STATUS_CTS_PING => {
                                    super::packet::incoming::status::ListPingPacket::decode(packet_header, self, &mut buffer).handle(self);
                                },
                                _ => debug!("Incoming play packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length)

                            }

                        } else if self.state == ConnectionState::PLAY {

                            match packet_header.id {

                                _ => debug!("Incoming play packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length)

                            }

                        }

                        // This is a memory leak. We need to get the time of last packet and check to see if it's more than 20 seconds ago
                    }
                }

            }*/

        }

    }

}
<|endoftext|>


<commit_before>use cursive::traits::*;
use cursive::views::*;
use cursive::Cursive;
use failure::{format_err, Error};
use gbhwdb_tools::config::{BoardLayout, GameConfig, GamePlatform};
use gbhwdb_tools::dat::DatFile;
use glob::glob;
use itertools::Itertools;
use std::cell::Cell;
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashSet};
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::path::Path;
use std::rc::Rc;
use std::sync::atomic::{self, AtomicBool};
use strsim::jaro;

#[derive(Clone, Debug)]
struct Dats {
    gb: DatFile,
    gbc: DatFile,
}

impl Dats {
    pub fn get_platform(&self, name: &str) -> Option<GamePlatform> {
        if self.gb.names.contains(name) {
            Some(GamePlatform::Gb)
        } else if self.gbc.names.contains(name) {
            Some(GamePlatform::Gbc)
        } else {
            None
        }
    }
}

fn load_dats() -> Result<Dats, Error> {
    let mut gb_dat = None;
    let mut gbc_dat = None;
    for entry in glob("*.dat")
        .expect("Invalid glob pattern")
        .filter_map(Result::ok)
    {
        match gbhwdb_tools::dat::from_path(&entry) {
            Ok(dat) => match dat.header.as_str() {
                "Nintendo - Game Boy" => gb_dat = Some(dat),
                "Nintendo - Game Boy Color" => gbc_dat = Some(dat),
                _ => (),
            },
            Err(e) => eprintln!("Failed to read {}: {}", entry.to_string_lossy(), e),
        }
    }
    match (gb_dat, gbc_dat) {
        (Some(gb), Some(gbc)) => Ok(Dats { gb, gbc }),
        (None, Some(_)) => Err(format_err!("No GB dat found")),
        (None, None) => Err(format_err!("No GBC dat found")),
        _ => Err(format_err!("No dats found")),
    }
}

fn load_cfgs<P: AsRef<Path>>(path: P) -> Result<BTreeMap<String, GameConfig>, Error> {
    let file = File::open(path)?;
    let file = BufReader::new(file);
    let cfgs = serde_json::from_reader(file)?;
    Ok(cfgs)
}

fn write_cfgs<P: AsRef<Path>>(path: P, cfgs: &BTreeMap<String, GameConfig>) -> Result<(), Error> {
    let file = File::create(path)?;
    let file = BufWriter::new(file);
    serde_json::to_writer_pretty(file, cfgs)?;
    Ok(())
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Command {
    Sync,
    Add,
    SaveQuit,
    Quit,
}

fn main_menu(siv: &mut Cursive, cfgs: &BTreeMap<String, GameConfig>, dats: &Dats) -> Command {
    let mut select = SelectView::new();
    select.add_item("Synchronize", Command::Sync);
    select.add_item("Add a game", Command::Add);
    select.add_item("Save and quit", Command::SaveQuit);
    let choice = Rc::new(Cell::new(Command::Quit));
    {
        let choice = choice.clone();
        select.set_on_submit(move |s, &selected| {
            choice.set(selected);
            s.quit();
        });
    }
    siv.add_layer(
        Dialog::new().title("gbhwdb-dat").content(
            LinearLayout::vertical()
                .child(TextView::new(format!(
                    " GB dat version: {}",
                    dats.gb.version
                )))
                .child(TextView::new(format!(
                    "GBC dat version: {}",
                    dats.gbc.version
                )))
                .child(TextView::new(format!("Number of games: {}", cfgs.len())))
                .child(DummyView.fixed_height(1))
                .child(select),
        ),
    );
    siv.run();
    siv.pop_layer();
    choice.get()
}

static QUIT: AtomicBool = AtomicBool::new(false);

fn main() -> Result<(), Error> {
    let mut cfgs = load_cfgs("../config/games.json")?;
    let dats = load_dats()?;
    let mut siv = Cursive::default();
    siv.add_global_callback('q', |s| {
        QUIT.store(true, atomic::Ordering::SeqCst);
        s.quit();
    });
    while !QUIT.load(atomic::Ordering::SeqCst) {
        let cmd = main_menu(&mut siv, &cfgs, &dats);
        match cmd {
            Command::Sync => sync(&mut siv, &mut cfgs, &dats),
            Command::Add => add(&mut siv, &mut cfgs, &dats),
            Command::SaveQuit => {
                write_cfgs("../config/games.json", &cfgs)?;
                break;
            }
            Command::Quit => {
                break;
            }
        }
    }
    Ok(())
}

#[derive(Clone, Debug)]
struct Candidate {
    name: String,
    rating: f64,
}

impl Candidate {
    pub fn new(current_name: &str, name: &str) -> Candidate {
        Candidate {
            name: name.to_owned(),
            rating: jaro(&current_name.to_lowercase(), &name.to_lowercase()),
        }
    }
}

fn sync(siv: &mut Cursive, cfgs: &mut BTreeMap<String, GameConfig>, dats: &Dats) {
    let gb_names = dats.gb.names.iter();
    let gbc_names = dats.gbc.names.iter();
    let names = gb_names.chain(gbc_names).cloned().collect::<HashSet<_>>();
    let name_problems = cfgs
        .iter_mut()
        .filter(|(_, game_cfg)| !names.contains(&game_cfg.name))
        .collect::<Vec<_>>();
    if name_problems.len() > 0 {
        let total = name_problems.len();
        for (idx, (code, game_cfg)) in name_problems.into_iter().enumerate() {
            let candidates = names
                .iter()
                .map(|name| Candidate::new(&game_cfg.name, &name))
                .sorted_by(|a, b| b.rating.partial_cmp(&a.rating).unwrap_or(Ordering::Equal))
                .take(5)
                .map(|c| (format!("Score {:.02}: {}", c.rating, c.name), Some(c.name)));
            let mut select = SelectView::new();
            select.add_item("(skip)", None);
            select.add_all(candidates);
            let choice = Rc::new(Cell::new(None));
            {
                let choice = choice.clone();
                select.set_on_submit(move |s, selected| {
                    choice.set(selected.as_ref().cloned());
                    s.quit();
                });
            }
            {
                let current_name = game_cfg.name.clone();
                select.set_on_select(move |s, selected| {
                    let content = match selected {
                        Some(candidate) => format!("After:  {}", candidate),
                        None => format!("After:  {}", &current_name),
                    };
                    s.call_on_id("selection", |view: &mut TextView| {
                        view.set_content(content);
                    });
                });
            }
            siv.add_layer(
                Dialog::new()
                    .title(format!("Fix name problem {}/{}", idx + 1, total))
                    .padding((2, 2, 1, 1))
                    .content(
                        LinearLayout::vertical()
                            .child(TextView::new(format!("Game code: {}", code)))
                            .child(TextView::new(format!("Before: {}", game_cfg.name)))
                            .child(
                                TextView::new(format!("After:  {}", game_cfg.name))
                                    .with_id("selection"),
                            )
                            .child(DummyView.fixed_height(1))
                            .child(select),
                    ),
            );
            siv.run();
            siv.pop_layer();
            if QUIT.load(atomic::Ordering::SeqCst) {
                return;
            }
            if let Some(name) = choice.replace(None) {
                game_cfg.name = name;
            }
        }
    }

    let platform_problems = cfgs
        .iter_mut()
        .filter_map(|(code, cfg)| match dats.get_platform(&cfg.name) {
            Some(platform) if platform != cfg.platform => Some((code, cfg, platform)),
            _ => None,
        })
        .collect::<Vec<_>>();
    if platform_problems.len() > 0 {
        let total = platform_problems.len();
        for (idx, (code, cfg, platform)) in platform_problems.into_iter().enumerate() {
            let choice = Rc::new(Cell::new(false));
            let mut dialog = Dialog::new()
                .title(format!("Fix platform problem {}/{}", idx + 1, total))
                .content(
                    LinearLayout::vertical()
                        .child(TextView::new(format!("Game code: {}", code)))
                        .child(TextView::new(format!("Before: {}", cfg.platform)))
                        .child(TextView::new(format!("After:  {}", platform))),
                );
            {
                let choice = choice.clone();
                dialog.add_button("Ok", move |s| {
                    choice.set(true);
                    s.quit();
                });
            }
            {
                let choice = choice.clone();
                dialog.add_button("Cancel", move |s| {
                    choice.set(false);
                    s.quit();
                });
            }
            siv.add_layer(dialog);
            siv.run();
            siv.pop_layer();
            if QUIT.load(atomic::Ordering::SeqCst) {
                return;
            }
            if choice.get() {
                cfg.platform = platform;
            }
        }
    }

    siv.add_layer(
        Dialog::around(TextView::new("Synchronization complete")).button("Ok", |s| s.quit()),
    );
    siv.run();
    siv.pop_layer();
}

fn add(siv: &mut Cursive, cfgs: &mut BTreeMap<String, GameConfig>, dats: &Dats) {
    let existing_names = cfgs
        .values()
        .map(|cfg| cfg.name.clone())
        .collect::<HashSet<_>>();
    let gb_games = dats
        .gb
        .names
        .iter()
        .filter(|name| !existing_names.contains(name.as_str()))
        .cloned()
        .map(|name| (GamePlatform::Gb, name));
    let gbc_games = dats
        .gbc
        .names
        .iter()
        .filter(|name| !existing_names.contains(name.as_str()))
        .cloned()
        .map(|name| (GamePlatform::Gbc, name));
    let games = gb_games.chain(gbc_games).collect::<Vec<_>>();
    let mut search = EditView::new();
    search.set_on_edit(move |s, text, _| {
        s.call_on_id(
            "search_results",
            |results: &mut SelectView<(GamePlatform, String)>| {
                results.clear();
                if text.len() > 0 {
                    let candidates = games
                        .iter()
                        .map(|(platform, name)| (*platform, Candidate::new(text, &name)))
                        .sorted_by(|(_, a), (_, b)| {
                            b.rating.partial_cmp(&a.rating).unwrap_or(Ordering::Equal)
                        })
                        .take(10)
                        .map(|(platform, c)| {
                            (
                                format!("Score {:.02}: {}", c.rating, c.name),
                                (platform, c.name),
                            )
                        });
                    results.add_all(candidates);
                }
            },
        );
    });
    let choice = Rc::new(Cell::<Option<(GamePlatform, String)>>::new(None));
    {
        let choice = choice.clone();
        search.set_on_submit(move |s, _| {
            if let Some(Some(selection)) = s.call_on_id(
                "search_results",
                |results: &mut SelectView<(GamePlatform, String)>| results.selection(),
            ) {
                choice.set(Some((selection.0, selection.1.clone())));
                s.quit();
            }
        });
    }
    let mut select = SelectView::<(GamePlatform, String)>::new();
    {
        let choice = choice.clone();
        select.set_on_submit(move |s, selection: &(GamePlatform, String)| {
            choice.set(Some(selection.clone()));
            s.quit();
        });
    }
    siv.add_layer(
        Dialog::new()
            .title("Select game to add")
            .content(
                LinearLayout::vertical()
                    .child(TextView::new("Search:"))
                    .child(search)
                    .child(DummyView.fixed_height(1))
                    .child(TextView::new("Results:"))
                    .child(select.with_id("search_results").fixed_height(10)),
            )
            .fixed_width(150),
    );
    siv.run();
    siv.pop_layer();
    let (platform, name) = choice
        .replace(None)
        .unwrap_or((GamePlatform::Gb, String::new()));
    if name.len() == 0 || QUIT.load(atomic::Ordering::SeqCst) {
        return;
    }
    let code = EditView::new();
    let mut layout = RadioGroup::new();
    let layout_choice = Rc::new(Cell::new(BoardLayout::RomMapperRam));
    let layout_container = LinearLayout::vertical()
        .child(layout.button(BoardLayout::Rom, "Rom"))
        .child(layout.button(BoardLayout::RomMapper, "Rom + mapper"))
        .child(
            layout
                .button(BoardLayout::RomMapperRam, "Rom + mapper + ram")
                .selected(),
        )
        .child(layout.button(
            BoardLayout::RomMapperRamXtal,
            "Rom + mapper + ram + crystal",
        ))
        .child(layout.button(BoardLayout::Mbc2, "MBC2"))
        .child(layout.button(BoardLayout::Mbc6, "MBC6"))
        .child(layout.button(BoardLayout::Mbc7, "MBC7"))
        .child(layout.button(BoardLayout::Type15, "Type 15 (MBC5 + dual ROM)"))
        .child(layout.button(BoardLayout::Huc3, "HuC-3"))
        .child(layout.button(BoardLayout::Tama, "Tamagotchi 3"));
    {
        let layout_choice = layout_choice.clone();
        layout.set_on_change(move |_, &layout| layout_choice.set(layout));
    }
    let mut dialog = Dialog::new().title("Add a game").content(
        LinearLayout::vertical()
            .child(TextView::new("Name:"))
            .child(TextView::new(name.as_str()))
            .child(TextView::new("Platform:"))
            .child(TextView::new(format!("{}", platform)))
            .child(TextView::new("Code:"))
            .child(code.with_id("code"))
            .child(TextView::new("Board layout:"))
            .child(layout_container),
    );
    let code_value = Rc::new(Cell::new(None));
    {
        let code_value = code_value.clone();
        dialog.add_button("Ok", move |s| {
            s.call_on_id("code", |code: &mut EditView| {
                code_value.set(Some(code.get_content().to_string()));
            });
            s.quit();
        });
    }
    siv.add_layer(dialog.fixed_width(150));
    siv.run();
    siv.pop_layer();
    if QUIT.load(atomic::Ordering::SeqCst) {
        return;
    }
    if let Some(code) = code_value.replace(None) {
        if code.len() > 0 {
            cfgs.insert(
                code,
                GameConfig {
                    name,
                    platform,
                    layout: layout_choice.get(),
                },
            );
        }
    }
}
<commit_msg>Don't suggest platform change if name is ambiguous<commit_after>use cursive::traits::*;
use cursive::views::*;
use cursive::Cursive;
use failure::{format_err, Error};
use gbhwdb_tools::config::{BoardLayout, GameConfig, GamePlatform};
use gbhwdb_tools::dat::DatFile;
use glob::glob;
use itertools::Itertools;
use std::cell::Cell;
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashSet};
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::path::Path;
use std::rc::Rc;
use std::sync::atomic::{self, AtomicBool};
use strsim::jaro;

#[derive(Clone, Debug)]
struct Dats {
    gb: DatFile,
    gbc: DatFile,
}

impl Dats {
    pub fn get_platform(&self, name: &str) -> Option<GamePlatform> {
        match (self.gb.names.contains(name), self.gbc.names.contains(name)) {
            (true, false) => Some(GamePlatform::Gb),
            (false, true) => Some(GamePlatform::Gbc),
            _ => None
        }
    }
}

fn load_dats() -> Result<Dats, Error> {
    let mut gb_dat = None;
    let mut gbc_dat = None;
    for entry in glob("*.dat")
        .expect("Invalid glob pattern")
        .filter_map(Result::ok)
    {
        match gbhwdb_tools::dat::from_path(&entry) {
            Ok(dat) => match dat.header.as_str() {
                "Nintendo - Game Boy" => gb_dat = Some(dat),
                "Nintendo - Game Boy Color" => gbc_dat = Some(dat),
                _ => (),
            },
            Err(e) => eprintln!("Failed to read {}: {}", entry.to_string_lossy(), e),
        }
    }
    match (gb_dat, gbc_dat) {
        (Some(gb), Some(gbc)) => Ok(Dats { gb, gbc }),
        (None, Some(_)) => Err(format_err!("No GB dat found")),
        (None, None) => Err(format_err!("No GBC dat found")),
        _ => Err(format_err!("No dats found")),
    }
}

fn load_cfgs<P: AsRef<Path>>(path: P) -> Result<BTreeMap<String, GameConfig>, Error> {
    let file = File::open(path)?;
    let file = BufReader::new(file);
    let cfgs = serde_json::from_reader(file)?;
    Ok(cfgs)
}

fn write_cfgs<P: AsRef<Path>>(path: P, cfgs: &BTreeMap<String, GameConfig>) -> Result<(), Error> {
    let file = File::create(path)?;
    let file = BufWriter::new(file);
    serde_json::to_writer_pretty(file, cfgs)?;
    Ok(())
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Command {
    Sync,
    Add,
    SaveQuit,
    Quit,
}

fn main_menu(siv: &mut Cursive, cfgs: &BTreeMap<String, GameConfig>, dats: &Dats) -> Command {
    let mut select = SelectView::new();
    select.add_item("Synchronize", Command::Sync);
    select.add_item("Add a game", Command::Add);
    select.add_item("Save and quit", Command::SaveQuit);
    let choice = Rc::new(Cell::new(Command::Quit));
    {
        let choice = choice.clone();
        select.set_on_submit(move |s, &selected| {
            choice.set(selected);
            s.quit();
        });
    }
    siv.add_layer(
        Dialog::new().title("gbhwdb-dat").content(
            LinearLayout::vertical()
                .child(TextView::new(format!(
                    " GB dat version: {}",
                    dats.gb.version
                )))
                .child(TextView::new(format!(
                    "GBC dat version: {}",
                    dats.gbc.version
                )))
                .child(TextView::new(format!("Number of games: {}", cfgs.len())))
                .child(DummyView.fixed_height(1))
                .child(select),
        ),
    );
    siv.run();
    siv.pop_layer();
    choice.get()
}

static QUIT: AtomicBool = AtomicBool::new(false);

fn main() -> Result<(), Error> {
    let mut cfgs = load_cfgs("../config/games.json")?;
    let dats = load_dats()?;
    let mut siv = Cursive::default();
    siv.add_global_callback('q', |s| {
        QUIT.store(true, atomic::Ordering::SeqCst);
        s.quit();
    });
    while !QUIT.load(atomic::Ordering::SeqCst) {
        let cmd = main_menu(&mut siv, &cfgs, &dats);
        match cmd {
            Command::Sync => sync(&mut siv, &mut cfgs, &dats),
            Command::Add => add(&mut siv, &mut cfgs, &dats),
            Command::SaveQuit => {
                write_cfgs("../config/games.json", &cfgs)?;
                break;
            }
            Command::Quit => {
                break;
            }
        }
    }
    Ok(())
}

#[derive(Clone, Debug)]
struct Candidate {
    name: String,
    rating: f64,
}

impl Candidate {
    pub fn new(current_name: &str, name: &str) -> Candidate {
        Candidate {
            name: name.to_owned(),
            rating: jaro(&current_name.to_lowercase(), &name.to_lowercase()),
        }
    }
}

fn sync(siv: &mut Cursive, cfgs: &mut BTreeMap<String, GameConfig>, dats: &Dats) {
    let gb_names = dats.gb.names.iter();
    let gbc_names = dats.gbc.names.iter();
    let names = gb_names.chain(gbc_names).cloned().collect::<HashSet<_>>();
    let name_problems = cfgs
        .iter_mut()
        .filter(|(_, game_cfg)| !names.contains(&game_cfg.name))
        .collect::<Vec<_>>();
    if name_problems.len() > 0 {
        let total = name_problems.len();
        for (idx, (code, game_cfg)) in name_problems.into_iter().enumerate() {
            let candidates = names
                .iter()
                .map(|name| Candidate::new(&game_cfg.name, &name))
                .sorted_by(|a, b| b.rating.partial_cmp(&a.rating).unwrap_or(Ordering::Equal))
                .take(5)
                .map(|c| (format!("Score {:.02}: {}", c.rating, c.name), Some(c.name)));
            let mut select = SelectView::new();
            select.add_item("(skip)", None);
            select.add_all(candidates);
            let choice = Rc::new(Cell::new(None));
            {
                let choice = choice.clone();
                select.set_on_submit(move |s, selected| {
                    choice.set(selected.as_ref().cloned());
                    s.quit();
                });
            }
            {
                let current_name = game_cfg.name.clone();
                select.set_on_select(move |s, selected| {
                    let content = match selected {
                        Some(candidate) => format!("After:  {}", candidate),
                        None => format!("After:  {}", &current_name),
                    };
                    s.call_on_id("selection", |view: &mut TextView| {
                        view.set_content(content);
                    });
                });
            }
            siv.add_layer(
                Dialog::new()
                    .title(format!("Fix name problem {}/{}", idx + 1, total))
                    .padding((2, 2, 1, 1))
                    .content(
                        LinearLayout::vertical()
                            .child(TextView::new(format!("Game code: {}", code)))
                            .child(TextView::new(format!("Before: {}", game_cfg.name)))
                            .child(
                                TextView::new(format!("After:  {}", game_cfg.name))
                                    .with_id("selection"),
                            )
                            .child(DummyView.fixed_height(1))
                            .child(select),
                    ),
            );
            siv.run();
            siv.pop_layer();
            if QUIT.load(atomic::Ordering::SeqCst) {
                return;
            }
            if let Some(name) = choice.replace(None) {
                game_cfg.name = name;
            }
        }
    }

    let platform_problems = cfgs
        .iter_mut()
        .filter_map(|(code, cfg)| match dats.get_platform(&cfg.name) {
            Some(platform) if platform != cfg.platform => Some((code, cfg, platform)),
            _ => None,
        })
        .collect::<Vec<_>>();
    if platform_problems.len() > 0 {
        let total = platform_problems.len();
        for (idx, (code, cfg, platform)) in platform_problems.into_iter().enumerate() {
            let choice = Rc::new(Cell::new(false));
            let mut dialog = Dialog::new()
                .title(format!("Fix platform problem {}/{}", idx + 1, total))
                .content(
                    LinearLayout::vertical()
                        .child(TextView::new(format!("Game code: {}", code)))
                        .child(TextView::new(format!("Before: {}", cfg.platform)))
                        .child(TextView::new(format!("After:  {}", platform))),
                );
            {
                let choice = choice.clone();
                dialog.add_button("Ok", move |s| {
                    choice.set(true);
                    s.quit();
                });
            }
            {
                let choice = choice.clone();
                dialog.add_button("Cancel", move |s| {
                    choice.set(false);
                    s.quit();
                });
            }
            siv.add_layer(dialog);
            siv.run();
            siv.pop_layer();
            if QUIT.load(atomic::Ordering::SeqCst) {
                return;
            }
            if choice.get() {
                cfg.platform = platform;
            }
        }
    }

    siv.add_layer(
        Dialog::around(TextView::new("Synchronization complete")).button("Ok", |s| s.quit()),
    );
    siv.run();
    siv.pop_layer();
}

fn add(siv: &mut Cursive, cfgs: &mut BTreeMap<String, GameConfig>, dats: &Dats) {
    let existing_names = cfgs
        .values()
        .map(|cfg| cfg.name.clone())
        .collect::<HashSet<_>>();
    let gb_games = dats
        .gb
        .names
        .iter()
        .filter(|name| !existing_names.contains(name.as_str()))
        .cloned()
        .map(|name| (GamePlatform::Gb, name));
    let gbc_games = dats
        .gbc
        .names
        .iter()
        .filter(|name| !existing_names.contains(name.as_str()))
        .cloned()
        .map(|name| (GamePlatform::Gbc, name));
    let games = gb_games.chain(gbc_games).collect::<Vec<_>>();
    let mut search = EditView::new();
    search.set_on_edit(move |s, text, _| {
        s.call_on_id(
            "search_results",
            |results: &mut SelectView<(GamePlatform, String)>| {
                results.clear();
                if text.len() > 0 {
                    let candidates = games
                        .iter()
                        .map(|(platform, name)| (*platform, Candidate::new(text, &name)))
                        .sorted_by(|(_, a), (_, b)| {
                            b.rating.partial_cmp(&a.rating).unwrap_or(Ordering::Equal)
                        })
                        .take(10)
                        .map(|(platform, c)| {
                            (
                                format!("Score {:.02}: {}", c.rating, c.name),
                                (platform, c.name),
                            )
                        });
                    results.add_all(candidates);
                }
            },
        );
    });
    let choice = Rc::new(Cell::<Option<(GamePlatform, String)>>::new(None));
    {
        let choice = choice.clone();
        search.set_on_submit(move |s, _| {
            if let Some(Some(selection)) = s.call_on_id(
                "search_results",
                |results: &mut SelectView<(GamePlatform, String)>| results.selection(),
            ) {
                choice.set(Some((selection.0, selection.1.clone())));
                s.quit();
            }
        });
    }
    let mut select = SelectView::<(GamePlatform, String)>::new();
    {
        let choice = choice.clone();
        select.set_on_submit(move |s, selection: &(GamePlatform, String)| {
            choice.set(Some(selection.clone()));
            s.quit();
        });
    }
    siv.add_layer(
        Dialog::new()
            .title("Select game to add")
            .content(
                LinearLayout::vertical()
                    .child(TextView::new("Search:"))
                    .child(search)
                    .child(DummyView.fixed_height(1))
                    .child(TextView::new("Results:"))
                    .child(select.with_id("search_results").fixed_height(10)),
            )
            .fixed_width(150),
    );
    siv.run();
    siv.pop_layer();
    let (platform, name) = choice
        .replace(None)
        .unwrap_or((GamePlatform::Gb, String::new()));
    if name.len() == 0 || QUIT.load(atomic::Ordering::SeqCst) {
        return;
    }
    let code = EditView::new();
    let mut layout = RadioGroup::new();
    let layout_choice = Rc::new(Cell::new(BoardLayout::RomMapperRam));
    let layout_container = LinearLayout::vertical()
        .child(layout.button(BoardLayout::Rom, "Rom"))
        .child(layout.button(BoardLayout::RomMapper, "Rom + mapper"))
        .child(
            layout
                .button(BoardLayout::RomMapperRam, "Rom + mapper + ram")
                .selected(),
        )
        .child(layout.button(
            BoardLayout::RomMapperRamXtal,
            "Rom + mapper + ram + crystal",
        ))
        .child(layout.button(BoardLayout::Mbc2, "MBC2"))
        .child(layout.button(BoardLayout::Mbc6, "MBC6"))
        .child(layout.button(BoardLayout::Mbc7, "MBC7"))
        .child(layout.button(BoardLayout::Type15, "Type 15 (MBC5 + dual ROM)"))
        .child(layout.button(BoardLayout::Huc3, "HuC-3"))
        .child(layout.button(BoardLayout::Tama, "Tamagotchi 3"));
    {
        let layout_choice = layout_choice.clone();
        layout.set_on_change(move |_, &layout| layout_choice.set(layout));
    }
    let mut dialog = Dialog::new().title("Add a game").content(
        LinearLayout::vertical()
            .child(TextView::new("Name:"))
            .child(TextView::new(name.as_str()))
            .child(TextView::new("Platform:"))
            .child(TextView::new(format!("{}", platform)))
            .child(TextView::new("Code:"))
            .child(code.with_id("code"))
            .child(TextView::new("Board layout:"))
            .child(layout_container),
    );
    let code_value = Rc::new(Cell::new(None));
    {
        let code_value = code_value.clone();
        dialog.add_button("Ok", move |s| {
            s.call_on_id("code", |code: &mut EditView| {
                code_value.set(Some(code.get_content().to_string()));
            });
            s.quit();
        });
    }
    siv.add_layer(dialog.fixed_width(150));
    siv.run();
    siv.pop_layer();
    if QUIT.load(atomic::Ordering::SeqCst) {
        return;
    }
    if let Some(code) = code_value.replace(None) {
        if code.len() > 0 {
            cfgs.insert(
                code,
                GameConfig {
                    name,
                    platform,
                    layout: layout_choice.get(),
                },
            );
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;

use glium::Surface;
use glium::glutin;

mod support;

fn main() {
    use glium::DisplayBuild;

    // building the display, ie. the main object
    let display = glutin::WindowBuilder::new()
        .build_glium()
        .unwrap();

    // building the vertex buffer, which contains all the vertices that we will draw
    let vertex_buffer = {
        #[derive(Copy, Clone)]
        struct Vertex {
            position: [f32; 2]
        }

        implement_vertex!(Vertex, position);

        glium::VertexBuffer::new(&display,
            vec![
                Vertex { position: [-0.005, -0.005] },
                Vertex { position: [  0.0 , 0.005] },
                Vertex { position: [ 0.005, -0.005] },
            ]
        )
    };

    // building the vertex buffer with the attributes per instance
    let per_instance = {
        #[derive(Copy, Clone)]
        struct Attr {
            world_position: [f32; 2],
        }

        implement_vertex!(Attr, world_position);

        let mut data = Vec::new();
        for x in (0u32 .. 104) {
            for y in (0u32 .. 82) {
                data.push(Attr {
                    world_position: [((x as f32) / 50.0) - 1.0, ((y as f32) / 40.0) - 1.0],
                });
            }
        }

        glium::vertex::VertexBuffer::new(&display, data)
    };

    let index_buffer = glium::IndexBuffer::new(&display,
        glium::index::TrianglesList(vec![0u16, 1, 2]));

    let program = glium::Program::from_source(&display,
        "
            #version 140

            in vec2 position;
            in vec2 world_position;

            void main() {
                gl_Position = vec4(position + world_position, 0.0, 1.0);
            }
        ",
        "
            #version 140

            out vec4 color;

            void main() {
                color = vec4(1.0, 0.0, 0.0, 1.0);
            }
        ",
        None)
        .unwrap();

    // the main loop
    support::start_loop(|| {
        // drawing a frame
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 0.0);
        target.draw((&vertex_buffer, per_instance.per_instance_if_supported().unwrap()),
                    &index_buffer, &program, &uniform!{},
                    &Default::default()).unwrap();
        target.finish();

        // polling and handling the events received by the window
        for event in display.poll_events() {
            match event {
                glutin::Event::Closed => return support::Action::Stop,
                _ => ()
            }
        }

        support::Action::Continue
    });
}
<commit_msg>Rework the instancing example to use 10000 teapots<commit_after>#[macro_use]
extern crate glium;
extern crate rand;

use glium::Surface;
use glium::glutin;

mod support;

fn main() {
    use glium::DisplayBuild;

    println!("This example draws 10,000 instanced teapots. Each teapot gets a random position and \
              direction at initialization. Then the CPU updates and uploads the positions of each \
              teapot at each frame.");

    // building the display, ie. the main object
    let display = glutin::WindowBuilder::new()
        .with_depth_buffer(24)
        .build_glium()
        .unwrap();

    // building the vertex and index buffers
    let vertex_buffer = support::load_wavefront(&display, include_bytes!("support/teapot.obj"));
    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    // list of teapots with position and direction
    let mut teapots = (0 .. 10000)
        .map(|_| {
            let pos: (f32, f32, f32) = (rand::random(), rand::random(), rand::random());
            let dir: (f32, f32, f32) = (rand::random(), rand::random(), rand::random());
            let pos = (pos.0 * 1.5 - 0.75, pos.1 * 1.5 - 0.75, pos.2 * 1.5 - 0.75);
            let dir = (dir.0 * 1.5 - 0.75, dir.1 * 1.5 - 0.75, dir.2 * 1.5 - 0.75);
            (pos, dir)
        })
        .collect::<Vec<_>>();

    // building the vertex buffer with the attributes per instance
    let mut per_instance = {
        #[derive(Copy, Clone)]
        struct Attr {
            world_position: (f32, f32, f32),
        }

        implement_vertex!(Attr, world_position);

        let data = teapots.iter().map(|_| {
            Attr {
                world_position: (0.0, 0.0, 0.0),
            }
        }).collect::<Vec<_>>();

        glium::vertex::VertexBuffer::dynamic(&display, data)
    };

    let program = glium::Program::from_source(&display,
        "
            #version 140

            in vec3 position;
            in vec3 normal;
            in vec3 world_position;
            out vec3 v_position;
            out vec3 v_normal;
            out vec3 v_color;

            void main() {
                v_position = position;
                v_normal = normal;
                v_color = vec3(float(gl_InstanceID) / 10000.0, 1.0, 1.0);
                gl_Position = vec4(position * 0.0005 + world_position, 1.0);
            }
        ",
        "
            #version 140

            in vec3 v_normal;
            in vec3 v_color;
            out vec4 f_color;

            const vec3 LIGHT = vec3(-0.2, 0.8, 0.1);

            void main() {
                float lum = max(dot(normalize(v_normal), normalize(LIGHT)), 0.0);
                vec3 color = (0.3 + 0.7 * lum) * v_color;
                f_color = vec4(color, 1.0);
            }
        ",
        None)
        .unwrap();

    let camera = support::camera::CameraState::new();

    // the main loop
    support::start_loop(|| {
        // updating the teapots
        {
            let mut mapping = per_instance.map();
            for (src, dest) in teapots.iter_mut().zip(mapping.iter_mut()) {
                (src.0).0 += (src.1).0 * 0.001;
                (src.0).1 += (src.1).1 * 0.001;
                (src.0).2 += (src.1).2 * 0.001;

                dest.world_position = src.0;
            }
        }

        // drawing a frame
        let params = glium::DrawParameters {
            depth_test: glium::DepthTest::IfLess,
            depth_write: true,
            .. Default::default()
        };

        let mut target = display.draw();
        target.clear_color_and_depth((0.0, 0.0, 0.0, 0.0), 1.0);
        target.draw((&vertex_buffer, per_instance.per_instance_if_supported().unwrap()),
                    &indices, &program, &uniform!{
                        matrix: camera.get_perspective()
                    },
                    &params).unwrap();
        target.finish();

        // polling and handling the events received by the window
        for event in display.poll_events() {
            match event {
                glutin::Event::Closed => return support::Action::Stop,
                _ => ()
            }
        }

        support::Action::Continue
    });
}
<|endoftext|>