<commit_before>// This lexer is based on the grammar I found here: https://github.com/antlr/grammars-v4/blob/master/asm6502/asm6502.g4
// It doesn't support the arithmetic operators, however. It looks like it matches the various 6502 assembly examples
// I have seen online and so is good enough.

use std;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::iter::Peekable;
use std::str;
use assembler::token::{ImmediateBase, LexerToken};
use ::opcodes::OpCode;

#[derive(Debug, PartialEq)]
pub struct LexerError {
    pub message: String,
}

impl LexerError {
    fn unexpected_ident<A, B>(expected: A, found: B, line: u32, column: u32) -> LexerError
        where A: std::fmt::Display,
              B: std::fmt::Display
    {
        LexerError {
            message: format!("Unexpected identifier. Found '{}', expected '{}'. Line {}, col {}",
                             found,
                             expected,
                             line,
                             column),
        }
    }
    fn out_of_bounds<A>(addr: A, line: u32, column: u32) -> LexerError
        where A: std::fmt::Display
    {
        LexerError::from(format!("Memory address '{}' too large. Line {}, col {}",
                                 addr,
                                 line,
                                 column))
    }

    fn error_consuming_number(line: u32, column: u32) -> LexerError {
        LexerError::from(format!("Error consuming number. Line {} col {}", line, column))
    }

    fn unexpected_eof() -> LexerError {
        LexerError::from(format!("Unexpected end of file"))
    }

    fn expected_memory_address(line: u32, column: u32) -> LexerError {
        LexerError::from(format!("Expected memory address. Line {} col {}", line, column))
    }
}

impl From<std::io::Error> for LexerError {
    fn from(error: std::io::Error) -> LexerError {
        LexerError { message: error.description().into() }
    }
}

impl From<String> for LexerError {
    fn from(error: String) -> LexerError {
        LexerError { message: error }
    }
}

impl<'a> From<&'a str> for LexerError {
    fn from(error: &str) -> LexerError {
        LexerError { message: error.into() }
    }
}

/// Lexer accepts the program code as a string
/// and converts it to a list of Tokens
pub struct Lexer {
    line: u32,
    col: u32,
}

impl Lexer {
    pub fn new() -> Lexer {
        Lexer { line: 0, col: 0 }
    }

    /// Returns a vector of Tokens given an input of
    /// 6502 assembly code
    pub fn lex_string<S>(&mut self, input: S) -> Result<Vec<Vec<LexerToken>>, LexerError>
        where S: Into<String>
    {
        Ok(self.lex(input.into())?)
    }

    /// Returns a vector of Tokens given a file
    /// to load 6502 assembly code from
    pub fn lex_file<P>(&mut self, path: P) -> Result<Vec<Vec<LexerToken>>, LexerError>
        where P: AsRef<std::path::Path>
    {
        let mut file = File::open(&path)?;
        let mut contents = String::new();

        file.read_to_string(&mut contents)?;

        Ok(self.lex(contents)?)
    }

    fn advance<I>(&mut self, mut peeker: &mut Peekable<I>)
        where I: Iterator<Item = char>
    {
        if let None = peeker.peek() {
            return;
        }

        peeker.next();
        self.col += 1;
    }

    /// Performs the bulk of the lexing logic
    fn lex(&mut self, source: String) -> Result<Vec<Vec<LexerToken>>, LexerError> {

        let mut result = Vec::new();

        for line in source.lines() {
            self.line += 1;
            self.col = 0;

            // Skip blank lines
            if line.trim().len() == 0 {
                continue;
            }

            let mut tokens = Vec::new();
            let mut iter = line.chars();
            let mut peeker = iter.peekable();

            loop {
                // Break out if we've reached the end of the line
                if let None = peeker.peek() {
                    break;
                }

                // Consume any leading whitespace voids we're sitting in
                if peeker.peek().unwrap().is_whitespace() {
                    self.consume_whitespace(&mut peeker);
                } else if peeker.peek().unwrap().is_alphanumeric() {
                    let token = self.consume_alphanumeric(&mut peeker)?;
                    tokens.push(token);
                } else if *peeker.peek().unwrap() == ';' {
                    // Skip the rest of this line
                    break;
                } else if *peeker.peek().unwrap() == '(' {
                    // Indirect addressing
                    peeker.next(); // Jump it
                    tokens.push(LexerToken::OpenParenthesis);
                } else if *peeker.peek().unwrap() == ')' {
                    // Indirect addressing
                    peeker.next(); // Jump it
                    tokens.push(LexerToken::CloseParenthesis);
                } else if *peeker.peek().unwrap() == '$' {
                    let token = self.consume_address(&mut peeker)?;
                    tokens.push(token);
                } else if *peeker.peek().unwrap() == '#' {
                    if let LexerToken::Immediate(number, base) = self.consume_number(&mut peeker)? {
                        tokens.push(LexerToken::Immediate(number, base));
                    }
                } else if *peeker.peek().unwrap() == '.' {
                    self.advance(&mut peeker);
                    tokens.push(LexerToken::Comma);
                } else if *peeker.peek().unwrap() == '=' {
                    self.advance(&mut peeker);
                    tokens.push(LexerToken::Assignment);
                } else if *peeker.peek().unwrap() == ',' {
                    println!("Comma found");
                    self.advance(&mut peeker);
                    tokens.push(LexerToken::Comma);
                }
            }

            result.push(tokens);
        }

        Ok(result)
    }

    /// Consumes alphanumeric characters until it reachs something that terminates it
    fn consume_alphanumeric<I>(&mut self,
                               mut peeker: &mut Peekable<I>)
                               -> Result<LexerToken, LexerError>
        where I: Iterator<Item = char>
    {
        let mut tok = String::new();

        loop {
            if let None = peeker.peek() {
                break;
            }
            let c = *peeker.peek().unwrap();

            if c.is_alphanumeric() || c == '_' {
                tok.push(c);
                self.advance(&mut peeker);
            } else {
                break;
            }
        }

        Ok(LexerToken::Ident(tok))
    }

    /// Decides the base of a number we are about to consume
    fn consume_number<I>(&mut self, mut peeker: &mut Peekable<I>) -> Result<LexerToken, LexerError>
        where I: Iterator<Item = char>
    {
        // Default to base16
        let mut base = ImmediateBase::Base16;

        let c = *peeker.peek().unwrap();
        if c == '$' {
            // The number is base16
            self.advance(&mut peeker);
            self.consume_digits(&mut peeker, &base)
        } else if c == '#' {
            // The number is base 10
            self.advance(&mut peeker);
            if let None = peeker.peek() {
                return Err(LexerError::unexpected_eof());
            }

            base = ImmediateBase::Base10;
            if *peeker.peek().unwrap() == '$' {
                // Skip over the dollar sign and revert to base16
                base = ImmediateBase::Base16;
                self.advance(&mut peeker);
            }

            self.consume_digits(&mut peeker, &base)
        } else {
            Err(LexerError::error_consuming_number(self.line, self.col))
        }
    }

    /// Consumes number of a specified base until it can't anymore
    fn consume_digits<I>(&mut self,
                         mut peeker: &mut Peekable<I>,
                         base: &ImmediateBase)
                         -> Result<LexerToken, LexerError>
        where I: Iterator<Item = char>
    {
        let mut result = String::new();

        let b = if let ImmediateBase::Base10 = *base {
            10
        } else {
            16
        };
        loop {
            if let None = peeker.peek() {
                break;
            }
            let c = *peeker.peek().unwrap();
            if c.is_digit(b) {
                result.push(c);
                self.advance(&mut peeker);
            } else {
                break;
            }
        }

        Ok(LexerToken::Immediate(result.to_uppercase(), base.clone()))
    }

    /// Consumes a memory address
    fn consume_address<I>(&mut self, mut peeker: &mut Peekable<I>) -> Result<LexerToken, LexerError>
        where I: Iterator<Item = char>
    {
        // Grab the actual numbers
        if let LexerToken::Immediate(val, _) = self.consume_number(&mut peeker)? {
            let val = val.to_uppercase();
            // if the length is greater than 4.. its outside the memory bounds
            if val.len() > 4 {
                return Err(LexerError::out_of_bounds(&val, self.line, self.col - val.len() as u32));
            }

            Ok(LexerToken::Address(val.clone()))

        } else {
            Err(LexerError::expected_memory_address(self.line, self.col))
        }
    }

    /// Consumes whitespace characters until it encounters a
    /// non-whitespace character
    #[inline(always)]
    fn consume_whitespace<I>(&mut self, mut peeker: &mut Peekable<I>)
        where I: Iterator<Item = char>
    {
        loop {
            if let None = peeker.peek() {
                break;
            } else {
                if !peeker.peek().unwrap().is_whitespace() {
                    break;
                } else {
                    self.advance(&mut peeker);
                }
            }
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, LexerToken};

    #[test]
    fn can_lex_basic_opcode_and_addressing_mode() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            LDA $4400
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("LDA".into()), LexerToken::Address("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_variable_assignment() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            MY_VARIABLE = #$20
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("MY_VARIABLE".into()),
                     LexerToken::Assignment,
                     LexerToken::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_base_ten_variable_assignment() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            MY_VARIABLE = #50
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("MY_VARIABLE".into()),
                     LexerToken::Assignment,
                     LexerToken::Immediate("50".into(), ImmediateBase::Base10)],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_absolute_addressing() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            LDA $4400,X
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("LDA".into()),
                     LexerToken::Address("4400".into()),
                     LexerToken::Comma,
                     LexerToken::Ident("X".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_indirect_y_addressing() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            LDA ($FF),Y
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("LDA".into()),
                     LexerToken::OpenParenthesis,
                     LexerToken::Address("FF".into()),
                     LexerToken::CloseParenthesis,
                     LexerToken::Comma,
                     LexerToken::Ident("Y".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_indirect_x_addressing() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            LDA ($FF,X)
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("LDA".into()),
                     LexerToken::OpenParenthesis,
                     LexerToken::Address("FF".into()),
                     LexerToken::Comma,
                     LexerToken::Ident("X".into()),
                     LexerToken::CloseParenthesis],
                   &tokens[0][..]);
    }
}<commit_msg>Add test for unexpected tokens<commit_after>// This lexer is based on the grammar I found here: https://github.com/antlr/grammars-v4/blob/master/asm6502/asm6502.g4
// It doesn't support the arithmetic operators, however. It looks like it matches the various 6502 assembly examples
// I have seen online and so is good enough.

