<commit_before>extern crate regex;

mod matching;
mod support;
mod tokens;
mod lexer;
mod files;
mod parser;
mod symbols;

use std::vec::Vec;
use tokens::*;
use lexer::*;
use parser::*;
use symbols::*;

fn main()
{
    let source_file = "sampleData/BasicArithmatic.java";
    let lex_output_file = "sampleData/output/stable/BasicArithmatic.java.lexed";
    let preprocessed_output_file = "sampleData/output/stable/BasicArithmatic.java.preprocessed";

    let mut tokens: Vec<Token> = lex_file(source_file);

    println!("\n\nFound Tokens:");
    tokens.print_to(lex_output_file, true);

    remove_meta(&mut tokens);
    println!("\n\nPerged Tokens:");
    tokens.print_to(preprocessed_output_file, true);

    let mut symbols_table = SymbolTable::new();
    parse_class(&tokens, 1, &mut symbols_table);

    if tokens[0].value != "class"
    {
        panic!("Unexpected token: {}: {}", tokens[0].value, tokens[0].name);
    }

    let (last_index, asm_string) = compile_class(&tokens, 1);
}

fn parse_class(tokens: &Vec<Token>, start_index: usize, symbols_table: &mut SymbolTable)
{
    let mut static_variables: Vec<(usize, usize)> = Vec::new();
    let mut static_methods: Vec<(usize, usize)> = Vec::new();
    let mut static_classes: Vec<(usize, usize)> = Vec::new();
    let mut non_static_variables: Vec<(usize, usize)> = Vec::new();
    let mut non_static_methods: Vec<(usize, usize)> = Vec::new();
    let mut non_static_classes: Vec<(usize, usize)> = Vec::new();

    println!("\n<------------ Parse Class --------------->");
    let mut min_value = 0;
    let mut skip_amount = 0;

    for (index, token) in tokens.iter().enumerate()
    {
        if min_value != 0
        {
            min_value -= 1;
            continue;
        }
        //Static Variable/Method/Class
        if token.name == "mod.access"
        {
            skip_amount = 3;
            if tokens[index + skip_amount].name.starts_with("operator")
            {
                println!("------Incoming Static Variable Decl!");

                let (low, high) = symbols_table.get_variable_locations(tokens, index + 1);
                symbols_table.static_variables.push((low, high));

                min_value =  low;
                min_value -= index;
            }
            else if tokens[index + 1].name.starts_with("construct")
            {
                if tokens[index + skip_amount].name.starts_with("control")
                {
                    println!("------Incoming Static Class Decl!");
                    min_value = find_outer_ending_brace(tokens, index);
                    static_classes.push((index + 2, min_value));
                    min_value -= index;
                }
                else
                {
                    panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
                }

            }
            else if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Static Method Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                static_methods.push((index + 1, min_value));
                min_value -=  index;
            }
            else
            {
                panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
            }

        }
        //Non-Static Variable/Method/Class
        else if token.name.starts_with("construct")
        {
            skip_amount = 2;
            if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Non-Static Class Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                non_static_classes.push((index + 1, min_value));
                min_value = 0;
            }
            else
            {
                panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
            }
        }
        //Non-Static Variable/Method/Class
        else if token.name == "type"
        {
            skip_amount = 2;

            if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Non-Static Method Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                non_static_methods.push((index, min_value));
                min_value -= index;
                //check for control
            }
            else if tokens[index + skip_amount].name.starts_with("operator")
            {
                println!("------Incoming Non-Static Variable Decl!");
                min_value =  find_next_semicolon(tokens, index);
                non_static_variables.push((index, min_value));
                min_value -= index;
            }
        }
        println!("\tIndex: {} | Token -> {} : {}", index, token.value, token.name );
        //deal with parameters
    }
    println!("\n<---------------- Static Variables --------------->");
    for &(start, end) in symbols_table.static_variables.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Static Classes ----------------->");
    for &(start, end) in static_classes.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Static Methods ----------------->");
    for &(start, end) in static_methods.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Variables --------------->");
    for &(start, end) in non_static_variables.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Classes ----------------->");
    for &(start, end) in non_static_classes.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Methods ----------------->");
    for &(start, end) in non_static_methods.iter()
    {
        println!("Start/End {}/{}", start, end);
    }
    println!("\n");
}

fn compile_class(tokens: &Vec<Token>, start_index: usize) -> (usize, String)
{
    let invalid_types = get_invalid_token_types();
    let invalid_values = get_invalid_token_values();

    let mut plp_string: String = String::new();
    let mut current_index: usize = start_index;

    if tokens[current_index].value != "{" { panic!("Expected '{{' received: {}", tokens[current_index].value); }
    else { current_index += 1; }

    for (index, token) in tokens.iter().enumerate()
    {
        // Handle forward skipping
        if index < current_index { continue; }
        else { current_index = index; }

        // TODO: encapsulate into token_rules.validate(token)
        // Panic! if token type is invalid
        if invalid_types.contains(&token.name)
        {
            panic!("Unsupported token type: {}", token.name);
        }
        // Panic! if token value is invalid
        else if invalid_values.contains(&&*token.value)
        {
            panic!("Unsupported token value: {}", token.value);
        }

        if token.value == "class"
        {
            // parse class body
            let (end_index, compiled_class) = compile_class(&tokens, index + 1);
            plp_string.push_str(&*compiled_class);

            current_index = end_index;
        }
        else if token.name == "type" // || token.name == "identifier"
        {
            // look ahead
            // parse variable declaration
            // OR parse method declaration
        }
        else if token.value == "if"
        {
            // parse if body
            // Unsupported for now
        }
        else if token.name == "construct.conditional"
        {
            // parse conditional
            // Unsupported for now
        }
        else
        {
            panic!("Unexpected token: {}\t{}", token.name, token.value);
        }
    }

    (current_index + 1, plp_string)
}

fn remove_meta(tokens: &mut Vec<Token>)
{
    // Indecies of tokens vector to be removed
    let mut invalid_indecies: Vec<usize> = Vec::new();

    let mut min_index: usize = 0;
    for (index, token) in tokens.iter().enumerate()
    {
        // Allow forward skipping (by setting min_index)
        if index < min_index
        {
            invalid_indecies.push(index);
        }
        // Remove imports
        else if token.name == "special.import"
        {
            invalid_indecies.push(index);
            min_index = index + 2; // skip the next token (semi-colon)
        }
        // Remove comments
        else if token.name.starts_with("comment")
        {
            invalid_indecies.push(index);
        }
        // Remove all modifiers (public, private, volatile, transient, static, etc.)
        else if token.name.starts_with("mod")
        {
            if token.name.ends_with("access")
            {
                continue;
            }
            invalid_indecies.push(index);
        }
        // Remove package declarations
        else if token.name == "special.package"
        {
            if token.name.ends_with("package")
            {
                continue;
            }
            invalid_indecies.push(index);
            min_index = find_next_semicolon(tokens, index) + 1;
        }
    }

    // count := how many indecies have already been removed
    // index := the index in the original tokens vector that should be removed
    for (count, index) in invalid_indecies.iter().enumerate()
    {
        // "index" refers to the index before any others were removed. Therefore, it must be offset by the number of removed tokens
        tokens.remove(index - count);
    }
}

fn compile_arithmetic_statement(tokens: &Vec<Token>, start: usize, temp_registers: (&str, &str), var_registers: (&str, &str), load_registers: (&str, &str)) -> (String, usize)
{
    let mut compiled_code = String::new();
    if tokens[start].value.starts_with("literal")
    {
        compiled_code.push_str("li ");
        compiled_code.push_str(temp_registers.0);
        compiled_code.push_str(", ");
        compiled_code.push_str(&*tokens[start].value);
        compiled_code.push_str("\n");
    }

    let mut index = start;
    while tokens[index].value != ";"
    {
        while tokens[index].name == "control" // ignore parenthesis
        {
            index += 1;
        }
        if !tokens[index].name.starts_with("operator")
        {
            let current_token = &tokens[index];
            panic!("Unexpected token while parsing arithmetic statement: {}: {}", current_token.value, current_token.name);
        }

        let operator = &tokens[index];
        while tokens[index].name == "control" // ignore parenthesis
        {
            index += 1;
        }
        let operand = &tokens[index + 1];
        if operand.name.starts_with("literal")
        {
            // TODO: use immediate operators
            compiled_code.push_str("li ");
            compiled_code.push_str(temp_registers.1);
            compiled_code.push_str(", ");
            compiled_code.push_str(&*operand.value);
            compiled_code.push_str("\n");
        }
        else if operand.name.starts_with("identifier")
        {
            // TODO: lookup memory location from symbols table
        }

        let line = compile_arithmetic_operation(operator, temp_registers, temp_registers.0);
        compiled_code.push_str(&*line);
        index += 2;
    }

    (compiled_code, index + 1)
}

fn compile_arithmetic_operation(operator: &Token, operands: (&str, &str), result_register: &str) -> String
{
    let mut compiled_code = String::new();

    if operator.name != "operator.binary"
    {
        panic!("Unsupported operator: {}: {}", operator.name, operator.value);
    }
    else if operator.value == "+"
    {
        compiled_code.push_str("addu ");
    }
    else if operator.value == "-"
    {
        compiled_code.push_str("subu ");
    }
    else if operator.value == "*"
    {
        compiled_code.push_str("mullo ");
    }
    else
    {
        panic!("Unsupported operator: {}: {}", operator.name, operator.value);
    }

    compiled_code.push_str(result_register);
    compiled_code.push_str(", ");
    compiled_code.push_str(operands.0);
    compiled_code.push_str(", ");
    compiled_code.push_str(operands.1);
    compiled_code.push_str("\n");

    compiled_code
}

fn find_next_semicolon(tokens: &Vec<Token>, start: usize) -> usize
{
    for (index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == ";" { return index + start; }
    }

    return 0;
}

fn find_outer_ending_brace(tokens: &Vec<Token>, start: usize) -> usize
{
    let mut open_braces = 0;

    for(index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == "{"
        {
            open_braces += 1;
        }
        else if token.value == "}"
        {
            open_braces -= 1;
            if open_braces == 0
            {
                return index + start;
            }
        }
    }

    return 0;
}
<commit_msg>Refactor: more relevant argument names<commit_after>extern crate regex;

mod matching;
mod support;
mod tokens;
mod lexer;
mod files;
mod parser;
mod symbols;

use std::vec::Vec;
use tokens::*;
use lexer::*;
use parser::*;
use symbols::*;

fn main()
{
    let source_file = "sampleData/BasicArithmatic.java";
    let lex_output_file = "sampleData/output/stable/BasicArithmatic.java.lexed";
    let preprocessed_output_file = "sampleData/output/stable/BasicArithmatic.java.preprocessed";

    let mut tokens: Vec<Token> = lex_file(source_file);

    println!("\n\nFound Tokens:");
    tokens.print_to(lex_output_file, true);

    remove_meta(&mut tokens);
    println!("\n\nPerged Tokens:");
    tokens.print_to(preprocessed_output_file, true);

    let mut symbols_table = SymbolTable::new();
    parse_class(&tokens, 1, &mut symbols_table);

    if tokens[0].value != "class"
    {
        panic!("Unexpected token: {}: {}", tokens[0].value, tokens[0].name);
    }

    let (last_index, asm_string) = compile_class(&tokens, 1);
}

fn parse_class(tokens: &Vec<Token>, start_index: usize, symbols_table: &mut SymbolTable)
{
    let mut static_variables: Vec<(usize, usize)> = Vec::new();
    let mut static_methods: Vec<(usize, usize)> = Vec::new();
    let mut static_classes: Vec<(usize, usize)> = Vec::new();
    let mut non_static_variables: Vec<(usize, usize)> = Vec::new();
    let mut non_static_methods: Vec<(usize, usize)> = Vec::new();
    let mut non_static_classes: Vec<(usize, usize)> = Vec::new();

    println!("\n<------------ Parse Class --------------->");
    let mut min_value = 0;
    let mut skip_amount = 0;

    for (index, token) in tokens.iter().enumerate()
    {
        if min_value != 0
        {
            min_value -= 1;
            continue;
        }
        //Static Variable/Method/Class
        if token.name == "mod.access"
        {
            skip_amount = 3;
            if tokens[index + skip_amount].name.starts_with("operator")
            {
                println!("------Incoming Static Variable Decl!");

                let (low, high) = symbols_table.get_variable_locations(tokens, index + 1);
                symbols_table.static_variables.push((low, high));

                min_value =  low;
                min_value -= index;
            }
            else if tokens[index + 1].name.starts_with("construct")
            {
                if tokens[index + skip_amount].name.starts_with("control")
                {
                    println!("------Incoming Static Class Decl!");
                    min_value = find_outer_ending_brace(tokens, index);
                    static_classes.push((index + 2, min_value));
                    min_value -= index;
                }
                else
                {
                    panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
                }

            }
            else if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Static Method Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                static_methods.push((index + 1, min_value));
                min_value -=  index;
            }
            else
            {
                panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
            }

        }
        //Non-Static Variable/Method/Class
        else if token.name.starts_with("construct")
        {
            skip_amount = 2;
            if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Non-Static Class Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                non_static_classes.push((index + 1, min_value));
                min_value = 0;
            }
            else
            {
                panic!("Unsupported or Unexpected token: {} + {}.", tokens[index + skip_amount].value, tokens[index + skip_amount].name);
            }
        }
        //Non-Static Variable/Method/Class
        else if token.name == "type"
        {
            skip_amount = 2;

            if tokens[index + skip_amount].name.starts_with("control")
            {
                println!("------Incoming Non-Static Method Decl!");
                min_value = find_outer_ending_brace(tokens, index);
                non_static_methods.push((index, min_value));
                min_value -= index;
                //check for control
            }
            else if tokens[index + skip_amount].name.starts_with("operator")
            {
                println!("------Incoming Non-Static Variable Decl!");
                min_value =  find_next_semicolon(tokens, index);
                non_static_variables.push((index, min_value));
                min_value -= index;
            }
        }
        println!("\tIndex: {} | Token -> {} : {}", index, token.value, token.name );
        //deal with parameters
    }
    println!("\n<---------------- Static Variables --------------->");
    for &(start, end) in symbols_table.static_variables.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Static Classes ----------------->");
    for &(start, end) in static_classes.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Static Methods ----------------->");
    for &(start, end) in static_methods.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Variables --------------->");
    for &(start, end) in non_static_variables.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Classes ----------------->");
    for &(start, end) in non_static_classes.iter()
    {
        println!("Start/End {}/{}", start, end);
    }

    println!("\n<---------------- Non-Static Methods ----------------->");
    for &(start, end) in non_static_methods.iter()
    {
        println!("Start/End {}/{}", start, end);
    }
    println!("\n");
}

fn compile_class(tokens: &Vec<Token>, start_index: usize) -> (usize, String)
{
    let invalid_types = get_invalid_token_types();
    let invalid_values = get_invalid_token_values();

    let mut plp_string: String = String::new();
    let mut current_index: usize = start_index;

    if tokens[current_index].value != "{" { panic!("Expected '{{' received: {}", tokens[current_index].value); }
    else { current_index += 1; }

    for (index, token) in tokens.iter().enumerate()
    {
        // Handle forward skipping
        if index < current_index { continue; }
        else { current_index = index; }

        // TODO: encapsulate into token_rules.validate(token)
        // Panic! if token type is invalid
        if invalid_types.contains(&token.name)
        {
            panic!("Unsupported token type: {}", token.name);
        }
        // Panic! if token value is invalid
        else if invalid_values.contains(&&*token.value)
        {
            panic!("Unsupported token value: {}", token.value);
        }

        if token.value == "class"
        {
            // parse class body
            let (end_index, compiled_class) = compile_class(&tokens, index + 1);
            plp_string.push_str(&*compiled_class);

            current_index = end_index;
        }
        else if token.name == "type" // || token.name == "identifier"
        {
            // look ahead
            // parse variable declaration
            // OR parse method declaration
        }
        else if token.value == "if"
        {
            // parse if body
            // Unsupported for now
        }
        else if token.name == "construct.conditional"
        {
            // parse conditional
            // Unsupported for now
        }
        else
        {
            panic!("Unexpected token: {}\t{}", token.name, token.value);
        }
    }

    (current_index + 1, plp_string)
}

fn remove_meta(tokens: &mut Vec<Token>)
{
    // Indecies of tokens vector to be removed
    let mut invalid_indecies: Vec<usize> = Vec::new();

    let mut min_index: usize = 0;
    for (index, token) in tokens.iter().enumerate()
    {
        // Allow forward skipping (by setting min_index)
        if index < min_index
        {
            invalid_indecies.push(index);
        }
        // Remove imports
        else if token.name == "special.import"
        {
            invalid_indecies.push(index);
            min_index = index + 2; // skip the next token (semi-colon)
        }
        // Remove comments
        else if token.name.starts_with("comment")
        {
            invalid_indecies.push(index);
        }
        // Remove all modifiers (public, private, volatile, transient, static, etc.)
        else if token.name.starts_with("mod")
        {
            if token.name.ends_with("access")
            {
                continue;
            }
            invalid_indecies.push(index);
        }
        // Remove package declarations
        else if token.name == "special.package"
        {
            if token.name.ends_with("package")
            {
                continue;
            }
            invalid_indecies.push(index);
            min_index = find_next_semicolon(tokens, index) + 1;
        }
    }

    // count := how many indecies have already been removed
    // index := the index in the original tokens vector that should be removed
    for (count, index) in invalid_indecies.iter().enumerate()
    {
        // "index" refers to the index before any others were removed. Therefore, it must be offset by the number of removed tokens
        tokens.remove(index - count);
    }
}