use std;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::iter::Peekable;
use std::str;
use assembler::token::{ImmediateBase, LexerToken};
use ::opcodes::OpCode;

#[derive(Debug, PartialEq)]
pub struct LexerError {
    pub message: String,
}

impl LexerError {
    fn unexpected_ident<A, B>(expected: A, found: B, line: u32, column: u32) -> LexerError
        where A: std::fmt::Display,
              B: std::fmt::Display
    {
        LexerError {
            message: format!("Unexpected identifier. Found '{}', expected '{}'. Line {}, col {}",
                             found,
                             expected,
                             line,
                             column),
        }
    }
    fn out_of_bounds<A>(addr: A, line: u32, column: u32) -> LexerError
        where A: std::fmt::Display
    {
        LexerError::from(format!("Memory address '{}' too large. Line {}, col {}",
                                 addr,
                                 line,
                                 column))
    }

    fn error_consuming_number(line: u32, column: u32) -> LexerError {
        LexerError::from(format!("Error consuming number. Line {} col {}", line, column))
    }

    fn unexpected_eof() -> LexerError {
        LexerError::from(format!("Unexpected end of file"))
    }

    fn expected_memory_address(line: u32, column: u32) -> LexerError {
        LexerError::from(format!("Expected memory address. Line {} col {}", line, column))
    }

    fn unexpected_token(line: u32, column: u32) -> LexerError {
        LexerError::from(format!("Unexpected token. Line {} col {}", line, column))
    }
}

impl From<std::io::Error> for LexerError {
    fn from(error: std::io::Error) -> LexerError {
        LexerError { message: error.description().into() }
    }
}

impl From<String> for LexerError {
    fn from(error: String) -> LexerError {
        LexerError { message: error }
    }
}

impl<'a> From<&'a str> for LexerError {
    fn from(error: &str) -> LexerError {
        LexerError { message: error.into() }
    }
}

/// Lexer accepts the program code as a string
/// and converts it to a list of Tokens
pub struct Lexer {
    line: u32,
    col: u32,
}

impl Lexer {
    pub fn new() -> Lexer {
        Lexer { line: 0, col: 0 }
    }

    /// Returns a vector of Tokens given an input of
    /// 6502 assembly code
    pub fn lex_string<S>(&mut self, input: S) -> Result<Vec<Vec<LexerToken>>, LexerError>
        where S: Into<String>
    {
        Ok(self.lex(input.into())?)
    }

    /// Returns a vector of Tokens given a file
    /// to load 6502 assembly code from
    pub fn lex_file<P>(&mut self, path: P) -> Result<Vec<Vec<LexerToken>>, LexerError>
        where P: AsRef<std::path::Path>
    {
        let mut file = File::open(&path)?;
        let mut contents = String::new();

        file.read_to_string(&mut contents)?;

        Ok(self.lex(contents)?)
    }

    fn advance<I>(&mut self, mut peeker: &mut Peekable<I>)
        where I: Iterator<Item = char>
    {
        if let None = peeker.peek() {
            return;
        }

        peeker.next();
        self.col += 1;
    }

    /// Performs the bulk of the lexing logic
    fn lex(&mut self, source: String) -> Result<Vec<Vec<LexerToken>>, LexerError> {

        let mut result = Vec::new();

        for line in source.lines() {
            self.line += 1;
            self.col = 0;

            // Skip blank lines
            if line.trim().len() == 0 {
                continue;
            }

            let mut tokens = Vec::new();
            let mut iter = line.chars();
            let mut peeker = iter.peekable();

            loop {
                // Break out if we've reached the end of the line
                if let None = peeker.peek() {
                    break;
                }

                // Consume any leading whitespace voids we're sitting in
                if peeker.peek().unwrap().is_whitespace() {
                    self.consume_whitespace(&mut peeker);
                } else if peeker.peek().unwrap().is_alphanumeric() {
                    let token = self.consume_alphanumeric(&mut peeker)?;
                    tokens.push(token);
                } else if *peeker.peek().unwrap() == ';' {
                    // Skip the rest of this line
                    break;
                } else if *peeker.peek().unwrap() == '(' {
                    // Indirect addressing
                    peeker.next(); // Jump it
                    tokens.push(LexerToken::OpenParenthesis);
                } else if *peeker.peek().unwrap() == ')' {
                    // Indirect addressing
                    peeker.next(); // Jump it
                    tokens.push(LexerToken::CloseParenthesis);
                } else if *peeker.peek().unwrap() == '$' {
                    let token = self.consume_address(&mut peeker)?;
                    tokens.push(token);
                } else if *peeker.peek().unwrap() == '#' {
                    if let LexerToken::Immediate(number, base) = self.consume_number(&mut peeker)? {
                        tokens.push(LexerToken::Immediate(number, base));
                    }
                } else if *peeker.peek().unwrap() == '.' {
                    self.advance(&mut peeker);
                    tokens.push(LexerToken::Comma);
                } else if *peeker.peek().unwrap() == '=' {
                    self.advance(&mut peeker);
                    tokens.push(LexerToken::Assignment);
                } else if *peeker.peek().unwrap() == ',' {
                    println!("Comma found");
                    self.advance(&mut peeker);
                    tokens.push(LexerToken::Comma);
                } else {
                    return Err(LexerError::unexpected_token(self.line, self.col));
                }
            }

            result.push(tokens);
        }

        Ok(result)
    }

    /// Consumes alphanumeric characters until it reachs something that terminates it
    fn consume_alphanumeric<I>(&mut self,
                               mut peeker: &mut Peekable<I>)
                               -> Result<LexerToken, LexerError>
        where I: Iterator<Item = char>
    {
        let mut tok = String::new();

        loop {
            if let None = peeker.peek() {
                break;
            }
            let c = *peeker.peek().unwrap();

            if c.is_alphanumeric() || c == '_' {
                tok.push(c);
                self.advance(&mut peeker);
            } else {
                break;
            }
        }

        Ok(LexerToken::Ident(tok))
    }

    /// Decides the base of a number we are about to consume
    fn consume_number<I>(&mut self, mut peeker: &mut Peekable<I>) -> Result<LexerToken, LexerError>
        where I: Iterator<Item = char>
    {
        // Default to base16
        let mut base = ImmediateBase::Base16;

        let c = *peeker.peek().unwrap();
        if c == '$' {
            // The number is base16
            self.advance(&mut peeker);
            self.consume_digits(&mut peeker, &base)
        } else if c == '#' {
            // The number is base 10
            self.advance(&mut peeker);
            if let None = peeker.peek() {
                return Err(LexerError::unexpected_eof());
            }

            base = ImmediateBase::Base10;
            if *peeker.peek().unwrap() == '$' {
                // Skip over the dollar sign and revert to base16
                base = ImmediateBase::Base16;
                self.advance(&mut peeker);
            }

            self.consume_digits(&mut peeker, &base)
        } else {
            Err(LexerError::error_consuming_number(self.line, self.col))
        }
    }

    /// Consumes number of a specified base until it can't anymore
    fn consume_digits<I>(&mut self,
                         mut peeker: &mut Peekable<I>,
                         base: &ImmediateBase)
                         -> Result<LexerToken, LexerError>
        where I: Iterator<Item = char>
    {
        let mut result = String::new();

        let b = if let ImmediateBase::Base10 = *base {
            10
        } else {
            16
        };
        loop {
            if let None = peeker.peek() {
                break;
            }
            let c = *peeker.peek().unwrap();
            if c.is_digit(b) {
                result.push(c);
                self.advance(&mut peeker);
            } else {
                break;
            }
        }

        Ok(LexerToken::Immediate(result.to_uppercase(), base.clone()))
    }

    /// Consumes a memory address
    fn consume_address<I>(&mut self, mut peeker: &mut Peekable<I>) -> Result<LexerToken, LexerError>
        where I: Iterator<Item = char>
    {
        // Grab the actual numbers
        if let LexerToken::Immediate(val, _) = self.consume_number(&mut peeker)? {
            let val = val.to_uppercase();
            // if the length is greater than 4.. its outside the memory bounds
            if val.len() > 4 {
                return Err(LexerError::out_of_bounds(&val, self.line, self.col - val.len() as u32));
            }

            Ok(LexerToken::Address(val.clone()))

        } else {
            Err(LexerError::expected_memory_address(self.line, self.col))
        }
    }