fn compile_arithmetic_statement(tokens: &Vec<Token>, start: usize, temp_registers: (&str, &str), var_registers: (&str, &str), load_registers: (&str, &str)) -> (String, usize)
{
    let mut compiled_code = String::new();
    if tokens[start].value.starts_with("literal")
    {
        compiled_code.push_str("li ");
        compiled_code.push_str(temp_registers.0);
        compiled_code.push_str(", ");
        compiled_code.push_str(&*tokens[start].value);
        compiled_code.push_str("\n");
    }

    let mut index = start;
    while tokens[index].value != ";"
    {
        while tokens[index].name == "control" // ignore parenthesis
        {
            index += 1;
        }
        if !tokens[index].name.starts_with("operator")
        {
            let current_token = &tokens[index];
            panic!("Unexpected token while parsing arithmetic statement: {}: {}", current_token.value, current_token.name);
        }

        let operator = &tokens[index];
        while tokens[index].name == "control" // ignore parenthesis
        {
            index += 1;
        }
        let operand = &tokens[index + 1];
        if operand.name.starts_with("literal")
        {
            // TODO: use immediate operators
            compiled_code.push_str("li ");
            compiled_code.push_str(temp_registers.1);
            compiled_code.push_str(", ");
            compiled_code.push_str(&*operand.value);
            compiled_code.push_str("\n");
        }
        else if operand.name.starts_with("identifier")
        {
            // TODO: lookup memory location from symbols table
        }

        let line = compile_arithmetic_operation(operator, temp_registers, temp_registers.0);
        compiled_code.push_str(&*line);
        index += 2;
    }

    (compiled_code, index + 1)
}

fn compile_arithmetic_operation(operator: &Token, operand_registers: (&str, &str), result_register: &str) -> String
{
    let mut compiled_code = String::new();

    if operator.name != "operator.binary"
    {
        panic!("Unsupported operator: {}: {}", operator.name, operator.value);
    }
    else if operator.value == "+"
    {
        compiled_code.push_str("addu ");
    }
    else if operator.value == "-"
    {
        compiled_code.push_str("subu ");
    }
    else if operator.value == "*"
    {
        compiled_code.push_str("mullo ");
    }
    else
    {
        panic!("Unsupported operator: {}: {}", operator.name, operator.value);
    }

    compiled_code.push_str(result_register);
    compiled_code.push_str(", ");
    compiled_code.push_str(operand_registers.0);
    compiled_code.push_str(", ");
    compiled_code.push_str(operand_registers.1);
    compiled_code.push_str("\n");

    compiled_code
}

fn find_next_semicolon(tokens: &Vec<Token>, start: usize) -> usize
{
    for (index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == ";" { return index + start; }
    }

    return 0;
}

fn find_outer_ending_brace(tokens: &Vec<Token>, start: usize) -> usize
{
    let mut open_braces = 0;

    for(index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == "{"
        {
            open_braces += 1;
        }
        else if token.value == "}"
        {
            open_braces -= 1;
            if open_braces == 0
            {
                return index + start;
            }
        }
    }

    return 0;
}
<|endoftext|>


<commit_before>use cpp_type::CppType;
use cpp_ffi_data::IndirectionChange;
use utils::JoinWithString;
use utils::CaseOperations;
pub use serializable::RustName;
extern crate libc;

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[allow(dead_code)]
pub enum RustTypeIndirection {
  None,
  Ptr,
  Ref { lifetime: Option<String> },
  PtrPtr,
}

impl RustName {
  pub fn new(parts: Vec<String>) -> RustName {
    assert!(parts.len() > 0);
    RustName { parts: parts }
  }

  pub fn crate_name(&self) -> Option<&String> {
    assert!(self.parts.len() > 0);
    if self.parts.len() > 1 {
      Some(&self.parts[0])
    } else {
      None
    }
  }
  pub fn last_name(&self) -> &String {
    self.parts.last().unwrap()
  }
  pub fn full_name(&self, current_crate: Option<&String>) -> String {
    if current_crate.is_some() && self.crate_name().is_some() &&
       current_crate.unwrap() == self.crate_name().unwrap() {
      format!("::{}", self.parts[1..].join("::"))
    } else {
      self.parts.join("::")
    }
  }

  pub fn includes(&self, other: &RustName) -> bool {
    let extra_modules_count = other.parts.len() - self.parts.len();
    extra_modules_count > 0 && other.parts[0..self.parts.len()] == self.parts[..]
  }

  pub fn includes_directly(&self, other: &RustName) -> bool {
    let extra_modules_count = other.parts.len() - self.parts.len();
    self.includes(other) && extra_modules_count == 1
  }
}

trait ToRustName {
  fn to_rust_name() -> RustName;
}

impl ToRustName for u8 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["u8".to_string()])
  }
}
impl ToRustName for i8 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["i8".to_string()])
  }
}
impl ToRustName for u16 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["u16".to_string()])
  }
}
impl ToRustName for i16 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["i16".to_string()])
  }
}
impl ToRustName for u32 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["u32".to_string()])
  }
}
impl ToRustName for i32 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["i32".to_string()])
  }
}
impl ToRustName for u64 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["u64".to_string()])
  }
}
impl ToRustName for i64 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["i64".to_string()])
  }
}
impl ToRustName for f32 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["f32".to_string()])
  }
}
impl ToRustName for f64 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["f64".to_string()])
  }
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum RustType {
  Void,
  Common {
    base: RustName,
    generic_arguments: Option<Vec<RustType>>,
    is_const: bool,
    is_const2: bool,
    indirection: RustTypeIndirection,
  },
  FunctionPointer {
    return_type: Box<RustType>,
    arguments: Vec<RustType>,
  },
}

impl RustType {
  #[allow(dead_code)]
  pub fn caption(&self) -> String {
    match *self {
      RustType::Void => "void".to_string(),
      RustType::Common { ref base,
                         ref generic_arguments,
                         ref is_const,
                         ref is_const2,
                         ref indirection } => {
        let mut name = base.last_name().to_snake_case();
        if let &Some(ref args) = generic_arguments {
          name = format!("{}_{}", name, args.iter().map(|x| x.caption()).join("_"));
        }
        let mut_text = if *is_const { "" } else { "_mut" };
        match *indirection {
          RustTypeIndirection::None => {}
          RustTypeIndirection::Ref { .. } => {
            name = format!("{}{}_ref", name, mut_text);
          }
          RustTypeIndirection::Ptr => {
            name = format!("{}{}_ptr", name, mut_text);
          }
          RustTypeIndirection::PtrPtr => {
            let mut_text2 = if *is_const2 { "" } else { "_mut" };
            name = format!("{}{}_ptr{}_ptr", name, mut_text, mut_text2);
          }
        }
        name
      }
      RustType::FunctionPointer { .. } => "fn".to_string(),
    }
  }

  #[allow(dead_code)]
  pub fn is_ref(&self) -> bool {
    match *self {
      RustType::Common { ref indirection, .. } => {
        match *indirection {
          RustTypeIndirection::Ref { .. } => true,
          _ => false,
        }
      }
      RustType::Void |
      RustType::FunctionPointer { .. } => false,
    }
  }

  pub fn with_lifetime(&self, new_lifetime: String) -> RustType {
    let mut r = self.clone();
    if let RustType::Common { ref mut indirection, .. } = r {
      if let RustTypeIndirection::Ref { ref mut lifetime } = *indirection {
        assert!(lifetime.is_none());
        *lifetime = Some(new_lifetime);
      }
    }
    r
  }

  pub fn dealias_libc(&self) -> RustType {
    match *self {
      RustType::Void => self.clone(),
      RustType::Common { ref base,
                         ref generic_arguments,
                         ref is_const,
                         ref is_const2,
                         ref indirection } => {
        if base.parts.len() == 2 && &base.parts[0] == "libc" {
          let real_name = match base.parts[1].as_ref() {
            "c_void" => return self.clone(),
            "c_schar" => libc::c_schar::to_rust_name(),
            "c_char" => libc::c_char::to_rust_name(),
            "c_uchar" => libc::c_uchar::to_rust_name(),
            "wchar_t" => libc::wchar_t::to_rust_name(),
            "c_short" => libc::c_short::to_rust_name(),
            "c_ushort" => libc::c_ushort::to_rust_name(),
            "c_int" => libc::c_int::to_rust_name(),
            "c_uint" => libc::c_uint::to_rust_name(),
            "c_long" => libc::c_long::to_rust_name(),
            "c_ulong" => libc::c_ulong::to_rust_name(),
            "c_longlong" => libc::c_longlong::to_rust_name(),
            "c_ulonglong" => libc::c_ulonglong::to_rust_name(),
            "c_float" => libc::c_float::to_rust_name(),
            "c_double" => libc::c_double::to_rust_name(),
            _ => panic!("unknown libc type: {:?}", base),
          };
          RustType::Common {
            base: real_name,
            generic_arguments: generic_arguments.clone(),
            is_const: is_const.clone(),
            is_const2: is_const2.clone(),
            indirection: indirection.clone(),
          }
        } else {
          self.clone()
        }
      }
      RustType::FunctionPointer { ref return_type, ref arguments } => {
        RustType::FunctionPointer {
          return_type: Box::new(return_type.as_ref().dealias_libc()),
          arguments: arguments.iter().map(|arg| arg.dealias_libc()).collect(),
        }
      }
    }
  }
}

#[derive(Debug, Clone, Eq, PartialEq)]
#[allow(dead_code)]
pub enum RustToCTypeConversion {
  None,
  RefToPtr,
  ValueToPtr,
  QFlagsToUInt,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CompleteType {
  pub cpp_type: CppType,
  pub cpp_ffi_type: CppType,
  pub cpp_to_ffi_conversion: IndirectionChange,
  pub rust_ffi_type: RustType,
  pub rust_api_type: RustType,
  pub rust_api_to_c_conversion: RustToCTypeConversion,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RustFFIArgument {
  pub name: String,
  pub argument_type: RustType,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RustFFIFunction {
  pub return_type: RustType,
  pub name: String,
  pub arguments: Vec<RustFFIArgument>,
}
<commit_msg>unsigned bug fix<commit_after>use cpp_type::CppType;
use cpp_ffi_data::IndirectionChange;
use utils::JoinWithString;
use utils::CaseOperations;
pub use serializable::RustName;
extern crate libc;

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[allow(dead_code)]
pub enum RustTypeIndirection {
  None,
  Ptr,
  Ref { lifetime: Option<String> },
  PtrPtr,
}

impl RustName {
  pub fn new(parts: Vec<String>) -> RustName {
    assert!(parts.len() > 0);
    RustName { parts: parts }
  }

  pub fn crate_name(&self) -> Option<&String> {
    assert!(self.parts.len() > 0);
    if self.parts.len() > 1 {
      Some(&self.parts[0])
    } else {
      None
    }
  }
  pub fn last_name(&self) -> &String {
    self.parts.last().unwrap()
  }
  pub fn full_name(&self, current_crate: Option<&String>) -> String {
    if current_crate.is_some() && self.crate_name().is_some() &&
       current_crate.unwrap() == self.crate_name().unwrap() {
      format!("::{}", self.parts[1..].join("::"))
    } else {
      self.parts.join("::")
    }
  }

  pub fn includes(&self, other: &RustName) -> bool {
    let extra_modules_count = other.parts.len() as isize - self.parts.len() as isize;
    extra_modules_count > 0 && other.parts[0..self.parts.len()] == self.parts[..]
  }

  pub fn includes_directly(&self, other: &RustName) -> bool {
    let extra_modules_count = other.parts.len() as isize - self.parts.len() as isize;
    self.includes(other) && extra_modules_count == 1
  }
}

trait ToRustName {
  fn to_rust_name() -> RustName;
}

impl ToRustName for u8 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["u8".to_string()])
  }
}
impl ToRustName for i8 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["i8".to_string()])
  }
}
impl ToRustName for u16 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["u16".to_string()])
  }
}
impl ToRustName for i16 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["i16".to_string()])
  }
}
impl ToRustName for u32 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["u32".to_string()])
  }
}
impl ToRustName for i32 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["i32".to_string()])
  }
}
impl ToRustName for u64 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["u64".to_string()])
  }
}
impl ToRustName for i64 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["i64".to_string()])
  }
}
impl ToRustName for f32 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["f32".to_string()])
  }
}
impl ToRustName for f64 {
  fn to_rust_name() -> RustName {
    RustName::new(vec!["f64".to_string()])
  }
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum RustType {
  Void,
  Common {
    base: RustName,
    generic_arguments: Option<Vec<RustType>>,
    is_const: bool,
    is_const2: bool,
    indirection: RustTypeIndirection,
  },
  FunctionPointer {
    return_type: Box<RustType>,
    arguments: Vec<RustType>,
  },
}

impl RustType {
  #[allow(dead_code)]
  pub fn caption(&self) -> String {
    match *self {
      RustType::Void => "void".to_string(),
      RustType::Common { ref base,
                         ref generic_arguments,
                         ref is_const,
                         ref is_const2,
                         ref indirection } => {
        let mut name = base.last_name().to_snake_case();
        if let &Some(ref args) = generic_arguments {
          name = format!("{}_{}", name, args.iter().map(|x| x.caption()).join("_"));
        }
        let mut_text = if *is_const { "" } else { "_mut" };
        match *indirection {
          RustTypeIndirection::None => {}
          RustTypeIndirection::Ref { .. } => {
            name = format!("{}{}_ref", name, mut_text);
          }
          RustTypeIndirection::Ptr => {
            name = format!("{}{}_ptr", name, mut_text);
          }
          RustTypeIndirection::PtrPtr => {
            let mut_text2 = if *is_const2 { "" } else { "_mut" };
            name = format!("{}{}_ptr{}_ptr", name, mut_text, mut_text2);
          }
        }
        name
      }
      RustType::FunctionPointer { .. } => "fn".to_string(),
    }
  }

  #[allow(dead_code)]
  pub fn is_ref(&self) -> bool {
    match *self {
      RustType::Common { ref indirection, .. } => {
        match *indirection {
          RustTypeIndirection::Ref { .. } => true,
          _ => false,
        }
      }
      RustType::Void |
      RustType::FunctionPointer { .. } => false,
    }
  }

  pub fn with_lifetime(&self, new_lifetime: String) -> RustType {
    let mut r = self.clone();
    if let RustType::Common { ref mut indirection, .. } = r {
      if let RustTypeIndirection::Ref { ref mut lifetime } = *indirection {
        assert!(lifetime.is_none());
        *lifetime = Some(new_lifetime);
      }
    }
    r
  }