    /// Consumes whitespace characters until it encounters a
    /// non-whitespace character
    #[inline(always)]
    fn consume_whitespace<I>(&mut self, mut peeker: &mut Peekable<I>)
        where I: Iterator<Item = char>
    {
        loop {
            if let None = peeker.peek() {
                break;
            } else {
                if !peeker.peek().unwrap().is_whitespace() {
                    break;
                } else {
                    self.advance(&mut peeker);
                }
            }
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, LexerToken};

    #[test]
    fn can_lex_basic_opcode_and_addressing_mode() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            LDA $4400
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("LDA".into()), LexerToken::Address("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_variable_assignment() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            MY_VARIABLE = #$20
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("MY_VARIABLE".into()),
                     LexerToken::Assignment,
                     LexerToken::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_base_ten_variable_assignment() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            MY_VARIABLE = #50
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("MY_VARIABLE".into()),
                     LexerToken::Assignment,
                     LexerToken::Immediate("50".into(), ImmediateBase::Base10)],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_absolute_addressing() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            LDA $4400,X
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("LDA".into()),
                     LexerToken::Address("4400".into()),
                     LexerToken::Comma,
                     LexerToken::Ident("X".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_indirect_y_addressing() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            LDA ($FF),Y
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("LDA".into()),
                     LexerToken::OpenParenthesis,
                     LexerToken::Address("FF".into()),
                     LexerToken::CloseParenthesis,
                     LexerToken::Comma,
                     LexerToken::Ident("Y".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_lex_indirect_x_addressing() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            LDA ($FF,X)
        ")
            .unwrap();

        assert_eq!(&[LexerToken::Ident("LDA".into()),
                     LexerToken::OpenParenthesis,
                     LexerToken::Address("FF".into()),
                     LexerToken::Comma,
                     LexerToken::Ident("X".into()),
                     LexerToken::CloseParenthesis],
                   &tokens[0][..]);
    }

    #[test]
    fn errors_on_unexpected_token() {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string("
            LDA ($F-----F,X)
        ");

        assert_eq!(Err(LexerError::unexpected_token(2, 18)), tokens);
    }
}<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use fnv::FnvHashMap;
use proc_macro2::TokenStream;
use vulkano::pipeline::layout::PipelineLayoutPcRange;
use vulkano::shader::spirv::ExecutionModel;
use vulkano::shader::{
    DescriptorIdentifier, DescriptorRequirements, ShaderExecution, ShaderInterfaceEntry,
    ShaderInterfaceEntryType, SpecializationConstantRequirements,
};
use vulkano::shader::{EntryPointInfo, ShaderInterface, ShaderStages};

pub(super) fn write_entry_point(
    name: &str,
    model: ExecutionModel,
    info: &EntryPointInfo,
) -> TokenStream {
    let execution = write_shader_execution(&info.execution);
    let model = syn::parse_str::<syn::Path>(&format!(
        "::vulkano::shader::spirv::ExecutionModel::{:?}",
        model
    ))
    .unwrap();
    let descriptor_requirements = write_descriptor_requirements(&info.descriptor_requirements);
    let push_constant_requirements =
        write_push_constant_requirements(&info.push_constant_requirements);
    let specialization_constant_requirements =
        write_specialization_constant_requirements(&info.specialization_constant_requirements);
    let input_interface = write_interface(&info.input_interface);
    let output_interface = write_interface(&info.output_interface);

    quote! {
        (
            #name.to_owned(),
            #model,
            ::vulkano::shader::EntryPointInfo {
                execution: #execution,
                descriptor_requirements: #descriptor_requirements.into_iter().collect(),
                push_constant_requirements: #push_constant_requirements,
                specialization_constant_requirements: #specialization_constant_requirements.into_iter().collect(),
                input_interface: #input_interface,
                output_interface: #output_interface,
            },
        ),
    }
}

fn write_shader_execution(execution: &ShaderExecution) -> TokenStream {
    match execution {
        ShaderExecution::Vertex => quote! { ::vulkano::shader::ShaderExecution::Vertex },
        ShaderExecution::TessellationControl => {
            quote! { ::vulkano::shader::ShaderExecution::TessellationControl }
        }
        ShaderExecution::TessellationEvaluation => {
            quote! { ::vulkano::shader::ShaderExecution::TessellationEvaluation }
        }
        ShaderExecution::Geometry(::vulkano::shader::GeometryShaderExecution { input }) => {
            let input = format_ident!("{}", format!("{:?}", input));
            quote! {
                ::vulkano::shader::ShaderExecution::Geometry {
                    input: GeometryShaderInput::#input,
                }
            }
        }
        ShaderExecution::Fragment => quote! { ::vulkano::shader::ShaderExecution::Fragment },
        ShaderExecution::Compute => quote! { ::vulkano::shader::ShaderExecution::Compute },
    }
}

fn write_descriptor_requirements(
    descriptor_requirements: &FnvHashMap<(u32, u32), DescriptorRequirements>,
) -> TokenStream {
    let descriptor_requirements = descriptor_requirements.into_iter().map(|(loc, reqs)| {
        let (set_num, binding_num) = loc;
        let DescriptorRequirements {
            descriptor_types,
            descriptor_count,
            image_format,
            image_multisampled,
            image_scalar_type,
            image_view_type,
            sampler_compare,
            sampler_no_unnormalized_coordinates,
            sampler_no_ycbcr_conversion,
            sampler_with_images,
            stages,
            storage_image_atomic,
            storage_read,
            storage_write,
        } = reqs;

        let descriptor_types = descriptor_types.into_iter().map(|ty| {
            let ident = format_ident!("{}", format!("{:?}", ty));
            quote! { ::vulkano::descriptor_set::layout::DescriptorType::#ident }
        });
        let image_format = match image_format {
            Some(image_format) => {
                let ident = format_ident!("{}", format!("{:?}", image_format));
                quote! { Some(::vulkano::format::Format::#ident) }
            }
            None => quote! { None },
        };
        let image_scalar_type = match image_scalar_type {
            Some(image_scalar_type) => {
                let ident = format_ident!("{}", format!("{:?}", image_scalar_type));
                quote! { Some(::vulkano::shader::ShaderScalarType::#ident) }
            }
            None => quote! { None },
        };
        let image_view_type = match image_view_type {
            Some(image_view_type) => {
                let ident = format_ident!("{}", format!("{:?}", image_view_type));
                quote! { Some(::vulkano::image::view::ImageViewType::#ident) }
            }
            None => quote! { None },
        };
        let sampler_compare = sampler_compare.iter();
        let sampler_no_unnormalized_coordinates = sampler_no_unnormalized_coordinates.iter();
        let sampler_no_ycbcr_conversion = sampler_no_ycbcr_conversion.iter();
        let sampler_with_images = {
            sampler_with_images.iter().map(|(&index, identifiers)| {
                let identifiers = identifiers.iter().map(
                    |DescriptorIdentifier {
                         set,
                         binding,
                         index,
                     }| {
                        quote! {
                            ::vulkano::shader::DescriptorIdentifier {
                                set: #set,
                                binding: #binding,
                                index: #index,
                            }
                        }
                    },
                );
                quote! {
                    (
                        #index,
                        [#(#identifiers),*].into_iter().collect(),
                    )
                }
            })
        };
        let stages = {
            let ShaderStages {
                vertex,
                tessellation_control,
                tessellation_evaluation,
                geometry,
                fragment,
                compute,
                raygen,
                any_hit,
                closest_hit,
                miss,
                intersection,
                callable,
            } = stages;

            quote! {
                ::vulkano::shader::ShaderStages {
                    vertex: #vertex,
                    tessellation_control: #tessellation_control,
                    tessellation_evaluation: #tessellation_evaluation,
                    geometry: #geometry,
                    fragment: #fragment,
                    compute: #compute,
                    raygen: #raygen,
                    any_hit: #any_hit,
                    closest_hit: #closest_hit,
                    miss: #miss,
                    intersection: #intersection,
                    callable: #callable,
                }
            }
        };
        let storage_image_atomic = storage_image_atomic.iter();
        let storage_read = storage_read.iter();
        let storage_write = storage_write.iter();

        quote! {
            (
                (#set_num, #binding_num),
                ::vulkano::shader::DescriptorRequirements {
                    descriptor_types: vec![#(#descriptor_types),*],
                    descriptor_count: #descriptor_count,
                    image_format: #image_format,
                    image_multisampled: #image_multisampled,
                    image_scalar_type: #image_scalar_type,
                    image_view_type: #image_view_type,
                    sampler_compare: [#(#sampler_compare),*].into_iter().collect(),
                    sampler_no_unnormalized_coordinates: [#(#sampler_no_unnormalized_coordinates),*].into_iter().collect(),
                    sampler_no_ycbcr_conversion: [#(#sampler_no_ycbcr_conversion),*].into_iter().collect(),
                    sampler_with_images: [#(#sampler_with_images),*].into_iter().collect(),
                    stages: #stages,
                    storage_image_atomic: [#(#storage_image_atomic),*].into_iter().collect(),
                    storage_read: [#(#storage_read),*].into_iter().collect(),
                    storage_write: [#(#storage_write),*].into_iter().collect(),
                },
            ),
        }
    });

    quote! {
        [
            #( #descriptor_requirements )*
        ]
    }
}

fn write_push_constant_requirements(
    push_constant_requirements: &Option<PipelineLayoutPcRange>,
) -> TokenStream {
    match push_constant_requirements {
        Some(PipelineLayoutPcRange {
            offset,
            size,
            stages,
        }) => {
            let stages = {
                let ShaderStages {
                    vertex,
                    tessellation_control,
                    tessellation_evaluation,
                    geometry,
                    fragment,
                    compute,
                    raygen,
                    any_hit,
                    closest_hit,
                    miss,
                    intersection,
                    callable,
                } = stages;

                quote! {
                    ::vulkano::shader::ShaderStages {
                        vertex: #vertex,
                        tessellation_control: #tessellation_control,
                        tessellation_evaluation: #tessellation_evaluation,
                        geometry: #geometry,
                        fragment: #fragment,
                        compute: #compute,
                        raygen: #raygen,
                        any_hit: #any_hit,
                        closest_hit: #closest_hit,
                        miss: #miss,
                        intersection: #intersection,
                        callable: #callable,
                    }
                }
            };

            quote! {
                Some(::vulkano::pipeline::layout::PipelineLayoutPcRange {
                    offset: #offset,
                    size: #size,
                    stages: #stages,
                })
            }
        }
        None => quote! {
            None
        },
    }
}

fn write_specialization_constant_requirements(
    specialization_constant_requirements: &FnvHashMap<u32, SpecializationConstantRequirements>,
) -> TokenStream {
    let specialization_constant_requirements = specialization_constant_requirements
        .into_iter()
        .map(|(&constant_id, reqs)| {
            let SpecializationConstantRequirements { size } = reqs;
            quote! {
                (
                    #constant_id,
                    ::vulkano::shader::SpecializationConstantRequirements {
                        size: #size,
                    },
                ),
            }
        });

    quote! {
        [
            #( #specialization_constant_requirements )*
        ]
    }
}

fn write_interface(interface: &ShaderInterface) -> TokenStream {
    let items = interface.elements().iter().map(
        |ShaderInterfaceEntry {
             location,
             component,
             ty:
                 ShaderInterfaceEntryType {
                     base_type,
                     num_components,
                     num_elements,
                     is_64bit,
                 },
             name,
         }| {
            let base_type = format_ident!("{}", format!("{:?}", base_type));

            quote! {
                ::vulkano::shader::ShaderInterfaceEntry {
                    location: #location,
                    component: #component,
                    ty: ::vulkano::shader::ShaderInterfaceEntryType {
                        base_type: ::vulkano::shader::ShaderScalarType::#base_type,
                        num_components: #num_components,
                        num_elements: #num_elements,
                        is_64bit: #is_64bit,
                    },
                    name: Some(::std::borrow::Cow::Borrowed(#name))
                },
            }
        },
    );

    quote! {
        unsafe {
            ::vulkano::shader::ShaderInterface::new_unchecked(vec![
                #( #items )*
            ])
        }
    }
}
<commit_msg>fix shader macro for geometry stages (#1823)<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use fnv::FnvHashMap;
use proc_macro2::TokenStream;
use vulkano::pipeline::layout::PipelineLayoutPcRange;
use vulkano::shader::spirv::ExecutionModel;
use vulkano::shader::{
    DescriptorIdentifier, DescriptorRequirements, ShaderExecution, ShaderInterfaceEntry,
    ShaderInterfaceEntryType, SpecializationConstantRequirements,
};
use vulkano::shader::{EntryPointInfo, ShaderInterface, ShaderStages};

pub(super) fn write_entry_point(
    name: &str,
    model: ExecutionModel,
    info: &EntryPointInfo,
) -> TokenStream {
    let execution = write_shader_execution(&info.execution);
    let model = syn::parse_str::<syn::Path>(&format!(
        "::vulkano::shader::spirv::ExecutionModel::{:?}",
        model
    ))
    .unwrap();
    let descriptor_requirements = write_descriptor_requirements(&info.descriptor_requirements);
    let push_constant_requirements =
        write_push_constant_requirements(&info.push_constant_requirements);
    let specialization_constant_requirements =
        write_specialization_constant_requirements(&info.specialization_constant_requirements);
    let input_interface = write_interface(&info.input_interface);
    let output_interface = write_interface(&info.output_interface);

    quote! {
        (
            #name.to_owned(),
            #model,
            ::vulkano::shader::EntryPointInfo {
                execution: #execution,
                descriptor_requirements: #descriptor_requirements.into_iter().collect(),
                push_constant_requirements: #push_constant_requirements,
                specialization_constant_requirements: #specialization_constant_requirements.into_iter().collect(),
                input_interface: #input_interface,
                output_interface: #output_interface,
            },
        ),
    }
}

fn write_shader_execution(execution: &ShaderExecution) -> TokenStream {
    match execution {
        ShaderExecution::Vertex => quote! { ::vulkano::shader::ShaderExecution::Vertex },
        ShaderExecution::TessellationControl => {
            quote! { ::vulkano::shader::ShaderExecution::TessellationControl }
        }
        ShaderExecution::TessellationEvaluation => {
            quote! { ::vulkano::shader::ShaderExecution::TessellationEvaluation }
        }
        ShaderExecution::Geometry(::vulkano::shader::GeometryShaderExecution { input }) => {
            let input = format_ident!("{}", format!("{:?}", input));
            quote! {
                ::vulkano::shader::ShaderExecution::Geometry(
                    ::vulkano::shader::GeometryShaderExecution {
                        input: ::vulkano::shader::GeometryShaderInput::#input
                    }
                )
            }
        }
        ShaderExecution::Fragment => quote! { ::vulkano::shader::ShaderExecution::Fragment },
        ShaderExecution::Compute => quote! { ::vulkano::shader::ShaderExecution::Compute },
    }
}

fn write_descriptor_requirements(
    descriptor_requirements: &FnvHashMap<(u32, u32), DescriptorRequirements>,
) -> TokenStream {
    let descriptor_requirements = descriptor_requirements.into_iter().map(|(loc, reqs)| {
        let (set_num, binding_num) = loc;
        let DescriptorRequirements {
            descriptor_types,
            descriptor_count,
            image_format,
            image_multisampled,
            image_scalar_type,
            image_view_type,
            sampler_compare,
            sampler_no_unnormalized_coordinates,
            sampler_no_ycbcr_conversion,
            sampler_with_images,
            stages,
            storage_image_atomic,
            storage_read,
            storage_write,
        } = reqs;

        let descriptor_types = descriptor_types.into_iter().map(|ty| {
            let ident = format_ident!("{}", format!("{:?}", ty));
            quote! { ::vulkano::descriptor_set::layout::DescriptorType::#ident }
        });
        let image_format = match image_format {
            Some(image_format) => {
                let ident = format_ident!("{}", format!("{:?}", image_format));
                quote! { Some(::vulkano::format::Format::#ident) }
            }
            None => quote! { None },
        };
        let image_scalar_type = match image_scalar_type {
            Some(image_scalar_type) => {
                let ident = format_ident!("{}", format!("{:?}", image_scalar_type));
                quote! { Some(::vulkano::shader::ShaderScalarType::#ident) }
            }
            None => quote! { None },
        };
        let image_view_type = match image_view_type {
            Some(image_view_type) => {
                let ident = format_ident!("{}", format!("{:?}", image_view_type));
                quote! { Some(::vulkano::image::view::ImageViewType::#ident) }
            }
            None => quote! { None },
        };
        let sampler_compare = sampler_compare.iter();
        let sampler_no_unnormalized_coordinates = sampler_no_unnormalized_coordinates.iter();
        let sampler_no_ycbcr_conversion = sampler_no_ycbcr_conversion.iter();
        let sampler_with_images = {
            sampler_with_images.iter().map(|(&index, identifiers)| {
                let identifiers = identifiers.iter().map(
                    |DescriptorIdentifier {
                         set,
                         binding,
                         index,
                     }| {
                        quote! {
                            ::vulkano::shader::DescriptorIdentifier {
                                set: #set,
                                binding: #binding,
                                index: #index,
                            }
                        }
                    },
                );
                quote! {
                    (
                        #index,
                        [#(#identifiers),*].into_iter().collect(),
                    )
                }
            })
        };
        let stages = {
            let ShaderStages {
                vertex,
                tessellation_control,
                tessellation_evaluation,
                geometry,
                fragment,
                compute,
                raygen,
                any_hit,
                closest_hit,
                miss,
                intersection,
                callable,
            } = stages;

            quote! {
                ::vulkano::shader::ShaderStages {
                    vertex: #vertex,
                    tessellation_control: #tessellation_control,
                    tessellation_evaluation: #tessellation_evaluation,
                    geometry: #geometry,
                    fragment: #fragment,
                    compute: #compute,
                    raygen: #raygen,
                    any_hit: #any_hit,
                    closest_hit: #closest_hit,
                    miss: #miss,
                    intersection: #intersection,
                    callable: #callable,
                }
            }
        };
        let storage_image_atomic = storage_image_atomic.iter();
        let storage_read = storage_read.iter();
        let storage_write = storage_write.iter();

        quote! {
            (
                (#set_num, #binding_num),
                ::vulkano::shader::DescriptorRequirements {
                    descriptor_types: vec![#(#descriptor_types),*],
                    descriptor_count: #descriptor_count,
                    image_format: #image_format,
                    image_multisampled: #image_multisampled,
                    image_scalar_type: #image_scalar_type,
                    image_view_type: #image_view_type,
                    sampler_compare: [#(#sampler_compare),*].into_iter().collect(),
                    sampler_no_unnormalized_coordinates: [#(#sampler_no_unnormalized_coordinates),*].into_iter().collect(),
                    sampler_no_ycbcr_conversion: [#(#sampler_no_ycbcr_conversion),*].into_iter().collect(),
                    sampler_with_images: [#(#sampler_with_images),*].into_iter().collect(),
                    stages: #stages,
                    storage_image_atomic: [#(#storage_image_atomic),*].into_iter().collect(),
                    storage_read: [#(#storage_read),*].into_iter().collect(),
                    storage_write: [#(#storage_write),*].into_iter().collect(),
                },
            ),
        }
    });

    quote! {
        [
            #( #descriptor_requirements )*
        ]
    }
}

fn write_push_constant_requirements(
    push_constant_requirements: &Option<PipelineLayoutPcRange>,
) -> TokenStream {
    match push_constant_requirements {
        Some(PipelineLayoutPcRange {
            offset,
            size,
            stages,
        }) => {
            let stages = {
                let ShaderStages {
                    vertex,
                    tessellation_control,
                    tessellation_evaluation,
                    geometry,
                    fragment,
                    compute,
                    raygen,
                    any_hit,
                    closest_hit,
                    miss,
                    intersection,
                    callable,
                } = stages;

                quote! {
                    ::vulkano::shader::ShaderStages {
                        vertex: #vertex,
                        tessellation_control: #tessellation_control,
                        tessellation_evaluation: #tessellation_evaluation,
                        geometry: #geometry,
                        fragment: #fragment,
                        compute: #compute,
                        raygen: #raygen,
                        any_hit: #any_hit,
                        closest_hit: #closest_hit,
                        miss: #miss,
                        intersection: #intersection,
                        callable: #callable,
                    }
                }
            };

            quote! {
                Some(::vulkano::pipeline::layout::PipelineLayoutPcRange {
                    offset: #offset,
                    size: #size,
                    stages: #stages,
                })
            }
        }
        None => quote! {
            None
        },
    }
}

fn write_specialization_constant_requirements(
    specialization_constant_requirements: &FnvHashMap<u32, SpecializationConstantRequirements>,
) -> TokenStream {
    let specialization_constant_requirements = specialization_constant_requirements
        .into_iter()
        .map(|(&constant_id, reqs)| {
            let SpecializationConstantRequirements { size } = reqs;
            quote! {
                (
                    #constant_id,
                    ::vulkano::shader::SpecializationConstantRequirements {
                        size: #size,
                    },
                ),
            }
        });

    quote! {
        [
            #( #specialization_constant_requirements )*
        ]
    }
}

fn write_interface(interface: &ShaderInterface) -> TokenStream {
    let items = interface.elements().iter().map(
        |ShaderInterfaceEntry {
             location,
             component,
             ty:
                 ShaderInterfaceEntryType {
                     base_type,
                     num_components,
                     num_elements,
                     is_64bit,
                 },
             name,
         }| {
            let base_type = format_ident!("{}", format!("{:?}", base_type));

            quote! {
                ::vulkano::shader::ShaderInterfaceEntry {
                    location: #location,
                    component: #component,
                    ty: ::vulkano::shader::ShaderInterfaceEntryType {
                        base_type: ::vulkano::shader::ShaderScalarType::#base_type,
                        num_components: #num_components,
                        num_elements: #num_elements,
                        is_64bit: #is_64bit,
                    },
                    name: Some(::std::borrow::Cow::Borrowed(#name))
                },
            }
        },
    );

    quote! {
        unsafe {
            ::vulkano::shader::ShaderInterface::new_unchecked(vec![
                #( #items )*
            ])
        }
    }
}
<|endoftext|>


<commit_before>use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use physics::Dimensions;
use cgmath;
use cgmath::{Matrix4, Point3, Vector3};
use specs;
use gfx_app::graphics::load_texture;
use character::gfx_macros::{pipe, VertexData, CharacterSheet, Position};
use game::gfx_macros::Projection;
use game::constants::{ASPECT_RATIO, VIEW_DISTANCE, RUN_SPRITE_OFFSET};
use gfx_app::mouse_controls::MouseInputState;
use terrain::controls::TerrainInputState;
use character::controls::CharacterInputState;
use data;
use character::orientation::{Orientation, Stance};
use character::character::{CharacterSprite, CharacterData};

pub mod gfx_macros;
pub mod character;
pub mod orientation;
pub mod controls;

const SHADER_VERT: &'static [u8] = include_bytes!("character.v.glsl");
const SHADER_FRAG: &'static [u8] = include_bytes!("character.f.glsl");

impl VertexData {
  fn new(pos: [f32; 3], buf_pos: [f32; 2]) -> VertexData {
    VertexData {
      pos,
      buf_pos,
    }
  }
}

#[derive(Debug)]
pub struct CharacterDrawable {
  projection: Projection,
  position: Position,
  orientation: Orientation,
  stance: Stance,
  direction: Orientation,
}

impl CharacterDrawable {
  pub fn new() -> CharacterDrawable {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(0.0, 0.0, VIEW_DISTANCE),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );

    let aspect_ratio: f32 = ASPECT_RATIO;

    CharacterDrawable {
      projection: Projection {
        model: Matrix4::from(view).into(),
        view: view.into(),
        proj: cgmath::perspective(cgmath::Deg(60.0f32), aspect_ratio, 0.1, 4000.0).into(),
      },
      position: Position {
        position: [0.0, 0.0],
      },
      orientation: Orientation::Right,
      stance: Stance::Normal,
      direction: Orientation::Right,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, mouse_input: &mut MouseInputState) {
    self.projection = *world_to_clip;
    let new_position = Position {
      position: [ci.x_movement, ci.y_movement]
    };

    if let Some(_) = mouse_input.left_click_point {
      self.stance = Stance::Firing;
    } else {
      self.stance = Stance::Normal;
    }

    let dx = new_position.position[0] - self.position.position[0];
    let dy = new_position.position[1] - self.position.position[1];
    self.orientation =
    if dx == 0.0 && dy < 0.0       { Orientation::Down }
    else if dx > 0.0 && dy < 0.0   { Orientation::DownRight }
    else if dx < 0.0 && dy < 0.0   { Orientation::DownLeft }
    else if dx == 0.0 && dy == 0.0 { Orientation::Still }
    else if dx > 0.0 && dy == 0.0  { Orientation::Right }
    else if dx < 0.0 && dy == 0.0  { Orientation::Left }
    else if dx == 0.0 && dy > 0.0  { Orientation::Up }
    else if dx > 0.0 && dy > 0.0   { Orientation::UpRight }
    else if dx < 0.0 && dy > 0.0   { Orientation::UpLeft }
    else { unreachable!() };
    self.position = new_position;
  }
}

impl specs::Component for CharacterDrawable {
  type Storage = specs::VecStorage<CharacterDrawable>;
}

pub struct DrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, pipe::Data<R>>,
  data: Vec<CharacterData>,
}

impl<R: gfx::Resources> DrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> DrawSystem<R>
    where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let tilesheet_bytes = &include_bytes!("../../assets/character.png")[..];

    let vertex_data: Vec<VertexData> =
      vec![
        VertexData::new([-20.0, -28.0, 0.0], [0.0, 1.0]),
        VertexData::new([20.0, -28.0, 0.0], [1.0, 1.0]),
        VertexData::new([20.0, 28.0, 0.0], [1.0, 0.0]),
        VertexData::new([-20.0, -28.0, 0.0], [0.0, 1.0]),
        VertexData::new([20.0, 28.0, 0.0], [1.0, 0.0]),
        VertexData::new([-20.0, 28.0, 0.0], [0.0, 0.0]),
      ];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, ());

    let char_texture = load_texture(factory, tilesheet_bytes).unwrap();
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              pipe::new())
      .unwrap();

    let pipeline_data = pipe::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_character();

    DrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data
    }
  }

  fn get_next_sprite(&self, character_idx: usize, mut drawable: &mut CharacterDrawable) -> CharacterSheet {
    let charsheet_total_width = 16128f32;
    let offset = 2.0;

    if drawable.orientation == Orientation::Still && drawable.stance == Stance::Normal {
      let sprite_idx = (drawable.direction as usize * 28 + RUN_SPRITE_OFFSET) as usize;
      let char_sprite = &self.data[sprite_idx];
      let elements_x = charsheet_total_width / (char_sprite.data[2] + offset);
      let char = CharacterSheet {
        div: elements_x,
        index: sprite_idx as f32
      };
      char
    } else if drawable.stance == Stance::Normal {
      drawable.direction = drawable.orientation;
      let sprite_idx = (drawable.orientation as usize * 28 + character_idx + RUN_SPRITE_OFFSET) as usize;
      let char_sprite = &self.data[sprite_idx];
      let elements_x = charsheet_total_width / (char_sprite.data[2] + offset);
      let char = CharacterSheet {
        div: elements_x,
        index: sprite_idx as f32
      };
      char
    } else {
      let idx = (character_idx as f32 * 4.0 / 14.0).floor() as usize;
      let sprite_idx = (drawable.orientation as usize * 8 + idx) as usize;
      let char_sprite = &self.data[sprite_idx];
      let elements_x = charsheet_total_width / (char_sprite.data[2] + offset);
      let char = CharacterSheet {
        div: elements_x,
        index: sprite_idx as f32
      };
      char
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut CharacterDrawable,
                 character: &CharacterSprite,
                 encoder: &mut gfx::Encoder<R, C>)
    where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb, &mut self.get_next_sprite(character.character_idx, &mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<C> specs::System<C> for PreDrawSystem {
  fn run(&mut self, arg: specs::RunArg, _: C) {
    use specs::Join;
    let (mut character, dim, mut terrain_input, mut character_input, mut mouse_input) =
      arg.fetch(|w| (
        w.write::<CharacterDrawable>(),
        w.read_resource::<Dimensions>(),
        w.write::<TerrainInputState>(),
        w.write::<CharacterInputState>(),
        w.write::<MouseInputState>()));

    for (c, i, ci, mi) in (&mut character, &mut terrain_input, &mut character_input, &mut mouse_input).join() {
      let world_to_clip = dim.world_to_projection(i);
      c.update(&world_to_clip, ci, mi);
    }
  }
}
<commit_msg>Refactor get_next_sprite function<commit_after>use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use physics::Dimensions;
use cgmath;
use cgmath::{Matrix4, Point3, Vector3};
use specs;
use gfx_app::graphics::load_texture;
use character::gfx_macros::{pipe, VertexData, CharacterSheet, Position};
use game::gfx_macros::Projection;
use game::constants::{ASPECT_RATIO, VIEW_DISTANCE, RUN_SPRITE_OFFSET};
use gfx_app::mouse_controls::MouseInputState;
use terrain::controls::TerrainInputState;
use character::controls::CharacterInputState;
use data;
use character::orientation::{Orientation, Stance};
use character::character::{CharacterSprite, CharacterData};

pub mod gfx_macros;
pub mod character;
pub mod orientation;
pub mod controls;

const SHADER_VERT: &'static [u8] = include_bytes!("character.v.glsl");
const SHADER_FRAG: &'static [u8] = include_bytes!("character.f.glsl");

impl VertexData {
  fn new(pos: [f32; 3], buf_pos: [f32; 2]) -> VertexData {
    VertexData {
      pos,
      buf_pos,
    }
  }
}

#[derive(Debug)]
pub struct CharacterDrawable {
  projection: Projection,
  position: Position,
  orientation: Orientation,
  stance: Stance,
  direction: Orientation,
}

impl CharacterDrawable {
  pub fn new() -> CharacterDrawable {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(0.0, 0.0, VIEW_DISTANCE),
      Point3::new(0.0, 0.0, 0.0),
      Vector3::unit_y(),
    );

    let aspect_ratio: f32 = ASPECT_RATIO;

    CharacterDrawable {
      projection: Projection {
        model: Matrix4::from(view).into(),
        view: view.into(),
        proj: cgmath::perspective(cgmath::Deg(60.0f32), aspect_ratio, 0.1, 4000.0).into(),
      },
      position: Position {
        position: [0.0, 0.0],
      },
      orientation: Orientation::Right,
      stance: Stance::Normal,
      direction: Orientation::Right,
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, mouse_input: &mut MouseInputState) {
    self.projection = *world_to_clip;
    let new_position = Position {
      position: [ci.x_movement, ci.y_movement]
    };

    if let Some(_) = mouse_input.left_click_point {
      self.stance = Stance::Firing;
    } else {
      self.stance = Stance::Normal;
    }

    let dx = new_position.position[0] - self.position.position[0];
    let dy = new_position.position[1] - self.position.position[1];
    self.orientation =
      if dx == 0.0 && dy < 0.0 { Orientation::Down } else if dx > 0.0 && dy < 0.0 { Orientation::DownRight } else if dx < 0.0 && dy < 0.0 { Orientation::DownLeft } else if dx == 0.0 && dy == 0.0 { Orientation::Still } else if dx > 0.0 && dy == 0.0 { Orientation::Right } else if dx < 0.0 && dy == 0.0 { Orientation::Left } else if dx == 0.0 && dy > 0.0 { Orientation::Up } else if dx > 0.0 && dy > 0.0 { Orientation::UpRight } else if dx < 0.0 && dy > 0.0 { Orientation::UpLeft } else { unreachable!() };
    self.position = new_position;
  }
}

impl specs::Component for CharacterDrawable {
  type Storage = specs::VecStorage<CharacterDrawable>;
}

pub struct DrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, pipe::Data<R>>,
  data: Vec<CharacterData>,
}

impl<R: gfx::Resources> DrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> DrawSystem<R>
    where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let tilesheet_bytes = &include_bytes!("../../assets/character.png")[..];

    let vertex_data: Vec<VertexData> =
      vec![
        VertexData::new([-20.0, -28.0, 0.0], [0.0, 1.0]),
        VertexData::new([20.0, -28.0, 0.0], [1.0, 1.0]),
        VertexData::new([20.0, 28.0, 0.0], [1.0, 0.0]),
        VertexData::new([-20.0, -28.0, 0.0], [0.0, 1.0]),
        VertexData::new([20.0, 28.0, 0.0], [1.0, 0.0]),
        VertexData::new([-20.0, 28.0, 0.0], [0.0, 0.0]),
      ];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data, ());

    let char_texture = load_texture(factory, tilesheet_bytes).unwrap();
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              pipe::new())
      .unwrap();

    let pipeline_data = pipe::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_character();

    DrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data
    }
  }

  fn get_next_sprite(&self, character_idx: usize, mut drawable: &mut CharacterDrawable) -> CharacterSheet {
    let charsheet_total_width = 16128f32;
    let offset = 2.0;

    let char_sprite = if drawable.orientation == Orientation::Still && drawable.stance == Stance::Normal {
      let sprite_idx = (drawable.direction as usize * 28 + RUN_SPRITE_OFFSET) as usize;
      (&self.data[sprite_idx], sprite_idx)
    } else if drawable.stance == Stance::Normal {
      drawable.direction = drawable.orientation;
      let sprite_idx = (drawable.orientation as usize * 28 + character_idx + RUN_SPRITE_OFFSET) as usize;
      (&self.data[sprite_idx], sprite_idx)
    } else {
      let idx = (character_idx as f32 * 4.0 / 14.0).floor() as usize;
      let sprite_idx = (drawable.orientation as usize * 8 + idx) as usize;
      (&self.data[sprite_idx], sprite_idx)
    };

    let elements_x = charsheet_total_width / (char_sprite.0.data[2] + offset);
    let char = CharacterSheet {
      div: elements_x,
      index: char_sprite.1 as f32
    };
    char
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut CharacterDrawable,
                 character: &CharacterSprite,
                 encoder: &mut gfx::Encoder<R, C>)
    where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb, &mut self.get_next_sprite(character.character_idx, &mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<C> specs::System<C> for PreDrawSystem {
  fn run(&mut self, arg: specs::RunArg, _: C) {
    use specs::Join;
    let (mut character, dim, mut terrain_input, mut character_input, mut mouse_input) =
      arg.fetch(|w| (
        w.write::<CharacterDrawable>(),
        w.read_resource::<Dimensions>(),
        w.write::<TerrainInputState>(),
        w.write::<CharacterInputState>(),
        w.write::<MouseInputState>()));

    for (c, i, ci, mi) in (&mut character, &mut terrain_input, &mut character_input, &mut mouse_input).join() {
      let world_to_clip = dim.world_to_projection(i);
      c.update(&world_to_clip, ci, mi);
    }
  }
}
<|endoftext|>


<commit_before>// Copyright 2018 Grove Enterprises LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs::File;
use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::time::Duration;
use std::thread;

extern crate clap;
extern crate datafusion;
extern crate etcd;
extern crate futures;
extern crate futures_timer;
extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate tokio_core;
extern crate uuid;

use clap::{Arg, App};
use etcd::Client;
use etcd::kv;
use datafusion::exec::*;
use futures::future::{ok, loop_fn, Future, Loop};
use futures::Stream;
use hyper::{Method, StatusCode, Chunk};
use hyper::client::HttpConnector;
use hyper::header::{ContentLength};
use hyper::server::{Http, Request, Response, Service};
use tokio_core::reactor::Core;
use uuid::Uuid;

fn main() {

    let matches = App::new("DataFusion Worker Node")
        .version("0.1.4") //TODO get dynamically based on crate version
        .arg(Arg::with_name("ETCD")
            .help("etcd endpoints")
            .short("e")
            .long("etcd")
            .value_name("URL")

            .takes_value(true))
        .arg(Arg::with_name("BIND")
            .short("b")
            .long("bind")
            .help("IP address and port to bind to")
            .default_value("0.0.0.0:8080")
            .takes_value(true))
        .arg(Arg::with_name("WEBROOT")
            .short("w")
            .long("webroot")
            .help("Location of HTML files")
            .default_value("./src/bin/worker/")
            .takes_value(true))
        .get_matches();

    let uuid = Uuid::new_v5(&uuid::NAMESPACE_DNS, "datafusion");
    let bind_addr_str = matches.value_of("BIND").unwrap().to_string();

    let bind_addr = bind_addr_str.parse().unwrap();
    let www_root = matches.value_of("WEBROOT").unwrap().to_string();

    let etcd_endpoints = matches.value_of("ETCD").unwrap();

    // create futures event loop
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    println!("Worker {} listening on {} and serving content from {}", uuid, bind_addr, www_root);

    let _ = Http::new()
        .bind(&bind_addr, move|| Ok(Worker { www_root: www_root.clone() })).unwrap();

    // start a loop to register with etcd every 5 seconds with a ttl of 10 seconds
    let etcd = Client::new(&handle, &[etcd_endpoints], None).unwrap();
    let heartbeat_loop = loop_fn(Membership::new(etcd, uuid, bind_addr_str), |client| {
        client.register()
            .and_then(|(client, done)| {
                if done {
                    Ok(Loop::Break(client))
                } else {
                    Ok(Loop::Continue(client))
                }
            })
    });
    core.run(heartbeat_loop).unwrap();

}

/// Worker struct to store state
struct Worker {
    www_root: String
}

impl Worker {

    fn load_static_file(&self, filename: &str) -> String {

        let mut f = File::open(&filename).expect("file not found");

        let mut contents = String::new();
        f.read_to_string(&mut contents)
            .expect("something went wrong reading the file");

        contents
    }

}

fn handle_request(chunk: Chunk) -> Response {

    println!("handle_request()");

    // collect the entire body
    let body_bytes = chunk.iter()
        .cloned()
        .collect::<Vec<u8>>();

    match String::from_utf8(body_bytes.clone()) {
        Ok(json_str) => {
            println!("Received request");
            //println!("Request: {}", json_str);

            // this is a crude POC that demonstrates the worker receiving a plan, executing it,
            // and returning a result set

            //TODO: should stream results to client, not build a result set in memory
            //TODO: decide on a more appropriate format to return than csv
            //TODO: should not create a new execution context each time

            match serde_json::from_str(&json_str) {
                Ok(plan) => {
                    //println!("Plan: {:?}", plan);

                    // create execution context
                    let mut ctx = ExecutionContext::new();

                    match plan {
                        ExecutionPlan::Interactive { plan } => {
                            match ctx.create_execution_plan(&plan) {
                                Ok(exec) => {
                                    let it = exec.scan(&ctx);
                                    let mut result_set = "".to_string();

                                    it.for_each(|t| {
                                        match t {
                                            Ok(tuple) => {
                                                result_set += &tuple.to_string()
                                            },
                                            Err(e) => {
                                                result_set += &format!("ERROR: {:?}", e)
                                            }
                                        }
                                        result_set += "\n";
                                    });

                                    Response::new()
                                        .with_status(StatusCode::Ok)
                                        .with_header(ContentLength(result_set.len() as u64))
                                        .with_body(result_set)
                                },
                                Err(e) => {
                                    let msg = format!("Failed to create execution plan: {:?}", e);
                                    Response::new()
                                        .with_status(StatusCode::BadRequest)
                                        .with_header(ContentLength(msg.len() as u64))
                                        .with_body(msg)
                                }
                            }

                        },
                        _ => {
                            let msg = format!("Unsupported execution plan");
                            Response::new()
                                .with_status(StatusCode::BadRequest)
                                .with_header(ContentLength(msg.len() as u64))
                                .with_body(msg)
                        }
                    }

                },
                Err(e) => {
                    let msg = format!("Failed to parse execution plan: {:?}", e);
                    Response::new()
                        .with_status(StatusCode::BadRequest)
                        .with_header(ContentLength(msg.len() as u64))
                        .with_body(msg)

                }
            }

        },
        _ => {
            let msg = format!("Failed to parse request JSON");
            Response::new()
                .with_status(StatusCode::BadRequest)
                .with_header(ContentLength(msg.len() as u64))
                .with_body(msg)
        }
    }
}

impl Service for Worker {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;

    fn call(&self, req: Request) -> Self::Future {

        match req.method() {
            &Method::Get => { // all UI calls are GET

                //println!("path={:?}", req.path());

                // is this a known file and/or valid path?
                let filename = match req.path() {
                    "/" => Some("/index.html"),
                    "/css/main.css" => Some("/css/main.css"),
                    _ => None
                };

                // server page, or a 404 not found error
                match filename {
                    Some(f) => {
                        let fqpath = format!("{}/{}", self.www_root, f);
                        let content = self.load_static_file(&fqpath);
                        Box::new(futures::future::ok(
                            Response::new()
                                .with_header(ContentLength(content.len() as u64))
                                .with_body(content)))
                    }
                    _ => {
                        let fqpath = format!("{}/{}", self.www_root, "/404.html");
                        let content = self.load_static_file(&fqpath);
                        Box::new(futures::future::ok(
                            Response::new()
                                .with_status(StatusCode::NotFound)
                                .with_header(ContentLength(content.len() as u64))
                                .with_body(content)))
                    }
                }
            }
            &Method::Post => { // all REST calls are POST
                Box::new(
                    req.body()
                        .concat2()
                        .map(handle_request)
                )
            }
            _ => {
                Box::new(futures::future::ok(
                    Response::new().with_status(StatusCode::NotFound)
                ))
            }

        }
    }

}

struct Membership {
    etcd: Client<HttpConnector>,
    uuid: Uuid,
    bind_address: String
}

impl Membership {

    fn new(etcd: Client<HttpConnector>, uuid: Uuid, bind_address: String) -> Self {
        Membership { etcd, uuid, bind_address }
    }

    fn register(self) -> Box<Future<Item=(Self,bool),Error=Error>> {

        let key = format!("/datafusion/workers/{}", self.uuid);

        Box::new(kv::set(&self.etcd, &key, &self.bind_address, Some(10))
            .and_then(|etcd_response| {
                println!("Registered OK: {:?}", etcd_response);
                thread::sleep(Duration::from_millis(5000));
                ok((self,false))
            })
            .map_err(|_| Error::from(ErrorKind::NotFound)))
    }

}

<commit_msg>clean up error handling in worker<commit_after>// Copyright 2018 Grove Enterprises LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fs::File;
use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::time::Duration;
use std::thread;

extern crate clap;
extern crate datafusion;
extern crate etcd;
extern crate futures;
extern crate futures_timer;
extern crate hyper;
extern crate serde;
extern crate serde_json;
extern crate tokio_core;
extern crate uuid;

use clap::{Arg, App};
use etcd::Client;
use etcd::kv;
use datafusion::exec::*;
use futures::future::{ok, loop_fn, Future, Loop};
use futures::Stream;
use hyper::{Method, StatusCode, Chunk};
use hyper::client::HttpConnector;
use hyper::header::{ContentLength};
use hyper::server::{Http, Request, Response, Service};
use tokio_core::reactor::Core;
use uuid::Uuid;

fn main() {

    let matches = App::new("DataFusion Worker Node")
        .version("0.1.4") //TODO get dynamically based on crate version
        .arg(Arg::with_name("ETCD")
            .help("etcd endpoints")
            .short("e")
            .long("etcd")
            .value_name("URL")
            .required(true)
            .takes_value(true))
        .arg(Arg::with_name("BIND")
            .short("b")
            .long("bind")
            .help("IP address and port to bind to")
            .default_value("0.0.0.0:8080")
            .takes_value(true))
        .arg(Arg::with_name("WEBROOT")
            .short("w")
            .long("webroot")
            .help("Location of HTML files")
            .default_value("./src/bin/worker/")
            .takes_value(true))
        .get_matches();

    let uuid = Uuid::new_v5(&uuid::NAMESPACE_DNS, "datafusion");
    let bind_addr_str = matches.value_of("BIND").unwrap().to_string();

    let bind_addr = bind_addr_str.parse().unwrap();
    let www_root = matches.value_of("WEBROOT").unwrap().to_string();

    let etcd_endpoints = matches.value_of("ETCD").unwrap();

    // create futures event loop
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    println!("Worker {} listening on {} and serving content from {}", uuid, bind_addr, www_root);

    let _ = Http::new()
        .bind(&bind_addr, move|| Ok(Worker { www_root: www_root.clone() })).unwrap();

    // start a loop to register with etcd every 5 seconds with a ttl of 10 seconds
    match Client::new(&handle, &[etcd_endpoints], None) {
        Ok(etcd) => {
            let heartbeat_loop = loop_fn(Membership::new(etcd, uuid, bind_addr_str), |client| {
                client.register()
                    .and_then(|(client, done)| {
                        if done {
                            Ok(Loop::Break(client))
                        } else {
                            Ok(Loop::Continue(client))
                        }
                    })
            });
            match core.run(heartbeat_loop) {
                Ok(_) => println!("Heartbeat loop finished"),
                Err(e) => println!("Heartbeat loop failed: {:?}", e),
            }

        }
        Err(e) => println!("Failed to connect to etcd: {:?}", e)
    }

}

/// Worker struct to store state
struct Worker {
    www_root: String
}

impl Worker {

    fn load_static_file(&self, filename: &str) -> String {

        let mut f = File::open(&filename).expect("file not found");

        let mut contents = String::new();
        f.read_to_string(&mut contents)
            .expect("something went wrong reading the file");

        contents
    }

}

fn handle_request(chunk: Chunk) -> Response {

    println!("handle_request()");

    // collect the entire body
    let body_bytes = chunk.iter()
        .cloned()
        .collect::<Vec<u8>>();

    match String::from_utf8(body_bytes.clone()) {
        Ok(json_str) => {
            println!("Received request");
            //println!("Request: {}", json_str);

            // this is a crude POC that demonstrates the worker receiving a plan, executing it,
            // and returning a result set

            //TODO: should stream results to client, not build a result set in memory
            //TODO: decide on a more appropriate format to return than csv
            //TODO: should not create a new execution context each time

            match serde_json::from_str(&json_str) {
                Ok(plan) => {
                    //println!("Plan: {:?}", plan);

                    // create execution context
                    let mut ctx = ExecutionContext::new();

                    match plan {
                        ExecutionPlan::Interactive { plan } => {
                            match ctx.create_execution_plan(&plan) {
                                Ok(exec) => {
                                    let it = exec.scan(&ctx);
                                    let mut result_set = "".to_string();

                                    it.for_each(|t| {
                                        match t {
                                            Ok(tuple) => {
                                                result_set += &tuple.to_string()
                                            },
                                            Err(e) => {
                                                result_set += &format!("ERROR: {:?}", e)
                                            }
                                        }
                                        result_set += "\n";
                                    });

                                    Response::new()
                                        .with_status(StatusCode::Ok)
                                        .with_header(ContentLength(result_set.len() as u64))
                                        .with_body(result_set)
                                },
                                Err(e) => {
                                    let msg = format!("Failed to create execution plan: {:?}", e);
                                    Response::new()
                                        .with_status(StatusCode::BadRequest)
                                        .with_header(ContentLength(msg.len() as u64))
                                        .with_body(msg)
                                }
                            }

                        },
                        _ => {
                            let msg = format!("Unsupported execution plan");
                            Response::new()
                                .with_status(StatusCode::BadRequest)
                                .with_header(ContentLength(msg.len() as u64))
                                .with_body(msg)
                        }
                    }

                },
                Err(e) => {
                    let msg = format!("Failed to parse execution plan: {:?}", e);
                    Response::new()
                        .with_status(StatusCode::BadRequest)
                        .with_header(ContentLength(msg.len() as u64))
                        .with_body(msg)

                }
            }

        },
        _ => {
            let msg = format!("Failed to parse request JSON");
            Response::new()
                .with_status(StatusCode::BadRequest)
                .with_header(ContentLength(msg.len() as u64))
                .with_body(msg)
        }
    }
}

impl Service for Worker {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;

    fn call(&self, req: Request) -> Self::Future {

        match req.method() {
            &Method::Get => { // all UI calls are GET

                //println!("path={:?}", req.path());

                // is this a known file and/or valid path?
                let filename = match req.path() {
                    "/" => Some("/index.html"),
                    "/css/main.css" => Some("/css/main.css"),
                    _ => None
                };

                // server page, or a 404 not found error
                match filename {
                    Some(f) => {
                        let fqpath = format!("{}/{}", self.www_root, f);
                        let content = self.load_static_file(&fqpath);
                        Box::new(futures::future::ok(
                            Response::new()
                                .with_header(ContentLength(content.len() as u64))
                                .with_body(content)))
                    }
                    _ => {
                        let fqpath = format!("{}/{}", self.www_root, "/404.html");
                        let content = self.load_static_file(&fqpath);
                        Box::new(futures::future::ok(
                            Response::new()
                                .with_status(StatusCode::NotFound)
                                .with_header(ContentLength(content.len() as u64))
                                .with_body(content)))
                    }
                }
            }
            &Method::Post => { // all REST calls are POST
                Box::new(
                    req.body()
                        .concat2()
                        .map(handle_request)
                )
            }
            _ => {
                Box::new(futures::future::ok(
                    Response::new().with_status(StatusCode::NotFound)
                ))
            }

        }
    }

}

struct Membership {
    etcd: Client<HttpConnector>,
    uuid: Uuid,
    bind_address: String
}

impl Membership {

    fn new(etcd: Client<HttpConnector>, uuid: Uuid, bind_address: String) -> Self {
        Membership { etcd, uuid, bind_address }
    }

    fn register(self) -> Box<Future<Item=(Self,bool),Error=Error>> {

        let key = format!("/datafusion/workers/{}", self.uuid);

        Box::new(kv::set(&self.etcd, &key, &self.bind_address, Some(10))
            .and_then(|etcd_response| {
                println!("Registered OK: {:?}", etcd_response);
                thread::sleep(Duration::from_millis(5000));
                ok((self,false))
            })
            .map_err(|_| Error::from(ErrorKind::NotFound)))
    }

}

<|endoftext|>


<commit_before>// Matrix properties checks.
use approx::RelativeEq;
use num::{One, Zero};

use alga::general::{ClosedAdd, ClosedMul, Real};

use base::allocator::Allocator;
use base::dimension::{Dim, DimMin};
use base::storage::Storage;
use base::{DefaultAllocator, Matrix, Scalar, SquareMatrix};

impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
    /// Indicates if this is a square matrix.
    #[inline]
    pub fn is_empty(&self) -> bool {
        let (nrows, ncols) = self.shape();
        nrows == 0 || ncols == 0
    }

    /// Indicates if this is a square matrix.
    #[inline]
    pub fn is_square(&self) -> bool {
        let (nrows, ncols) = self.shape();
        nrows == ncols
    }

    // FIXME: RelativeEq prevents us from using those methods on integer matrices…
    /// Indicated if this is the identity matrix within a relative error of `eps`.
    ///
    /// If the matrix is diagonal, this checks that diagonal elements (i.e. at coordinates `(i, i)`
    /// for i from `0` to `min(R, C)`) are equal one; and that all other elements are zero.
    #[inline]
    pub fn is_identity(&self, eps: N::Epsilon) -> bool
    where
        N: Zero + One + RelativeEq,
        N::Epsilon: Copy,
    {
        let (nrows, ncols) = self.shape();
        let d;

        if nrows > ncols {
            d = ncols;

            for i in d..nrows {
                for j in 0..ncols {
                    if !relative_eq!(self[(i, j)], N::zero(), epsilon = eps) {
                        return false;
                    }
                }
            }
        } else {
            // nrows <= ncols
            d = nrows;

            for i in 0..nrows {
                for j in d..ncols {
                    if !relative_eq!(self[(i, j)], N::zero(), epsilon = eps) {
                        return false;
                    }
                }
            }
        }

        // Off-diagonal elements of the sub-square matrix.
        for i in 1..d {
            for j in 0..i {
                // FIXME: use unsafe indexing.
                if !relative_eq!(self[(i, j)], N::zero(), epsilon = eps)
                    || !relative_eq!(self[(j, i)], N::zero(), epsilon = eps)
                {
                    return false;
                }
            }
        }

        // Diagonal elements of the sub-square matrix.
        for i in 0..d {
            if !relative_eq!(self[(i, i)], N::one(), epsilon = eps) {
                return false;
            }
        }

        true
    }

    /// Checks that `Mᵀ × M = Id`.
    ///
    /// In this definition `Id` is approximately equal to the identity matrix with a relative error
    /// equal to `eps`.
    #[inline]
    pub fn is_orthogonal(&self, eps: N::Epsilon) -> bool
    where
        N: Zero + One + ClosedAdd + ClosedMul + RelativeEq,
        S: Storage<N, R, C>,
        N::Epsilon: Copy,
        DefaultAllocator: Allocator<N, C, C>,
    {
        (self.tr_mul(self)).is_identity(eps)
    }
}

impl<N: Real, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S>
where
    DefaultAllocator: Allocator<N, D, D>,
{
    /// Checks that this matrix is orthogonal and has a determinant equal to 1.
    #[inline]
    pub fn is_special_orthogonal(&self, eps: N) -> bool
    where
        D: DimMin<D, Output = D>,
        DefaultAllocator: Allocator<(usize, usize), D>,
    {
        self.is_square() && self.is_orthogonal(eps) && self.determinant() > N::zero()
    }

    /// Returns `true` if this matrix is invertible.
    #[inline]
    pub fn is_invertible(&self) -> bool {
        // FIXME: improve this?
        self.clone_owned().try_inverse().is_some()
    }
}
<commit_msg>Fix mismatching description for is_empty<commit_after>// Matrix properties checks.
use approx::RelativeEq;
use num::{One, Zero};

use alga::general::{ClosedAdd, ClosedMul, Real};

use base::allocator::Allocator;
use base::dimension::{Dim, DimMin};
use base::storage::Storage;
use base::{DefaultAllocator, Matrix, Scalar, SquareMatrix};

impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
    /// Indicates if this is an empty matrix.
    #[inline]
    pub fn is_empty(&self) -> bool {
        let (nrows, ncols) = self.shape();
        nrows == 0 || ncols == 0
    }

    /// Indicates if this is a square matrix.
    #[inline]
    pub fn is_square(&self) -> bool {
        let (nrows, ncols) = self.shape();
        nrows == ncols
    }

    // FIXME: RelativeEq prevents us from using those methods on integer matrices…
    /// Indicated if this is the identity matrix within a relative error of `eps`.
    ///
    /// If the matrix is diagonal, this checks that diagonal elements (i.e. at coordinates `(i, i)`
    /// for i from `0` to `min(R, C)`) are equal one; and that all other elements are zero.
    #[inline]
    pub fn is_identity(&self, eps: N::Epsilon) -> bool
    where
        N: Zero + One + RelativeEq,
        N::Epsilon: Copy,
    {
        let (nrows, ncols) = self.shape();
        let d;

        if nrows > ncols {
            d = ncols;

            for i in d..nrows {
                for j in 0..ncols {
                    if !relative_eq!(self[(i, j)], N::zero(), epsilon = eps) {
                        return false;
                    }
                }
            }
        } else {
            // nrows <= ncols
            d = nrows;

            for i in 0..nrows {
                for j in d..ncols {
                    if !relative_eq!(self[(i, j)], N::zero(), epsilon = eps) {
                        return false;
                    }
                }
            }
        }

        // Off-diagonal elements of the sub-square matrix.
        for i in 1..d {
            for j in 0..i {
                // FIXME: use unsafe indexing.
                if !relative_eq!(self[(i, j)], N::zero(), epsilon = eps)
                    || !relative_eq!(self[(j, i)], N::zero(), epsilon = eps)
                {
                    return false;
                }
            }
        }

        // Diagonal elements of the sub-square matrix.
        for i in 0..d {
            if !relative_eq!(self[(i, i)], N::one(), epsilon = eps) {
                return false;
            }
        }

        true
    }

    /// Checks that `Mᵀ × M = Id`.
    ///
    /// In this definition `Id` is approximately equal to the identity matrix with a relative error
    /// equal to `eps`.
    #[inline]
    pub fn is_orthogonal(&self, eps: N::Epsilon) -> bool
    where
        N: Zero + One + ClosedAdd + ClosedMul + RelativeEq,
        S: Storage<N, R, C>,
        N::Epsilon: Copy,
        DefaultAllocator: Allocator<N, C, C>,
    {
        (self.tr_mul(self)).is_identity(eps)
    }
}

impl<N: Real, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S>
where
    DefaultAllocator: Allocator<N, D, D>,
{
    /// Checks that this matrix is orthogonal and has a determinant equal to 1.
    #[inline]
    pub fn is_special_orthogonal(&self, eps: N) -> bool
    where
        D: DimMin<D, Output = D>,
        DefaultAllocator: Allocator<(usize, usize), D>,
    {
        self.is_square() && self.is_orthogonal(eps) && self.determinant() > N::zero()
    }

    /// Returns `true` if this matrix is invertible.
    #[inline]
    pub fn is_invertible(&self) -> bool {
        // FIXME: improve this?
        self.clone_owned().try_inverse().is_some()
    }
}
<|endoftext|>