  pub fn dealias_libc(&self) -> RustType {
    match *self {
      RustType::Void => self.clone(),
      RustType::Common { ref base,
                         ref generic_arguments,
                         ref is_const,
                         ref is_const2,
                         ref indirection } => {
        if base.parts.len() == 2 && &base.parts[0] == "libc" {
          let real_name = match base.parts[1].as_ref() {
            "c_void" => return self.clone(),
            "c_schar" => libc::c_schar::to_rust_name(),
            "c_char" => libc::c_char::to_rust_name(),
            "c_uchar" => libc::c_uchar::to_rust_name(),
            "wchar_t" => libc::wchar_t::to_rust_name(),
            "c_short" => libc::c_short::to_rust_name(),
            "c_ushort" => libc::c_ushort::to_rust_name(),
            "c_int" => libc::c_int::to_rust_name(),
            "c_uint" => libc::c_uint::to_rust_name(),
            "c_long" => libc::c_long::to_rust_name(),
            "c_ulong" => libc::c_ulong::to_rust_name(),
            "c_longlong" => libc::c_longlong::to_rust_name(),
            "c_ulonglong" => libc::c_ulonglong::to_rust_name(),
            "c_float" => libc::c_float::to_rust_name(),
            "c_double" => libc::c_double::to_rust_name(),
            _ => panic!("unknown libc type: {:?}", base),
          };
          RustType::Common {
            base: real_name,
            generic_arguments: generic_arguments.clone(),
            is_const: is_const.clone(),
            is_const2: is_const2.clone(),
            indirection: indirection.clone(),
          }
        } else {
          self.clone()
        }
      }
      RustType::FunctionPointer { ref return_type, ref arguments } => {
        RustType::FunctionPointer {
          return_type: Box::new(return_type.as_ref().dealias_libc()),
          arguments: arguments.iter().map(|arg| arg.dealias_libc()).collect(),
        }
      }
    }
  }
}

#[derive(Debug, Clone, Eq, PartialEq)]
#[allow(dead_code)]
pub enum RustToCTypeConversion {
  None,
  RefToPtr,
  ValueToPtr,
  QFlagsToUInt,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CompleteType {
  pub cpp_type: CppType,
  pub cpp_ffi_type: CppType,
  pub cpp_to_ffi_conversion: IndirectionChange,
  pub rust_ffi_type: RustType,
  pub rust_api_type: RustType,
  pub rust_api_to_c_conversion: RustToCTypeConversion,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RustFFIArgument {
  pub name: String,
  pub argument_type: RustType,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RustFFIFunction {
  pub return_type: RustType,
  pub name: String,
  pub arguments: Vec<RustFFIArgument>,
}
<|endoftext|>


<commit_before>extern crate image;

mod obj_load;

use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::vec::Vec;

use glium::{IndexBuffer, VertexBuffer, Program, Surface, DrawParameters};
use glium::backend::Facade;
use glium::index::PrimitiveType;
use glium::texture::{RawImage2d, SrgbTexture2d};

use cgmath::prelude::*;
use cgmath::Matrix4;
use cgmath::conv::*;

/// Renderer representation of a vertex
#[derive(Copy, Clone, Debug)]
pub struct Vertex {
    pub position: [f32; 3],
    pub normal: [f32; 3],
    pub tex_coords: [f32; 2]
}

implement_vertex!(Vertex, position, normal, tex_coords);

/// Renderer representation of a material
pub struct Material {
    pub diffuse: [f32; 3],
    pub diffuse_image: Option<image::RgbaImage>,
    pub diffuse_texture: Option<SrgbTexture2d>
}

impl Material {
    /// Create a new material based on a material loaded from the scene file
    fn new(obj_mat: obj_load::Material) -> Material {
        // Create diffuse texture and load it to the GPU
        let diffuse_image = match obj_mat.tex_diffuse {
            Some(tex_path) => Some(Material::load_image(&tex_path)),
            None => None
        };
        Material {
            diffuse: obj_mat.c_diffuse.expect("No diffuse color!"),
            diffuse_image: diffuse_image,
            diffuse_texture: None
        }
    }

    /// Load an image at
    fn load_image(path: &Path) -> image::RgbaImage {
        let tex_reader = BufReader::new(File::open(path).expect("Failed to open image!"));
        image::load(tex_reader, image::PNG).expect("Failed to load image!").to_rgba()
    }

    fn upload_textures<F: Facade>(&mut self, facade: &F) {
        self.diffuse_texture = match self.diffuse_image {
            Some(ref image) => {
                let image_dim = image.dimensions();
                let tex_image = RawImage2d::from_raw_rgba_reversed(image.clone().into_raw(), image_dim);
                Some(SrgbTexture2d::new(facade, tex_image).expect("Failed to upload texture!"))
            }
            None => Some(SrgbTexture2d::empty(facade, 0, 0).expect("Failed to upload empty texture!"))
        }
    }
}

/// Renderer representation of mesh with a common material
pub struct Mesh {
    pub vertices: Vec<Vertex>,
    pub indices: Vec<u32>,
    pub material: Material,
    pub local_to_world: Matrix4<f32>,
    pub vertex_buffer: Option<VertexBuffer<Vertex>>,
    pub index_buffer: Option<IndexBuffer<u32>>
}

impl Mesh {
    fn new(obj_mat: obj_load::Material) -> Mesh {
        Mesh { vertices: Vec::new(),
               indices: Vec::new(),
               material: Material::new(obj_mat),
               local_to_world: Matrix4::identity(),
               vertex_buffer: None,
               index_buffer: None
        }
    }

    /// Load the textures + vertex and index buffers to the GPU
    fn upload_data<F: Facade>(&mut self, facade: &F) {
        self.material.upload_textures(facade);
        self.vertex_buffer = Some(VertexBuffer::new(facade, &self.vertices)
                                  .expect("Failed to create vertex buffer!"));
        self.index_buffer = Some(IndexBuffer::new(facade, PrimitiveType::TrianglesList, &self.indices)
                                 .expect("Failed to create index buffer!"));
    }

    /// Draw this mesh to the target
    pub fn draw<S: Surface>(&self, target: &mut S, program: &Program, draw_parameters: &DrawParameters,
                            world_to_clip: Matrix4<f32>) {
        let uniforms = uniform! {
            local_to_world: array4x4(self.local_to_world),
            world_to_clip: array4x4(world_to_clip),
            u_light: [-1.0, 0.4, 0.9f32],
            u_color: self.material.diffuse,
            u_has_diffuse: self.material.diffuse_image.is_some(),
            tex_diffuse: self.material.diffuse_texture.as_ref().expect("Use of unloaded texture!")
        };
        target.draw(self.vertex_buffer.as_ref().expect("No vertex buffer!"),
                    self.index_buffer.as_ref().expect("No index buffer!"),
                    &program, &uniforms, &draw_parameters).unwrap();
    }
}

/// Renderer representation of a scene
#[derive(Default)]
pub struct Scene {
    pub meshes: Vec<Mesh>,
    pub min: [f32; 3],
    pub max: [f32; 3],
}

impl Scene {
    pub fn get_center(&self) -> [f32; 3] {
        let mut res = [0.0f32; 3];
        for i in 0..2 {
            res[i] = (self.min[i] + self.max[i]) / 2.0;
        }
        res
    }

    pub fn get_size(&self) -> f32 {
        let mut max = 0.0f32;
        for i in 0..2 {
            max = max.max(self.max[i] - self.min[i]);
        }
        max
    }

    fn update_ranges(&mut self, new_pos: [f32; 3]) {
        for i in 0..2 {
            self.min[i] = self.min[i].min(new_pos[i]);
        }
        for i in 0..2 {
            self.max[i] = self.max[i].max(new_pos[i]);
        }
    }

}

/// Load a scene from the given path bind resources to given facade
pub fn load_scene<F: Facade>(scene_path: &Path, facade: &F) -> Scene {
    let mut scene = Scene { .. Default::default() };
    let obj = obj_load::load_obj(scene_path).expect("Failed to load.");

    // Closure to calculate planar normal for a polygon
    let calculate_normal = |polygon: &obj_load::Polygon| -> [f32; 3] {
        let pos_i1 = polygon.index_vertices[0][0].expect("No vertex positions!");
        let pos_i2 = polygon.index_vertices[1][0].expect("No vertex positions!");
        let pos_i3 = polygon.index_vertices[2][0].expect("No vertex positions!");
        let pos_1 = obj.positions[pos_i1];
        let pos_2 = obj.positions[pos_i2];
        let pos_3 = obj.positions[pos_i3];
        let u = [pos_2[0] - pos_1[0],
                 pos_2[1] - pos_1[1],
                 pos_2[2] - pos_1[2]];
        let v = [pos_3[0] - pos_1[0],
                 pos_3[1] - pos_1[1],
                 pos_3[2] - pos_1[2]];
        [u[1]*v[2] - u[2]*v[1],
         u[2]*v[0] - u[0]*v[2],
         u[0]*v[1] - u[1]*v[0]]
    };

    // Group the polygons by materials for easy rendering
    for range in &obj.material_ranges {
        let obj_mat = obj.materials.get(&range.name)
            .expect(&::std::fmt::format(format_args!("Couldn't find material {}!", range.name)));
        let mut mesh = Mesh::new(obj_mat.clone());
        let mut vertex_map = HashMap::new();
        for tri in &obj.polygons[range.start_i..range.end_i] {
            let default_tex_coords= [0.0; 2];
            for index_vertex in &tri.index_vertices {
                match vertex_map.get(index_vertex) {
                    // Vertex has already been added
                    Some(&i) => mesh.indices.push(i),
                    None => {
                        // Add vertex to map
                        vertex_map.insert(index_vertex, mesh.vertices.len() as u32);
                        // Panic if there is no positions
                        let pos_i = index_vertex[0].expect("No vertex positions!");
                        let pos = obj.positions[pos_i];
                        scene.update_ranges(pos);

                        let tex_coords = match index_vertex[1] {
                            Some(tex_coords_i) => obj.tex_coords[tex_coords_i],
                            None => default_tex_coords
                        };
                        let normal = match index_vertex[2] {
                            Some(normal_i) => obj.normals[normal_i],
                            None => calculate_normal(tri)
                        };

                        mesh.indices.push(mesh.vertices.len() as u32);
                        mesh.vertices.push(Vertex { position: pos, normal: normal, tex_coords: tex_coords });
                    }
                }
            }
        }
        if !mesh.vertices.is_empty() {
            mesh.upload_data(facade);
            scene.meshes.push(mesh);
        }
    }
    scene
}
<commit_msg>Avoid cloning loaded materials<commit_after>extern crate image;

mod obj_load;

use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::vec::Vec;

use glium::{IndexBuffer, VertexBuffer, Program, Surface, DrawParameters};
use glium::backend::Facade;
use glium::index::PrimitiveType;
use glium::texture::{RawImage2d, SrgbTexture2d};

use cgmath::prelude::*;
use cgmath::Matrix4;
use cgmath::conv::*;

/// Renderer representation of a vertex
#[derive(Copy, Clone, Debug)]
pub struct Vertex {
    pub position: [f32; 3],
    pub normal: [f32; 3],
    pub tex_coords: [f32; 2]
}

implement_vertex!(Vertex, position, normal, tex_coords);

/// Renderer representation of a material
pub struct Material {
    pub diffuse: [f32; 3],
    pub diffuse_image: Option<image::RgbaImage>,
    pub diffuse_texture: Option<SrgbTexture2d>
}

impl Material {
    /// Create a new material based on a material loaded from the scene file
    fn new(obj_mat: &obj_load::Material) -> Material {
        // Create diffuse texture and load it to the GPU
        let diffuse_image = match obj_mat.tex_diffuse {
            Some(ref tex_path) => Some(Material::load_image(tex_path)),
            None => None
        };
        Material {
            diffuse: obj_mat.c_diffuse.expect("No diffuse color!"),
            diffuse_image: diffuse_image,
            diffuse_texture: None
        }
    }

    /// Load an image at
    fn load_image(path: &Path) -> image::RgbaImage {
        let tex_reader = BufReader::new(File::open(path).expect("Failed to open image!"));
        image::load(tex_reader, image::PNG).expect("Failed to load image!").to_rgba()
    }

    fn upload_textures<F: Facade>(&mut self, facade: &F) {
        self.diffuse_texture = match self.diffuse_image {
            Some(ref image) => {
                let image_dim = image.dimensions();
                let tex_image = RawImage2d::from_raw_rgba_reversed(image.clone().into_raw(), image_dim);
                Some(SrgbTexture2d::new(facade, tex_image).expect("Failed to upload texture!"))
            }
            None => Some(SrgbTexture2d::empty(facade, 0, 0).expect("Failed to upload empty texture!"))
        }
    }
}

/// Renderer representation of mesh with a common material
pub struct Mesh {
    pub vertices: Vec<Vertex>,
    pub indices: Vec<u32>,
    pub material: Material,
    pub local_to_world: Matrix4<f32>,
    pub vertex_buffer: Option<VertexBuffer<Vertex>>,
    pub index_buffer: Option<IndexBuffer<u32>>
}

impl Mesh {
    fn new(obj_mat: &obj_load::Material) -> Mesh {
        Mesh { vertices: Vec::new(),
               indices: Vec::new(),
               material: Material::new(obj_mat),
               local_to_world: Matrix4::identity(),
               vertex_buffer: None,
               index_buffer: None
        }
    }

    /// Load the textures + vertex and index buffers to the GPU
    fn upload_data<F: Facade>(&mut self, facade: &F) {
        self.material.upload_textures(facade);
        self.vertex_buffer = Some(VertexBuffer::new(facade, &self.vertices)
                                  .expect("Failed to create vertex buffer!"));
        self.index_buffer = Some(IndexBuffer::new(facade, PrimitiveType::TrianglesList, &self.indices)
                                 .expect("Failed to create index buffer!"));
    }

    /// Draw this mesh to the target
    pub fn draw<S: Surface>(&self, target: &mut S, program: &Program, draw_parameters: &DrawParameters,
                            world_to_clip: Matrix4<f32>) {
        let uniforms = uniform! {
            local_to_world: array4x4(self.local_to_world),
            world_to_clip: array4x4(world_to_clip),
            u_light: [-1.0, 0.4, 0.9f32],
            u_color: self.material.diffuse,
            u_has_diffuse: self.material.diffuse_image.is_some(),
            tex_diffuse: self.material.diffuse_texture.as_ref().expect("Use of unloaded texture!")
        };
        target.draw(self.vertex_buffer.as_ref().expect("No vertex buffer!"),
                    self.index_buffer.as_ref().expect("No index buffer!"),
                    &program, &uniforms, &draw_parameters).unwrap();
    }
}

/// Renderer representation of a scene
#[derive(Default)]
pub struct Scene {
    pub meshes: Vec<Mesh>,
    pub min: [f32; 3],
    pub max: [f32; 3],
}

impl Scene {
    pub fn get_center(&self) -> [f32; 3] {
        let mut res = [0.0f32; 3];
        for i in 0..2 {
            res[i] = (self.min[i] + self.max[i]) / 2.0;
        }
        res
    }

    pub fn get_size(&self) -> f32 {
        let mut max = 0.0f32;
        for i in 0..2 {
            max = max.max(self.max[i] - self.min[i]);
        }
        max
    }

    fn update_ranges(&mut self, new_pos: [f32; 3]) {
        for i in 0..2 {
            self.min[i] = self.min[i].min(new_pos[i]);
        }
        for i in 0..2 {
            self.max[i] = self.max[i].max(new_pos[i]);
        }
    }

}

/// Load a scene from the given path bind resources to given facade
pub fn load_scene<F: Facade>(scene_path: &Path, facade: &F) -> Scene {
    let mut scene = Scene { .. Default::default() };
    let obj = obj_load::load_obj(scene_path).expect("Failed to load.");

    // Closure to calculate planar normal for a polygon
    let calculate_normal = |polygon: &obj_load::Polygon| -> [f32; 3] {
        let pos_i1 = polygon.index_vertices[0][0].expect("No vertex positions!");
        let pos_i2 = polygon.index_vertices[1][0].expect("No vertex positions!");
        let pos_i3 = polygon.index_vertices[2][0].expect("No vertex positions!");
        let pos_1 = obj.positions[pos_i1];
        let pos_2 = obj.positions[pos_i2];
        let pos_3 = obj.positions[pos_i3];
        let u = [pos_2[0] - pos_1[0],
                 pos_2[1] - pos_1[1],
                 pos_2[2] - pos_1[2]];
        let v = [pos_3[0] - pos_1[0],
                 pos_3[1] - pos_1[1],
                 pos_3[2] - pos_1[2]];
        [u[1]*v[2] - u[2]*v[1],
         u[2]*v[0] - u[0]*v[2],
         u[0]*v[1] - u[1]*v[0]]
    };

    // Group the polygons by materials for easy rendering
    for range in &obj.material_ranges {
        let obj_mat = obj.materials.get(&range.name)
            .expect(&::std::fmt::format(format_args!("Couldn't find material {}!", range.name)));
        let mut mesh = Mesh::new(&obj_mat);
        let mut vertex_map = HashMap::new();
        for tri in &obj.polygons[range.start_i..range.end_i] {
            let default_tex_coords= [0.0; 2];
            for index_vertex in &tri.index_vertices {
                match vertex_map.get(index_vertex) {
                    // Vertex has already been added
                    Some(&i) => mesh.indices.push(i),
                    None => {
                        // Add vertex to map
                        vertex_map.insert(index_vertex, mesh.vertices.len() as u32);
                        // Panic if there is no positions
                        let pos_i = index_vertex[0].expect("No vertex positions!");
                        let pos = obj.positions[pos_i];
                        scene.update_ranges(pos);

                        let tex_coords = match index_vertex[1] {
                            Some(tex_coords_i) => obj.tex_coords[tex_coords_i],
                            None => default_tex_coords
                        };
                        let normal = match index_vertex[2] {
                            Some(normal_i) => obj.normals[normal_i],
                            None => calculate_normal(tri)
                        };

                        mesh.indices.push(mesh.vertices.len() as u32);
                        mesh.vertices.push(Vertex { position: pos, normal: normal, tex_coords: tex_coords });
                    }
                }
            }
        }
        if !mesh.vertices.is_empty() {
            mesh.upload_data(facade);
            scene.meshes.push(mesh);
        }
    }
    scene
}
<|endoftext|>


<commit_before>// Copyright 2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::f64;
use std::iter;
use std::slice;

use num::traits::{cast, NumCast};
use itertools::Itertools;

use stats::logprobs::{self, LogProb};

/// Implementation of a cumulative distribution function.
#[derive(Debug, Clone)]
pub struct CDF<T: PartialOrd> {
    inner: Vec<(T, LogProb)>
}

impl<T: PartialOrd> CDF<T> {
    /// Create CDF from given probability mass function (PMF). The PMF may contain duplicate values
    /// the probabilities of which are summed during generation of the CDF.
    pub fn from_pmf(mut entries: Vec<(T, LogProb)>) -> Self {
        entries.sort_by(|&(ref a, _), &(ref b, _)| a.partial_cmp(b).unwrap());
        let mut inner: Vec<(T, LogProb)> = Vec::new();
        for mut e in entries.into_iter() {
            let p = logprobs::add(inner.last().map_or(f64::NEG_INFINITY, |e| e.1), e.1);
            if !inner.is_empty() && inner.last().unwrap().0 == e.0 {
                inner.last_mut().unwrap().1 = p;
            }
            else {
                e.1 = p;
                inner.push(e);
            }
        }
        let mut cdf = CDF {
            inner: inner
        };

        if relative_eq!(cdf.total_prob(), 0.0) && cdf.total_prob() > 0.0 {
            cdf.inner.last_mut().unwrap().1 = 0.0;
        }

        cdf
    }

    /// Create CDF from iterator. This can be used to replace the values of a CDF.
    pub fn from_cdf<I: Iterator<Item = (T, LogProb)>>(entries: I) -> Self {
        CDF { inner: entries.collect_vec() }
    }

    pub fn reduce(self) -> Self {
        let mut inner = Vec::new();
        let mut last = f64::NEG_INFINITY;
        for e in self.inner.into_iter() {
            if last != e.1 {
                last = e.1;
                inner.push(e);
            }
        }
        CDF { inner: inner }
    }

    pub fn sample(mut self, n: usize) -> Self {
        if self.inner.len() <= n {
            self
        }
        else {
            let s = self.inner.len() / (n - 1);
            let last = self.inner.pop().unwrap();
            let mut inner = self.inner.into_iter().step(s).collect_vec();
            inner.push(last);
            CDF {
                inner: inner
            }
        }
    }

    pub fn iter(&self) -> slice::Iter<(T, f64)>{
        self.inner.iter()
    }

    pub fn iter_pmf<'a>(&'a self) -> CDFPMFIter<'a, T> {
        fn cdf_to_pmf<'a, G>(last_prob: &mut LogProb, e: &'a (G, LogProb)) -> Option<(&'a G, LogProb)> {
            let &(ref value, cdf_prob) = e;
            let prob = logprobs::sub(cdf_prob, *last_prob);
            *last_prob = cdf_prob;
            Some((value, prob))
        }
        self.inner.iter().scan(f64::NEG_INFINITY, cdf_to_pmf)
    }

    /// Get cumulative probability for a given value. If the value is not present,
    /// return the probability of the previous value.
    pub fn get(&self, value: &T) -> Option<LogProb> {
        if self.inner.is_empty() {
            None
        }
        else {
            Some(match self.inner.binary_search_by(|e| e.0.partial_cmp(value).unwrap()) {
                Ok(i) => self.inner[i].1,
                Err(i) => if i > 0 { self.inner[i - 1].1 } else { f64::NEG_INFINITY }
            })
        }
    }

    pub fn get_pmf(&self, value: &T) -> Option<LogProb> {
        if self.inner.is_empty() {
            None
        }
        else {
            Some(match self.inner.binary_search_by(|e| e.0.partial_cmp(value).unwrap()) {
                Ok(i) => if i > 0 { logprobs::sub(self.inner[i].1, self.inner[i - 1].1) } else { self.inner[0].1 },
                Err(i) => if i > 0 { self.inner[i - 1].1 } else { f64::NEG_INFINITY }
            })
        }
    }

    pub fn total_prob(&self) -> LogProb {
        let &(_, prob) = self.inner.last().unwrap();
        prob
    }

    /// Return maximum a posteriori probability estimate (MAP).
    pub fn map(&self) -> &T {
        let mut max = self.iter_pmf().next().unwrap();
        for e in self.iter_pmf() {
            if e.1 >= max.1 {
                max = e;
            }
        }
        &max.0
    }

    pub fn credible_interval(&self) -> (&T, &T) {
        let lower = self.inner.binary_search_by(|&(_, p)| p.partial_cmp(&0.025f64.ln()).unwrap()).unwrap_or_else(|i| i);
        let upper = self.inner.binary_search_by(|&(_, p)| p.partial_cmp(&0.975f64.ln()).unwrap()).unwrap_or_else(|i| i - 1);

        (&self.inner[lower].0, &self.inner[upper].0)
    }

    pub fn len(&self) -> usize {
        self.inner.len()
    }
}

impl<T: NumCast + Clone + PartialOrd> CDF<T> {
    pub fn expected_value(&self) -> f64 {
        self.iter_pmf().map(|(value, prob)| {
            cast::<T, f64>(value.clone()).unwrap() * prob.exp()
        }).fold(0.0f64, |s, e| s + e)
    }

    pub fn variance(&self) -> f64 {
        let ev = self.expected_value();
        self.iter_pmf().map(|(value, prob)| {
                (cast::<T, f64>(value.clone()).unwrap() - ev).powi(2) * prob.exp()
        }).fold(0.0, |s, e| s + e)
    }

    pub fn standard_deviation(&self) -> f64 {
        self.variance().sqrt()
    }
}

pub type CDFPMFIter<'a, T> = iter::Scan<slice::Iter<'a, (T, LogProb)>, LogProb, fn(&mut LogProb, &'a (T, LogProb)) -> Option<(&'a T, LogProb)>>;

#[cfg(test)]
mod test {
    use super::*;

    use itertools::Itertools;

    #[test]
    fn test_cdf() {
        let mut pmf = vec![(0.0, 0.1f64.ln())];
        for i in 0..9 {
            pmf.push((i as f64, 0.1f64.ln()));
        }
        println!("{:?}", pmf);

        let cdf = CDF::from_pmf(pmf.clone());
        println!("{:?}", cdf);
        for &(value, prob) in pmf.iter().skip(2) {
            assert_ulps_eq!(prob, cdf.get_pmf(&value).unwrap(), epsilon = 0.0000000000001);
        }
        assert_relative_eq!(cdf.total_prob(), 1.0f64.ln());
        assert_relative_eq!(cdf.get(&1.0).unwrap(), 0.3f64.ln(), epsilon = 0.00000001);
    }
}
<commit_msg>Documentation.<commit_after>// Copyright 2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for discrete probability distributions in terms of cumulative distribution
//! functions (CDF).

use std::f64;
use std::iter;
use std::slice;

use num::traits::{cast, NumCast};
use itertools::Itertools;

use stats::logprobs::{self, LogProb};

/// Implementation of a cumulative distribution function.
#[derive(Debug, Clone)]
pub struct CDF<T: PartialOrd> {
    inner: Vec<(T, LogProb)>
}

impl<T: PartialOrd> CDF<T> {
    /// Create CDF from given probability mass function (PMF). The PMF may contain duplicate values
    /// the probabilities of which are summed during generation of the CDF.
    ///
    /// # Arguments
    ///
    /// * `pmf` - the PMF as a vector of value/probability pairs
    pub fn from_pmf(mut entries: Vec<(T, LogProb)>) -> Self {
        entries.sort_by(|&(ref a, _), &(ref b, _)| a.partial_cmp(b).unwrap());
        let mut inner: Vec<(T, LogProb)> = Vec::new();
        for mut e in entries.into_iter() {
            let p = logprobs::add(inner.last().map_or(f64::NEG_INFINITY, |e| e.1), e.1);
            if !inner.is_empty() && inner.last().unwrap().0 == e.0 {
                inner.last_mut().unwrap().1 = p;
            }
            else {
                e.1 = p;
                inner.push(e);
            }
        }
        let mut cdf = CDF {
            inner: inner
        };

        if relative_eq!(cdf.total_prob(), 0.0) && cdf.total_prob() > 0.0 {
            cdf.inner.last_mut().unwrap().1 = 0.0;
        }

        cdf
    }

    /// Create CDF from iterator. This can be used to replace the values of a CDF.
    pub fn from_cdf<I: Iterator<Item = (T, LogProb)>>(entries: I) -> Self {
        CDF { inner: entries.collect_vec() }
    }

    /// Reduce CDF by omitting values with zero probability.
    pub fn reduce(self) -> Self {
        let mut inner = Vec::new();
        let mut last = f64::NEG_INFINITY;
        for e in self.inner.into_iter() {
            if last != e.1 {
                last = e.1;
                inner.push(e);
            }
        }
        CDF { inner: inner }
    }

    /// Downsample CDF to n entries.
    pub fn sample(mut self, n: usize) -> Self {
        if self.inner.len() <= n {
            self
        }
        else {
            let s = self.inner.len() / (n - 1);
            let last = self.inner.pop().unwrap();
            let mut inner = self.inner.into_iter().step(s).collect_vec();
            inner.push(last);
            CDF {
                inner: inner
            }
        }
    }

    /// Provide iterator.
    pub fn iter(&self) -> slice::Iter<(T, f64)>{
        self.inner.iter()
    }

    /// Iterator over corresponding PMF.
    pub fn iter_pmf<'a>(&'a self) -> CDFPMFIter<'a, T> {
        fn cdf_to_pmf<'a, G>(last_prob: &mut LogProb, e: &'a (G, LogProb)) -> Option<(&'a G, LogProb)> {
            let &(ref value, cdf_prob) = e;
            let prob = logprobs::sub(cdf_prob, *last_prob);
            *last_prob = cdf_prob;
            Some((value, prob))
        }
        self.inner.iter().scan(f64::NEG_INFINITY, cdf_to_pmf)
    }

    /// Get cumulative probability for a given value. If the value is not present,
    /// return the probability of the previous value. Complexity O(log n).
    pub fn get(&self, value: &T) -> Option<LogProb> {
        if self.inner.is_empty() {
            None
        }
        else {
            Some(match self.inner.binary_search_by(|e| e.0.partial_cmp(value).unwrap()) {
                Ok(i) => self.inner[i].1,
                Err(i) => if i > 0 { self.inner[i - 1].1 } else { f64::NEG_INFINITY }
            })
        }
    }

    /// Get probability (i.e. probability mass) for a given value. Complexity O(log n).
    pub fn get_pmf(&self, value: &T) -> Option<LogProb> {
        if self.inner.is_empty() {
            None
        }
        else {
            Some(match self.inner.binary_search_by(|e| e.0.partial_cmp(value).unwrap()) {
                Ok(i) => if i > 0 { logprobs::sub(self.inner[i].1, self.inner[i - 1].1) } else { self.inner[0].1 },
                Err(i) => if i > 0 { self.inner[i - 1].1 } else { f64::NEG_INFINITY }
            })
        }
    }

    /// Return total probability.
    pub fn total_prob(&self) -> LogProb {
        let &(_, prob) = self.inner.last().unwrap();
        prob
    }

    /// Return maximum a posteriori probability estimate (MAP).
    pub fn map(&self) -> &T {
        let mut max = self.iter_pmf().next().unwrap();
        for e in self.iter_pmf() {
            if e.1 >= max.1 {
                max = e;
            }
        }
        &max.0
    }

    /// Return 95% credible interval.
    pub fn credible_interval(&self) -> (&T, &T) {
        let lower = self.inner.binary_search_by(|&(_, p)| p.partial_cmp(&0.025f64.ln()).unwrap()).unwrap_or_else(|i| i);
        let upper = self.inner.binary_search_by(|&(_, p)| p.partial_cmp(&0.975f64.ln()).unwrap()).unwrap_or_else(|i| i - 1);

        (&self.inner[lower].0, &self.inner[upper].0)
    }

    /// Number of entries in the CDF.
    pub fn len(&self) -> usize {
        self.inner.len()
    }
}

impl<T: NumCast + Clone + PartialOrd> CDF<T> {
    pub fn expected_value(&self) -> f64 {
        self.iter_pmf().map(|(value, prob)| {
            cast::<T, f64>(value.clone()).unwrap() * prob.exp()
        }).fold(0.0f64, |s, e| s + e)
    }

    pub fn variance(&self) -> f64 {
        let ev = self.expected_value();
        self.iter_pmf().map(|(value, prob)| {
                (cast::<T, f64>(value.clone()).unwrap() - ev).powi(2) * prob.exp()
        }).fold(0.0, |s, e| s + e)
    }

    pub fn standard_deviation(&self) -> f64 {
        self.variance().sqrt()
    }
}

pub type CDFPMFIter<'a, T> = iter::Scan<slice::Iter<'a, (T, LogProb)>, LogProb, fn(&mut LogProb, &'a (T, LogProb)) -> Option<(&'a T, LogProb)>>;

#[cfg(test)]
mod test {
    use super::*;

    use itertools::Itertools;

    #[test]
    fn test_cdf() {
        let mut pmf = vec![(0.0, 0.1f64.ln())];
        for i in 0..9 {
            pmf.push((i as f64, 0.1f64.ln()));
        }
        println!("{:?}", pmf);

        let cdf = CDF::from_pmf(pmf.clone());
        println!("{:?}", cdf);
        for &(value, prob) in pmf.iter().skip(2) {
            assert_ulps_eq!(prob, cdf.get_pmf(&value).unwrap(), epsilon = 0.0000000000001);
        }
        assert_relative_eq!(cdf.total_prob(), 1.0f64.ln());
        assert_relative_eq!(cdf.get(&1.0).unwrap(), 0.3f64.ln(), epsilon = 0.00000001);
    }
}
<|endoftext|>


<commit_before>use toml::Parser;
use regex::Regex;
use std::io::prelude::*;
use std::fs::File;
use std::io::Error;

pub enum TomlError {
    parseError(&'static str),
    ioError(Error)
}

pub fn read_version(file: String) -> Option<String> {
   let file_map = Parser::new(&file).parse().unwrap();
   println!("{:?}", file_map);
   let package = file_map.get("package").unwrap();
   let version = package.as_table()
       .unwrap()
       .get("version");
   match version {
       Some(v) => Some(v.as_str().unwrap().into()),
       None => None
   }
}

pub fn file_with_new_version(file: String, new_version: &str) -> String {
    let re = Regex::new(r#"version\s=\s"\d+\.\d+\.\d+""#).unwrap();
    let new_version = format!("version = \"{}\"", new_version);
    re.replace(&file, &new_version[..])
}

pub fn read_from_file() -> Result<String, TomlError> {
    let cargo_file = match read_cargo_toml() {
        Ok(buffer) => buffer,
        Err(err) => return Err(TomlError::ioError(err))
    };

    match read_version(cargo_file) {
        Some(version) => Ok(version),
        None => Err(TomlError::parseError("No version field found"))
    }
}

pub fn write_new_version(new_version: String) -> Result<(), Error> {
    let cargo_toml = match read_cargo_toml() {
        Ok(file) => file,
        Err(err) => return Err(err)
    };
    let new_cargo_toml = file_with_new_version(cargo_toml, &new_version);

    let mut handle = match File::open("Cargo.toml") {
        Ok(handle) => handle,
        Err(err) => {
            return Err(err)
        }
    };

    match handle.write_all(new_cargo_toml.as_bytes()) {
        Ok(_) => Ok(()),
        Err(err) => Err(err)
    }
}

fn read_cargo_toml() -> Result<String, Error> {
    let mut handle = match File::open("Cargo.toml") {
        Ok(handle) => handle,
        Err(err) => {
            return Err(err)
        }
    };

    let mut buffer = String::new();
    match handle.read_to_string(&mut buffer) {
        Ok(_) => Ok(buffer),
        Err(err) => Err(err)
    }
}

#[cfg(test)]
mod tests {
    extern crate toml;
    extern crate regex;
    use super::*;

    fn example_file() -> String {
        "[package]
    name = \"semantic-rs\"
    version = \"0.1.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    fn example_file_without_version() -> String {
        "[package]
    name = \"semantic-rs\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    #[test]
    fn read_version_number() {
        let version_str = read_version(example_file());
        assert_eq!(version_str, Some("0.1.0".into()));
    }

    #[test]
    fn read_file_without_version_number() {
        let version_str = read_version(example_file_without_version());
        assert_eq!(version_str, None);
    }

    #[test]
    fn write_new_version_number() {
        let new_toml_file = file_with_new_version(example_file(), "0.2.0".into());
        let expected_file =
            "[package]
    name = \"semantic-rs\"
    version = \"0.2.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string();
        assert_eq!(new_toml_file, expected_file);
    }
}
<commit_msg>style(toml_file): Remove debug output on stdout<commit_after>use toml::Parser;
use regex::Regex;
use std::io::prelude::*;
use std::fs::File;
use std::io::Error;

pub enum TomlError {
    parseError(&'static str),
    ioError(Error)
}

pub fn read_version(file: String) -> Option<String> {
   let file_map = Parser::new(&file).parse().unwrap();
   let package = file_map.get("package").unwrap();
   let version = package.as_table()
       .unwrap()
       .get("version");
   match version {
       Some(v) => Some(v.as_str().unwrap().into()),
       None => None
   }
}

pub fn file_with_new_version(file: String, new_version: &str) -> String {
    let re = Regex::new(r#"version\s=\s"\d+\.\d+\.\d+""#).unwrap();
    let new_version = format!("version = \"{}\"", new_version);
    re.replace(&file, &new_version[..])
}

pub fn read_from_file() -> Result<String, TomlError> {
    let cargo_file = match read_cargo_toml() {
        Ok(buffer) => buffer,
        Err(err) => return Err(TomlError::ioError(err))
    };

    match read_version(cargo_file) {
        Some(version) => Ok(version),
        None => Err(TomlError::parseError("No version field found"))
    }
}

pub fn write_new_version(new_version: String) -> Result<(), Error> {
    let cargo_toml = match read_cargo_toml() {
        Ok(file) => file,
        Err(err) => return Err(err)
    };
    let new_cargo_toml = file_with_new_version(cargo_toml, &new_version);

    let mut handle = match File::open("Cargo.toml") {
        Ok(handle) => handle,
        Err(err) => {
            return Err(err)
        }
    };

    match handle.write_all(new_cargo_toml.as_bytes()) {
        Ok(_) => Ok(()),
        Err(err) => Err(err)
    }
}

fn read_cargo_toml() -> Result<String, Error> {
    let mut handle = match File::open("Cargo.toml") {
        Ok(handle) => handle,
        Err(err) => {
            return Err(err)
        }
    };

    let mut buffer = String::new();
    match handle.read_to_string(&mut buffer) {
        Ok(_) => Ok(buffer),
        Err(err) => Err(err)
    }
}

#[cfg(test)]
mod tests {
    extern crate toml;
    extern crate regex;
    use super::*;

    fn example_file() -> String {
        "[package]
    name = \"semantic-rs\"
    version = \"0.1.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    fn example_file_without_version() -> String {
        "[package]
    name = \"semantic-rs\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    #[test]
    fn read_version_number() {
        let version_str = read_version(example_file());
        assert_eq!(version_str, Some("0.1.0".into()));
    }

    #[test]
    fn read_file_without_version_number() {
        let version_str = read_version(example_file_without_version());
        assert_eq!(version_str, None);
    }

    #[test]
    fn write_new_version_number() {
        let new_toml_file = file_with_new_version(example_file(), "0.2.0".into());
        let expected_file =
            "[package]
    name = \"semantic-rs\"
    version = \"0.2.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string();
        assert_eq!(new_toml_file, expected_file);
    }
}
<|endoftext|>


<commit_before>use toml::Parser;
use regex::Regex;

pub fn read_version(file: String) -> String {
   let file_map = Parser::new(&file).parse().unwrap();
   println!("{:?}", file_map);
   let package = file_map.get("package").unwrap();
   package.as_table()
       .unwrap()
       .get("version").unwrap()
       .as_str()
       .unwrap()
       .into()
}

pub fn file_with_new_version(file: String, new_version: &str) -> String {
    let re = Regex::new(r#"version\s=\s"\d+\.\d+\.\d+""#).unwrap();
    let new_version = format!("version = \"{}\"", new_version);
    re.replace(&file, &new_version[..])
}

#[cfg(test)]
mod tests {
    extern crate toml;
    extern crate regex;
    use super::*;

    fn example_file() -> String {
        "[package]
    name = \"semantic-rs\"
    version = \"0.1.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    #[test]
    fn read_version_number() {
        let version_str = read_version(example_file());
        assert_eq!(version_str, "0.1.0");
    }

    #[test]
    fn write_new_version_number() {
        let new_toml_file = file_with_new_version(example_file(), "0.2.0".into());
        let expected_file =
            "[package]
    name = \"semantic-rs\"
    version = \"0.2.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string();
        assert_eq!(new_toml_file, expected_file);
    }
}
<commit_msg>feat(toml_file): Handle the case when toml file doesn't contain version property<commit_after>use toml::Parser;
use regex::Regex;

pub fn read_version(file: String) -> Option<String> {
   let file_map = Parser::new(&file).parse().unwrap();
   println!("{:?}", file_map);
   let package = file_map.get("package").unwrap();
   let version = package.as_table()
       .unwrap()
       .get("version");
   match version {
       Some(v) => Some(v.as_str().unwrap().into()),
       None => None
   }
}

pub fn file_with_new_version(file: String, new_version: &str) -> String {
    let re = Regex::new(r#"version\s=\s"\d+\.\d+\.\d+""#).unwrap();
    let new_version = format!("version = \"{}\"", new_version);
    re.replace(&file, &new_version[..])
}

#[cfg(test)]
mod tests {
    extern crate toml;
    extern crate regex;
    use super::*;

    fn example_file() -> String {
        "[package]
    name = \"semantic-rs\"
    version = \"0.1.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    fn example_file_without_version() -> String {
        "[package]
    name = \"semantic-rs\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    #[test]
    fn read_version_number() {
        let version_str = read_version(example_file());
        assert_eq!(version_str, Some("0.1.0".into()));
    }

    #[test]
    fn read_file_without_version_number() {
        let version_str = read_version(example_file_without_version());
        assert_eq!(version_str, None);
    }

    #[test]
    fn write_new_version_number() {
        let new_toml_file = file_with_new_version(example_file(), "0.2.0".into());
        let expected_file =
            "[package]
    name = \"semantic-rs\"
    version = \"0.2.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string();
        assert_eq!(new_toml_file, expected_file);
    }
}
<|endoftext|>