<commit_before>extern crate regex;

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

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

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)
}

/// Removes all meta tokens from the give Vector
/// Meta tokens include:
/// * comments
/// * imports
/// * permission modifiers (public, private, protected)
///
/// Note that in future versions, imports will not be removed.
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 permission modifiers (public, private, protected)
        else if token.name == "mod.permission"
        {
            invalid_indecies.push(index);
        }
    }

    // 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

            // TODO: parse method calls
        }

        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;
}
<commit_msg>Generalize utility functions<commit_after>extern crate regex;

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

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

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)
}

/// Removes all meta tokens from the give Vector
/// Meta tokens include:
/// * comments
/// * imports
/// * permission modifiers (public, private, protected)
///
/// Note that in future versions, imports will not be removed.
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 permission modifiers (public, private, protected)
        else if token.name == "mod.permission"
        {
            invalid_indecies.push(index);
        }
    }

    // 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

            // TODO: parse method calls
        }

        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
}

/// @deprecated
/// Replaced by find_next using ";" as the symbol argument
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;
}

/// @deprecated
/// Replaced by identify_body_bounds using ("{", "}") as the symbol argument
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;
}

/// Identifies the end index of a nestable body given the open and close symbols for the body
/// For instance, an arithmetic expression can have nested parenthesis groups (e.g. (1 + (2*(2+1))) * (2 + 5) )
/// In the example, this method would identify the start and stop as these (> and < respectively):
///     >(1 + (2*(2+1)))< * (2 + 5)
/// If the start_index was specified as the parenthesis surrounding 2 + 5, then this method would select:
///     (1 + (2*(2+1))) * >(2 + 5)<
///
/// The start index passed to this method should be the index AFTER the first open symbol
/// @return the index of the token that closes the specified body or None if the block is not closed or nested properly
fn identify_body_bounds(tokens: &Vec<Token>, start: usize, symbols: (&str, &str)) -> Option<usize>
{
    let (open, close) = symbols;
    let mut open_braces = 1;

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

    return None;
}

/// Identifies the index of the next token matching the specified symbol
/// @return the index of the specified token (after the specified start index) or None if no such symbol exists
fn find_next(tokens: &Vec<Token>, start: usize, symbol: &str) -> Option<usize>
{
    for (index, token) in tokens[start..].iter().enumerate()
    {
        if token.value == symbol { return Some(index + start); }
    }

    return None;
}
<|endoftext|>


<commit_before>use cpp_type::CppType;
use cpp_ffi_type::CppToFfiTypeConversion;
use utils::JoinWithString;

extern crate libc;

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

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct RustName {
  pub parts: Vec<String>,
}

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("::")
    }
  }
}

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,
  NonVoid {
    base: RustName,
    generic_arguments: Option<Vec<RustType>>,
    is_const: bool,
    indirection: RustTypeIndirection,
  },
}

impl RustType {
  pub fn caption(&self) -> String {
    match *self {
      RustType::Void => "void".to_string(),
      RustType::NonVoid { ref base, ref generic_arguments, ref is_const, ref indirection } => {
        let mut name = base.last_name().clone();
        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 => {
            name = format!("{}{}_ptr_ptr", name, mut_text);
          }
        }
        name
      }
    }
  }

  pub fn is_ref(&self) -> bool {
    match *self {
      RustType::NonVoid { ref indirection, .. } => {
        match *indirection {
          RustTypeIndirection::Ref { .. } => true,
          _ => false,
        }
      }
      RustType::Void => false
    }
  }

  pub fn with_lifetime(&self, new_lifetime: String) -> RustType {
    let mut r = self.clone();
    if let RustType::NonVoid { 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::NonVoid { ref base, ref generic_arguments, ref is_const, 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::NonVoid {
            base: real_name,
            generic_arguments: generic_arguments.clone(),
            is_const: is_const.clone(),
            indirection: indirection.clone(),
          }
        } else {
          self.clone()
        }
      }
    }
  }
}

#[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: CppToFfiTypeConversion,
  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>rust type caption fixed<commit_after>use cpp_type::CppType;
use cpp_ffi_type::CppToFfiTypeConversion;
use utils::JoinWithString;
use utils::CaseOperations;

extern crate libc;

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

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct RustName {
  pub parts: Vec<String>,
}

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("::")
    }
  }
}

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,
  NonVoid {
    base: RustName,
    generic_arguments: Option<Vec<RustType>>,
    is_const: bool,
    indirection: RustTypeIndirection,
  },
}

impl RustType {
  pub fn caption(&self) -> String {
    match *self {
      RustType::Void => "void".to_string(),
      RustType::NonVoid { ref base, ref generic_arguments, ref is_const, 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 => {
            name = format!("{}{}_ptr_ptr", name, mut_text);
          }
        }
        name
      }
    }
  }

  pub fn is_ref(&self) -> bool {
    match *self {
      RustType::NonVoid { ref indirection, .. } => {
        match *indirection {
          RustTypeIndirection::Ref { .. } => true,
          _ => false,
        }
      }
      RustType::Void => false
    }
  }

  pub fn with_lifetime(&self, new_lifetime: String) -> RustType {
    let mut r = self.clone();
    if let RustType::NonVoid { 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::NonVoid { ref base, ref generic_arguments, ref is_const, 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::NonVoid {
            base: real_name,
            generic_arguments: generic_arguments.clone(),
            is_const: is_const.clone(),
            indirection: indirection.clone(),
          }
        } else {
          self.clone()
        }
      }
    }
  }
}

#[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: CppToFfiTypeConversion,
  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>use std::fs::File;
use std::io::prelude::*;

pub fn look_say_1(file: &str) -> usize {
    process(file, 40)
}

pub fn look_say_2(file: &str) -> usize {
    process(file, 1)
}

fn process(file: &str, n: usize) -> usize {
    let mut file = File::open(file).expect("File open fail.");
    let mut input = String::new();
    file.read_to_string(&mut input).expect("File read fail.");

    let mut output = input;
    for _ in 0..n {
        println!("{}", output);
        output = look_say(&*output);
    }
    output.len()
}

fn look_say(input: &str) -> String {
    let mut cs = input.chars();
    let mut output = String::new();
    let mut prev = '!';
    let mut word = Vec::new();
    loop {
        if let Some(c) = cs.next() {
            println!("curr: {}", c);
            println!("prev: {}", prev);
            if c != prev {
                if word.len() > 0 {
                    if let Some(n) = format!("{}", word.len()).chars().next() {
                        let m = word[0];
                        output.push(n);
                        output.push(m);
                        println!("{}{}", n, m);
                        println!("");
                    }
                }
                word.clear();
            }
            word.push(c);
            prev = c;
        } else {
            if word.len() > 0 {
                if let Some(n) = format!("{}", word.len()).chars().next() {
                    let m = word[0];
                    output.push(n);
                    output.push(m);
                    println!("{}{}", n, m);
                    println!("");
                }
            }
            break;
        }
    }
    output
}
<commit_msg>Solve day10 2015 part 1.<commit_after>use std::fs::File;
use std::io::prelude::*;

pub fn look_say_1(file: &str) -> usize {
    process(file, 40)
}

pub fn look_say_2(file: &str) -> usize {
    process(file, 1)
}

fn process(file: &str, n: usize) -> usize {
    let mut file = File::open(file).expect("File open fail.");
    let mut input = String::new();
    file.read_to_string(&mut input).expect("File read fail.");

    let mut output = input;
    for _ in 0..n {
        println!("{}", output);
        output = look_say(output.trim());
    }
    output.len()
}

fn look_say(input: &str) -> String {
    let mut cs = input.chars();
    let mut output = String::new();
    let mut prev = '!';
    let mut word = Vec::new();
    loop {
        if let Some(c) = cs.next() {
            println!("curr: {} prev: {}", c, prev);
            if c != prev {
                if word.len() > 0 {
                    if let Some(n) = format!("{}", word.len()).chars().next() {
                        let m = word[0];
                        output.push(n);
                        output.push(m);
                        println!("({}, {})", n, m);
                    }
                    word.clear();
                }
            }
            word.push(c);
            prev = c;
        } else {
            if word.len() > 0 {
                if let Some(n) = format!("{}", word.len()).chars().next() {
                    let m = word[0];
                    output.push(n);
                    output.push(m);
                    println!("({}, {}) eol", n, m);
                }
            }
            break;
        }
    }
    output
}
<|endoftext|>


<commit_before>//!
//! # Scheduler
//!

use std::mem;
use std::sync::*;
use std::thread::*;
use std::sync::mpsc::*;
use std::collections::vec_deque::*;

use num_cpus;

const MIN_THREADS: usize = 8;

lazy_static! {
    static ref SCHEDULER: Arc<Scheduler> = Arc::new(Scheduler::new());
}

///
/// The default maximum number of threads in a scheduler
///
fn initial_max_threads() -> usize {
    MIN_THREADS.min(num_cpus::get()*2)
}

///
/// Trait implemented by things that can be scheduled as a job
///
trait ScheduledJob : Send {
    /// Runs this particular job
    fn run(&mut self);
}

///
/// Basic job is just a FnOnce
///
struct Job<TFn>
where TFn: Send+FnOnce() -> () {
    action: Option<TFn>
}

impl<TFn> Job<TFn>
where TFn: Send+FnOnce() -> () {
    fn new(action: TFn) -> Job<TFn> {
        Job { action: Some(action) }
    }
}

impl<TFn> ScheduledJob for Job<TFn>
where TFn: Send+FnOnce() -> () {
    fn run(&mut self) {
        // Consume the action when it's run
        let mut action = None;
        mem::swap(&mut action, &mut self.action);

        if let Some(action) = action {
            action();
        } else {
            panic!("Cannot schedule an action twice");
        }
    }
}

///
/// The scheduler is used to schedule tasks onto a pool of threads
///
pub struct Scheduler {
    /// The queues that are active in the scheduler
    queues: Arc<Mutex<Vec<Weak<JobQueue>>>>,

    /// Active threads
    threads: Mutex<Vec<SchedulerThread>>,

    /// The maximum number of threads permitted in this scheduler
    max_threads: Mutex<usize>
}

///
/// A job queue provides a list of jobs to perform in order
///
pub struct JobQueue {
    /// Jobs scheduled on this queue
    queue: Mutex<VecDeque<Box<ScheduledJob>>>,

    /// True while this queue is running
    running: Mutex<bool>
}

///
/// A scheduler thread reads from the scheduler queue
///
struct SchedulerThread {
    /// The jobs that this thread should run
    jobs: Sender<Box<ScheduledJob>>,

    /// The thread itself
    thread: JoinHandle<()>,

    /// Flag that indicates that this thread is busy
    busy: Arc<Mutex<bool>>
}

impl SchedulerThread {
    ///
    /// Creates a new scheduler thread
    ///
    fn new() -> SchedulerThread {
        // All the thread does is run jobs from its channel
        let (jobs_in, jobs_out): (Sender<Box<ScheduledJob>>, Receiver<Box<ScheduledJob>>) = channel();
        let thread = spawn(move || {
            while let Ok(mut job) = jobs_out.recv() {
                job.run();
            }
        });

        SchedulerThread {
            jobs:   jobs_in,
            thread: thread,
            busy:   Arc::new(Mutex::new(false))
        }
    }

    ///
    /// Schedules a job to be run on this thread
    ///
    fn run<Job: 'static+ScheduledJob>(&self, job: Job) {
        self.jobs.send(Box::new(job)).unwrap();
    }
}

impl JobQueue {
    ///
    /// Creates a new job queue
    ///
    fn new() -> JobQueue {
        JobQueue {
            queue:      Mutex::new(VecDeque::new()),
            running:    Mutex::new(false)
        }
    }

    ///
    /// Adds a new job to this queue, returns true if the queue is dormant and needs to be started
    ///
    fn queue<TJob: 'static+ScheduledJob>(&self, job: TJob) -> bool {
        let mut queue   = self.queue.lock().unwrap();
        let running     = self.running.lock().unwrap();

        queue.push_back(Box::new(job));

        !*running
    }

    ///
    /// If there are any jobs waiting, dequeues the next one
    ///
    fn dequeue(&self) -> Option<Box<ScheduledJob>> {
        let mut queue = self.queue.lock().unwrap();

        queue.pop_front()
    }

    ///
    /// Runs jobs on this queue until there are none left, marking the job as inactive when done
    ///
    fn drain(&self) {
        let mut done = false;

        while !done {
            // Run jobs until the queue is drained
            while let Some(mut job) = self.dequeue() {
                job.run();
            }

            // Mark as not running any more
            {
                let queue       = self.queue.lock().unwrap();
                let mut running = self.running.lock().unwrap();

                // If the queue is empty at the point where we obtain the 'running' lock, we can deactivate ourselves
                if queue.len() == 0 {
                    *running    = false;
                    done        = true;
                }
            }
        }
    }
}

impl Scheduler {
    ///
    /// Creates a new scheduler
    ///
    /// (There's only actually one scheduler)
    ///
    fn new() -> Scheduler {
        let result = Scheduler {
            queues:         Arc::new(Mutex::new(vec![])),
            threads:        Mutex::new(vec![]),
            max_threads:    Mutex::new(initial_max_threads())
        };

        result
    }

    ///
    /// Finds the next queue that should be run. If this returns successfully, the queue will
    /// be marked as running.
    ///
    fn next_to_run(queues: &Arc<Mutex<Vec<Weak<JobQueue>>>>) -> Option<Arc<JobQueue>> {
        // Search the queues...
        let queues = queues.lock().unwrap();

        // Find a queue where is_running is false
        for q in queues.iter() {
            // Queue is a weak ref, check that it's still in use
            if let Some(q) = q.upgrade() {
                // If the queue is not running, then mark it as running and return it
                let mut is_running = q.running.lock().unwrap();
                if !*is_running {
                    // Clone here is necessary because the is_running update is borrowing q
                    *is_running = true;
                    return Some(q.clone());
                }
            }
        }

        None
    }

    ///
    /// Attempts to schedule a task on a dormant thread
    ///
    fn schedule_dormant<NextJob, RunJob, JobData>(&self, next_job: NextJob, job: RunJob) -> bool
    where RunJob: 'static+Send+Fn(JobData) -> (), NextJob: 'static+Send+Fn() -> Option<JobData> {
        let threads = self.threads.lock().unwrap();

        // Find the first thread that is not marked as busy and schedule this task on it
        for thread in threads.iter() {
            let mut busy = thread.busy.lock().unwrap();

            if !*busy {
                // Clone the busy mutex so we can return this thread to readiness
                let also_busy = thread.busy.clone();

                // This thread is busy
                *busy = true;
                thread.run(Job::new(move || {
                    let mut done = false;

                    while !done {
                        // Obtain the next job. The thread is not busy once there are no longer any jobs
                        // We hold the mutex while this is going on to avoid a race condition when a thread is going dormant
                        let job_data = {
                            let mut busy = also_busy.lock().unwrap();
                            let job_data = next_job();

                            // If there's no next job, then this thread is no longer busy
                            if job_data.is_none() {
                                *busy = false;
                            }

                            job_data
                        };

                        // Run the job if there is one, stop the thread if there is not
                        if let Some(job_data) = job_data {
                            job(job_data);
                        } else {
                            done = true;
                        }
                    }
                }));

                return true;
            }
        }

        // No dormant threads were found
        false
    }

    ///
    /// If we're running fewer than the maximum number of threads, try to spawn a new one
    ///
    fn spawn_thread_if_less_than_maximum(&self) -> bool {
        let max_threads = { *self.max_threads.lock().unwrap() };
        let threads     = self.threads.lock().unwrap();

        if threads.len() < max_threads {
            // Create a new thread
            let new_thread = SchedulerThread::new();
            self.threads.lock().unwrap().push(new_thread);

            true
        } else {
            // Can't spawn a new thread
            false
        }
    }

    ///
    /// Wakes a thread to run a dormant queue. Returns true if a thread was woken up
    ///
    fn schedule_thread(&self) -> bool {
        // Find a dormant thread and activate it
        let queues = self.queues.clone();

        // Schedule work on this dormant thread
        if !self.schedule_dormant(move || Self::next_to_run(&queues), move |work| work.drain()) {
            // Try to create a new thread
            if self.spawn_thread_if_less_than_maximum() {
                // Try harder to schedule this task if a thread was created
                self.schedule_thread()
            } else {
                // Couldn't schedule on an existing thread or create a new one
                false
            }
        } else {
            // Successfully scheduled
            true
        }
    }

    ///
    /// Spawns a thread in this scheduler
    ///
    pub fn spawn_thread(&self) {
        let new_thread = SchedulerThread::new();
        self.threads.lock().unwrap().push(new_thread);
    }

    ///
    /// Creates a new job queue for this scheduler
    ///
    pub fn create_job_queue(&self) -> Arc<JobQueue> {
        // Create the  new queue
        let new_queue = Arc::new(JobQueue::new());

        let mut queues = self.queues.lock().unwrap();

        // Replace an existing weak queue if possible
        let weak_pos = queues.iter().position(|q| q.upgrade().is_none());

        if let Some(weak_pos) = weak_pos {
            // Replace a queue that was present but is no longer in use
            queues[weak_pos] = Arc::downgrade(&new_queue);
        } else {
            // Add to the queues managed by this object (as a weak reference)
            queues.push(Arc::downgrade(&new_queue));
        }

        new_queue
    }

    ///
    /// Schedules a job on this scheduler
    ///
    pub fn async<TFn: 'static+Send+FnOnce() -> ()>(&self, queue: &Arc<JobQueue>, job: TFn) {
        if queue.queue(Job::new(job)) {
            // A true result indicates that the job was scheduled but the queue is not running. Try to schedule a thread if this occurs.
            self.schedule_thread();
        }
    }
}

///
/// Retrieves the global scheduler
///
pub fn scheduler<'a>() -> &'a Scheduler {
    &SCHEDULER
}

///
/// Creates a scheduler queue
///
pub fn queue() -> Arc<JobQueue> {
    scheduler().create_job_queue()
}

///
/// Creates a scheduler queue
///
pub fn async<TFn: 'static+Send+FnOnce() -> ()>(queue: &Arc<JobQueue>, job: TFn) {
    scheduler().async(queue, job)
}

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

    fn timeout<TFn: 'static+Send+FnOnce() -> ()>(action: TFn, millis: u64) {
        let (tx, rx)    = channel();
        let (tx1, tx2)  = (tx.clone(), tx.clone());

        spawn(move || {
            action();
            tx1.send(true).ok();
        });

        spawn(move || {
            sleep(Duration::from_millis(millis));
            tx2.send(false).ok();
        });

        if rx.recv().unwrap() == false {
            panic!("Timeout");
        }
    }

    #[test]
    fn can_schedule_async() {
        timeout(|| {
            let (tx, rx)    = channel();
            let queue       = queue();

            async(&queue, move || {
                tx.send(42).unwrap();
            });

            assert!(rx.recv().unwrap() == 42);
        }, 100);
    }

    #[test]
    fn can_schedule_after_queue_released() {
        timeout(|| {
            {
                let (tx, rx)    = channel();
                let queue1      = queue();

                async(&queue1, move || {
                    tx.send(42).unwrap();
                });

                assert!(rx.recv().unwrap() == 42);
            }

            {
                let (tx, rx)    = channel();
                let queue2      = queue();

                async(&queue2, move || {
                    tx.send(43).unwrap();
                });

                assert!(rx.recv().unwrap() == 43);
            }
        }, 100);
    }

    #[test]
    fn will_schedule_in_order() {
        timeout(|| {
            let (tx, rx)    = channel();
            let queue       = queue();

            let (tx1, tx2)  = (tx.clone(), tx.clone());

            async(&queue, move || {
                sleep(Duration::from_millis(100));
                tx1.send(1).unwrap();
            });
            async(&queue, move || {
                tx2.send(2).unwrap();
            });

            assert!(rx.recv().unwrap() == 1);
            assert!(rx.recv().unwrap() == 2);
        }, 500);
    }

    #[test]
    fn will_schedule_separate_queues_in_parallel() {
        timeout(|| {
            let (tx, rx)        = channel();
            let queue1          = queue();
            let queue2          = queue();
            let queue2_has_run  = Arc::new(Mutex::new(false));

            let queue1_check = queue2_has_run.clone();

            async(&queue1, move || {
                // The other task needs to start within 100ms for this to work
                sleep(Duration::from_millis(100));
                tx.send(*queue1_check.lock().unwrap()).unwrap();
            });
            async(&queue2, move || {
                *queue2_has_run.lock().unwrap() = true;
            });

            assert!(rx.recv().unwrap() == true);
        }, 500);
    }
}
<commit_msg>Fix dumb double lock of the threads mutex<commit_after>//!
//! # Scheduler
//!

use std::mem;
use std::sync::*;
use std::thread::*;
use std::sync::mpsc::*;
use std::collections::vec_deque::*;

use num_cpus;

const MIN_THREADS: usize = 8;

lazy_static! {
    static ref SCHEDULER: Arc<Scheduler> = Arc::new(Scheduler::new());
}

///
/// The default maximum number of threads in a scheduler
///
fn initial_max_threads() -> usize {
    MIN_THREADS.max(num_cpus::get()*2)
}

///
/// Trait implemented by things that can be scheduled as a job
///
trait ScheduledJob : Send {
    /// Runs this particular job
    fn run(&mut self);
}

///
/// Basic job is just a FnOnce
///
struct Job<TFn>
where TFn: Send+FnOnce() -> () {
    action: Option<TFn>
}

impl<TFn> Job<TFn>
where TFn: Send+FnOnce() -> () {
    fn new(action: TFn) -> Job<TFn> {
        Job { action: Some(action) }
    }
}

impl<TFn> ScheduledJob for Job<TFn>
where TFn: Send+FnOnce() -> () {
    fn run(&mut self) {
        // Consume the action when it's run
        let mut action = None;
        mem::swap(&mut action, &mut self.action);

        if let Some(action) = action {
            action();
        } else {
            panic!("Cannot schedule an action twice");
        }
    }
}

///
/// The scheduler is used to schedule tasks onto a pool of threads
///
pub struct Scheduler {
    /// The queues that are active in the scheduler
    queues: Arc<Mutex<Vec<Weak<JobQueue>>>>,

    /// Active threads
    threads: Mutex<Vec<SchedulerThread>>,

    /// The maximum number of threads permitted in this scheduler
    max_threads: Mutex<usize>
}

///
/// A job queue provides a list of jobs to perform in order
///
pub struct JobQueue {
    /// Jobs scheduled on this queue
    queue: Mutex<VecDeque<Box<ScheduledJob>>>,

    /// True while this queue is running
    running: Mutex<bool>
}

///
/// A scheduler thread reads from the scheduler queue
///
struct SchedulerThread {
    /// The jobs that this thread should run
    jobs: Sender<Box<ScheduledJob>>,

    /// The thread itself
    thread: JoinHandle<()>,

    /// Flag that indicates that this thread is busy
    busy: Arc<Mutex<bool>>
}

impl SchedulerThread {
    ///
    /// Creates a new scheduler thread
    ///
    fn new() -> SchedulerThread {
        // All the thread does is run jobs from its channel
        let (jobs_in, jobs_out): (Sender<Box<ScheduledJob>>, Receiver<Box<ScheduledJob>>) = channel();
        let thread = spawn(move || {
            while let Ok(mut job) = jobs_out.recv() {
                job.run();
            }
        });

        SchedulerThread {
            jobs:   jobs_in,
            thread: thread,
            busy:   Arc::new(Mutex::new(false))
        }
    }

    ///
    /// Schedules a job to be run on this thread
    ///
    fn run<Job: 'static+ScheduledJob>(&self, job: Job) {
        self.jobs.send(Box::new(job)).unwrap();
    }
}

impl JobQueue {
    ///
    /// Creates a new job queue
    ///
    fn new() -> JobQueue {
        JobQueue {
            queue:      Mutex::new(VecDeque::new()),
            running:    Mutex::new(false)
        }
    }

    ///
    /// Adds a new job to this queue, returns true if the queue is dormant and needs to be started
    ///
    fn queue<TJob: 'static+ScheduledJob>(&self, job: TJob) -> bool {
        let mut queue   = self.queue.lock().unwrap();
        let running     = self.running.lock().unwrap();

        queue.push_back(Box::new(job));

        !*running
    }

    ///
    /// If there are any jobs waiting, dequeues the next one
    ///
    fn dequeue(&self) -> Option<Box<ScheduledJob>> {
        let mut queue = self.queue.lock().unwrap();

        queue.pop_front()
    }

    ///
    /// Runs jobs on this queue until there are none left, marking the job as inactive when done
    ///
    fn drain(&self) {
        let mut done = false;

        while !done {
            // Run jobs until the queue is drained
            while let Some(mut job) = self.dequeue() {
                job.run();
            }

            // Mark as not running any more
            {
                let queue       = self.queue.lock().unwrap();
                let mut running = self.running.lock().unwrap();

                // If the queue is empty at the point where we obtain the 'running' lock, we can deactivate ourselves
                if queue.len() == 0 {
                    *running    = false;
                    done        = true;
                }
            }
        }
    }
}

impl Scheduler {
    ///
    /// Creates a new scheduler
    ///
    /// (There's only actually one scheduler)
    ///
    fn new() -> Scheduler {
        let result = Scheduler {
            queues:         Arc::new(Mutex::new(vec![])),
            threads:        Mutex::new(vec![]),
            max_threads:    Mutex::new(initial_max_threads())
        };

        result
    }

    ///
    /// Finds the next queue that should be run. If this returns successfully, the queue will
    /// be marked as running.
    ///
    fn next_to_run(queues: &Arc<Mutex<Vec<Weak<JobQueue>>>>) -> Option<Arc<JobQueue>> {
        // Search the queues...
        let queues = queues.lock().unwrap();

        // Find a queue where is_running is false
        for q in queues.iter() {
            // Queue is a weak ref, check that it's still in use
            if let Some(q) = q.upgrade() {
                // If the queue is not running, then mark it as running and return it
                let mut is_running = q.running.lock().unwrap();
                if !*is_running {
                    // Clone here is necessary because the is_running update is borrowing q
                    *is_running = true;
                    return Some(q.clone());
                }
            }
        }

        None
    }

    ///
    /// Attempts to schedule a task on a dormant thread
    ///
    fn schedule_dormant<NextJob, RunJob, JobData>(&self, next_job: NextJob, job: RunJob) -> bool
    where RunJob: 'static+Send+Fn(JobData) -> (), NextJob: 'static+Send+Fn() -> Option<JobData> {
        let threads = self.threads.lock().unwrap();

        // Find the first thread that is not marked as busy and schedule this task on it
        for thread in threads.iter() {
            let mut busy = thread.busy.lock().unwrap();

            if !*busy {
                // Clone the busy mutex so we can return this thread to readiness
                let also_busy = thread.busy.clone();

                // This thread is busy
                *busy = true;
                thread.run(Job::new(move || {
                    let mut done = false;

                    while !done {
                        // Obtain the next job. The thread is not busy once there are no longer any jobs
                        // We hold the mutex while this is going on to avoid a race condition when a thread is going dormant
                        let job_data = {
                            let mut busy = also_busy.lock().unwrap();
                            let job_data = next_job();

                            // If there's no next job, then this thread is no longer busy
                            if job_data.is_none() {
                                *busy = false;
                            }

                            job_data
                        };

                        // Run the job if there is one, stop the thread if there is not
                        if let Some(job_data) = job_data {
                            job(job_data);
                        } else {
                            done = true;
                        }
                    }
                }));

                return true;
            }
        }

        // No dormant threads were found
        false
    }

    ///
    /// If we're running fewer than the maximum number of threads, try to spawn a new one
    ///
    fn spawn_thread_if_less_than_maximum(&self) -> bool {
        let max_threads = { *self.max_threads.lock().unwrap() };
        let mut threads = self.threads.lock().unwrap();

        if threads.len() < max_threads {
            // Create a new thread
            let new_thread = SchedulerThread::new();
            threads.push(new_thread);

            true
        } else {
            // Can't spawn a new thread
            false
        }
    }

    ///
    /// Wakes a thread to run a dormant queue. Returns true if a thread was woken up
    ///
    fn schedule_thread(&self) -> bool {
        // Find a dormant thread and activate it
        let queues = self.queues.clone();

        // Schedule work on this dormant thread
        if !self.schedule_dormant(move || Self::next_to_run(&queues), move |work| work.drain()) {
            // Try to create a new thread
            if self.spawn_thread_if_less_than_maximum() {
                // Try harder to schedule this task if a thread was created
                self.schedule_thread()
            } else {
                // Couldn't schedule on an existing thread or create a new one
                false
            }
        } else {
            // Successfully scheduled
            true
        }
    }

    ///
    /// Spawns a thread in this scheduler
    ///
    pub fn spawn_thread(&self) {
        let new_thread = SchedulerThread::new();
        self.threads.lock().unwrap().push(new_thread);
    }

    ///
    /// Creates a new job queue for this scheduler
    ///
    pub fn create_job_queue(&self) -> Arc<JobQueue> {
        // Create the  new queue
        let new_queue = Arc::new(JobQueue::new());

        let mut queues = self.queues.lock().unwrap();

        // Replace an existing weak queue if possible
        let weak_pos = queues.iter().position(|q| q.upgrade().is_none());

        if let Some(weak_pos) = weak_pos {
            // Replace a queue that was present but is no longer in use
            queues[weak_pos] = Arc::downgrade(&new_queue);
        } else {
            // Add to the queues managed by this object (as a weak reference)
            queues.push(Arc::downgrade(&new_queue));
        }

        new_queue
    }

    ///
    /// Schedules a job on this scheduler
    ///
    pub fn async<TFn: 'static+Send+FnOnce() -> ()>(&self, queue: &Arc<JobQueue>, job: TFn) {
        if queue.queue(Job::new(job)) {
            // A true result indicates that the job was scheduled but the queue is not running. Try to schedule a thread if this occurs.
            self.schedule_thread();
        }
    }
}

///
/// Retrieves the global scheduler
///
pub fn scheduler<'a>() -> &'a Scheduler {
    &SCHEDULER
}

///
/// Creates a scheduler queue
///
pub fn queue() -> Arc<JobQueue> {
    scheduler().create_job_queue()
}

///
/// Creates a scheduler queue
///
pub fn async<TFn: 'static+Send+FnOnce() -> ()>(queue: &Arc<JobQueue>, job: TFn) {
    scheduler().async(queue, job)
}

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

    fn timeout<TFn: 'static+Send+FnOnce() -> ()>(action: TFn, millis: u64) {
        let (tx, rx)    = channel();
        let (tx1, tx2)  = (tx.clone(), tx.clone());

        spawn(move || {
            action();
            tx1.send(true).ok();
        });

        spawn(move || {
            sleep(Duration::from_millis(millis));
            tx2.send(false).ok();
        });

        if rx.recv().unwrap() == false {
            panic!("Timeout");
        }
    }

    #[test]
    fn can_schedule_async() {
        timeout(|| {
            let (tx, rx)    = channel();
            let queue       = queue();

            async(&queue, move || {
                tx.send(42).unwrap();
            });

            assert!(rx.recv().unwrap() == 42);
        }, 100);
    }

    #[test]
    fn can_schedule_after_queue_released() {
        timeout(|| {
            {
                let (tx, rx)    = channel();
                let queue1      = queue();

                async(&queue1, move || {
                    tx.send(42).unwrap();
                });

                assert!(rx.recv().unwrap() == 42);
            }

            {
                let (tx, rx)    = channel();
                let queue2      = queue();

                async(&queue2, move || {
                    tx.send(43).unwrap();
                });

                assert!(rx.recv().unwrap() == 43);
            }
        }, 100);
    }

    #[test]
    fn will_schedule_in_order() {
        timeout(|| {
            let (tx, rx)    = channel();
            let queue       = queue();

            let (tx1, tx2)  = (tx.clone(), tx.clone());

            async(&queue, move || {
                sleep(Duration::from_millis(100));
                tx1.send(1).unwrap();
            });
            async(&queue, move || {
                tx2.send(2).unwrap();
            });

            assert!(rx.recv().unwrap() == 1);
            assert!(rx.recv().unwrap() == 2);
        }, 500);
    }

    #[test]
    fn will_schedule_separate_queues_in_parallel() {
        timeout(|| {
            let (tx, rx)        = channel();
            let queue1          = queue();
            let queue2          = queue();
            let queue2_has_run  = Arc::new(Mutex::new(false));

            let queue1_check = queue2_has_run.clone();

            async(&queue1, move || {
                // The other task needs to start within 100ms for this to work
                sleep(Duration::from_millis(100));
                tx.send(*queue1_check.lock().unwrap()).unwrap();
            });
            async(&queue2, move || {
                *queue2_has_run.lock().unwrap() = true;
            });

            assert!(rx.recv().unwrap() == true);
        }, 500);
    }
}
<|endoftext|>


<commit_before>use super::CrateVersion;
use std::path::Path;
use rustc_serialize::json::Json;

use git2::build::RepoBuilder;
use git2::{Object, Oid, Reference, Delta, DiffFormat, ObjectType, Tree, Repository, ErrorClass,
           Error as GitError};
use std::str;

static INDEX_GIT_URL: &'static str = "https://github.com/rust-lang/crates.io-index";
static LAST_SEEN_REFNAME: &'static str = "refs/heads/crates-index-diff_last-seen";
static EMPTY_TREE_HASH: &'static str = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
static LINE_ADDED_INDICATOR: char = '+';

/// A wrapper for a repository of the crates.io index.
pub struct Index {
    pub seen_ref_name: &'static str,
    repo: Repository,
}

impl Index {
    pub fn repository(&self) -> &Repository {
        &self.repo
    }

    pub fn last_seen_reference(&self) -> Result<Reference, GitError> {
        self.repo.find_reference(self.seen_ref_name)
    }

    pub fn from_path_or_cloned<P>(path: P) -> Result<Index, GitError>
        where P: AsRef<Path>
    {
        let repo =
            Repository::open(path.as_ref()).or_else(|err| if err.class() == ErrorClass::Repository {
                    RepoBuilder::new().bare(true).clone(INDEX_GIT_URL, path.as_ref())
                } else {
                    Err(err)
                })?;

        Ok(Index {
            repo: repo,
            seen_ref_name: LAST_SEEN_REFNAME,
        })
    }

    pub fn fetch_changes(&self) -> Result<Vec<CrateVersion>, GitError> {
        let from = self.last_seen_reference()
            .and_then(|r| {
                r.target().ok_or_else(|| {
                    GitError::from_str("last-seen reference did not have a valid target")
                })
            })
            .or_else(|_| Oid::from_str(EMPTY_TREE_HASH))?;
        let to = {
            self.repo
                .find_remote("origin")
                .and_then(|mut r| r.fetch(&["refs/heads/*:refs/remotes/origin/*"], None, None))?;
            let latest_fetched_commit_oid = self.repo.refname_to_id("refs/remotes/origin/master")?;
            self.last_seen_reference()
                .and_then(|mut seen_ref| {
                    seen_ref.set_target(latest_fetched_commit_oid,
                                        "updating seen-ref head to latest fetched commit")
                })
                .or_else(|_err| {
                    self.repo
                        .reference(self.seen_ref_name,
                                   latest_fetched_commit_oid,
                                   true,
                                   "creating seen-ref at latest fetched commit")
                })?;
            latest_fetched_commit_oid
        };
        self.changes_from_objects(&self.repo.find_object(from, None)?,
                                  &self.repo.find_object(to, None)?)
    }

    pub fn changes<S1, S2>(&self, from: S1, to: S2) -> Result<Vec<CrateVersion>, GitError>
        where S1: AsRef<str>,
              S2: AsRef<str>
    {
        self.changes_from_objects(&self.repo.revparse_single(from.as_ref())?,
                                  &self.repo.revparse_single(to.as_ref())?)
    }

    pub fn changes_from_objects(&self, from: &Object, to: &Object) -> Result<Vec<CrateVersion>, GitError> {
        fn into_tree<'a>(repo: &'a Repository, obj: &Object) -> Result<Tree<'a>, GitError> {
            repo.find_tree(match obj.kind() {
                Some(ObjectType::Commit)
                    => obj.as_commit().expect("object of kind commit yields commit").tree_id(),
                _ => /* let it possibly fail later */ obj.id()
            })
        }
        let diff = self.repo
            .diff_tree_to_tree(Some(&into_tree(&self.repo, from)?),
                               Some(&into_tree(&self.repo, to)?),
                               None)?;
        let mut res = Vec::new();
        diff.print(DiffFormat::Patch, |delta, _, diffline| {
                if diffline.origin() != LINE_ADDED_INDICATOR {
                    return true;
                }

                if !match delta.status() {
                    Delta::Added | Delta::Modified => true,
                    _ => false,
                } {
                    return true;
                }

                let content = match str::from_utf8(diffline.content()) {
                    Ok(c) => c,
                    Err(_) => return true,
                };

                if let Some(c) = Json::from_str(content)
                    .ok()
                    .and_then(|json| CrateVersion::from_crates_diff_json(json).ok()) {
                    res.push(c)
                }
                return true;
            })
            .map(|_| res)
    }
}
<commit_msg>doc(index): docs for all remaining methods.<commit_after>use super::CrateVersion;
use std::path::Path;
use rustc_serialize::json::Json;

use git2::build::RepoBuilder;
use git2::{Object, Oid, Reference, Delta, DiffFormat, ObjectType, Tree, Repository, ErrorClass,
           Error as GitError};
use std::str;

static INDEX_GIT_URL: &'static str = "https://github.com/rust-lang/crates.io-index";
static LAST_SEEN_REFNAME: &'static str = "refs/heads/crates-index-diff_last-seen";
static EMPTY_TREE_HASH: &'static str = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
static LINE_ADDED_INDICATOR: char = '+';

/// A wrapper for a repository of the crates.io index.
pub struct Index {
    /// The name and path of the reference used to keep track of the last seen state of the crates.io
    /// repository. The default value is `refs/heads/crates-index-diff_last-seen`.
    pub seen_ref_name: &'static str,
    /// The crates.io repository.
    repo: Repository,
}

impl Index {
    /// Return the crates.io repository.
    pub fn repository(&self) -> &Repository {
        &self.repo
    }

    /// Return the reference pointing to the state we have seen after calling `fetch_changes()`.
    pub fn last_seen_reference(&self) -> Result<Reference, GitError> {
        self.repo.find_reference(self.seen_ref_name)
    }

    /// Return a new `Index` instance from the given `path`, which should contain a bare or non-bare
    /// clone of the `crates.io` index.
    /// If the directory does not contain the repository or does not exist, it will be cloned from
    /// the official location automatically (with complete history).
    pub fn from_path_or_cloned<P>(path: P) -> Result<Index, GitError>
        where P: AsRef<Path>
    {
        let repo =
            Repository::open(path.as_ref()).or_else(|err| if err.class() == ErrorClass::Repository {
                    RepoBuilder::new().bare(true).clone(INDEX_GIT_URL, path.as_ref())
                } else {
                    Err(err)
                })?;

        Ok(Index {
            repo: repo,
            seen_ref_name: LAST_SEEN_REFNAME,
        })
    }

    /// Return all `CrateVersion`s that are observed between the last time this method was called
    /// and the latest state of the `crates.io` index repository, which is obtained by fetching
    /// the remote called `origin`.
    /// The `last_seen_reference()` will be created or adjusted to point to the latest fetched state,
    /// which causes this method to have a different result each time it is called.
    pub fn fetch_changes(&self) -> Result<Vec<CrateVersion>, GitError> {
        let from = self.last_seen_reference()
            .and_then(|r| {
                r.target().ok_or_else(|| {
                    GitError::from_str("last-seen reference did not have a valid target")
                })
            })
            .or_else(|_| Oid::from_str(EMPTY_TREE_HASH))?;
        let to = {
            self.repo
                .find_remote("origin")
                .and_then(|mut r| r.fetch(&["refs/heads/*:refs/remotes/origin/*"], None, None))?;
            let latest_fetched_commit_oid = self.repo.refname_to_id("refs/remotes/origin/master")?;
            self.last_seen_reference()
                .and_then(|mut seen_ref| {
                    seen_ref.set_target(latest_fetched_commit_oid,
                                        "updating seen-ref head to latest fetched commit")
                })
                .or_else(|_err| {
                    self.repo
                        .reference(self.seen_ref_name,
                                   latest_fetched_commit_oid,
                                   true,
                                   "creating seen-ref at latest fetched commit")
                })?;
            latest_fetched_commit_oid
        };
        self.changes_from_objects(&self.repo.find_object(from, None)?,
                                  &self.repo.find_object(to, None)?)
    }

    /// Return all `CreateVersion`s observed between `from` and `to`. Both parameter are ref-specs
    /// pointing to either a commit or a tree.
    /// Learn more about specifying revisions
    /// in the
    /// [official documentation](https://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html)
    pub fn changes<S1, S2>(&self, from: S1, to: S2) -> Result<Vec<CrateVersion>, GitError>
        where S1: AsRef<str>,
              S2: AsRef<str>
    {
        self.changes_from_objects(&self.repo.revparse_single(from.as_ref())?,
                                  &self.repo.revparse_single(to.as_ref())?)
    }

    /// Similar to `changes()`, but requires `from` and `to` objects to be provided. They may point
    /// to either `Commit`s or `Tree`s.
    pub fn changes_from_objects(&self, from: &Object, to: &Object) -> Result<Vec<CrateVersion>, GitError> {
        fn into_tree<'a>(repo: &'a Repository, obj: &Object) -> Result<Tree<'a>, GitError> {
            repo.find_tree(match obj.kind() {
                Some(ObjectType::Commit)
                    => obj.as_commit().expect("object of kind commit yields commit").tree_id(),
                _ => /* let it possibly fail later */ obj.id()
            })
        }
        let diff = self.repo
            .diff_tree_to_tree(Some(&into_tree(&self.repo, from)?),
                               Some(&into_tree(&self.repo, to)?),
                               None)?;
        let mut res = Vec::new();
        diff.print(DiffFormat::Patch, |delta, _, diffline| {
                if diffline.origin() != LINE_ADDED_INDICATOR {
                    return true;
                }

                if !match delta.status() {
                    Delta::Added | Delta::Modified => true,
                    _ => false,
                } {
                    return true;
                }

                let content = match str::from_utf8(diffline.content()) {
                    Ok(c) => c,
                    Err(_) => return true,
                };

                if let Some(c) = Json::from_str(content)
                    .ok()
                    .and_then(|json| CrateVersion::from_crates_diff_json(json).ok()) {
                    res.push(c)
                }
                return true;
            })
            .map(|_| res)
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use ansi_term::Colour;

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ErrorKind {
    UndefinedVar = 0,
    TooFewArguments = 1,
}

#[derive(Debug, PartialEq)]
pub enum ErrorLevel {
    Info,
    Warning,
    Error,
}

#[derive(Debug, PartialEq)]
pub struct Error(pub ErrorLevel, pub ErrorKind, pub &'static str);

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let colour_level = match self.0 {
            ErrorLevel::Info => Colour::Yellow.paint("Info"),

            // ansi_term does not have orange for some reason
            ErrorLevel::Warning => Colour::RGB(255, 210, 0).paint("Warning"),
            ErrorLevel::Error => Colour::Red.paint("Error"),
        };

        // TODO: Remove clone
        write!(f, "[{}] (E{}) {}", colour_level, self.1 as i32, self.2)
    }
}

#[derive(Debug)]
pub struct ErrorStack(pub Vec<Error>);

impl fmt::Display for ErrorStack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.0 {
            // TODO: Remove this unwrap
            write!(f, "{}", error).unwrap();
        }

        write!(f, "")
    }
}
<commit_msg>Make error structs and enums copy and clonable<commit_after>use std::fmt;
use ansi_term::Colour;

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ErrorKind {
    UndefinedVar = 0,
    TooFewArguments = 1,
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ErrorLevel {
    Info,
    Warning,
    Error,
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Error(pub ErrorLevel, pub ErrorKind, pub &'static str);

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let colour_level = match self.0 {
            ErrorLevel::Info => Colour::Yellow.paint("Info"),

            // ansi_term does not have orange for some reason
            ErrorLevel::Warning => Colour::RGB(255, 210, 0).paint("Warning"),
            ErrorLevel::Error => Colour::Red.paint("Error"),
        };

        // TODO: Remove clone
        write!(f, "[{}] (E{}) {}", colour_level, self.1 as i32, self.2)
    }
}

#[derive(Debug)]
pub struct ErrorStack(pub Vec<Error>);

impl fmt::Display for ErrorStack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.0 {
            // TODO: Remove this unwrap
            write!(f, "{}", error).unwrap();
        }

        write!(f, "")
    }
}
<|endoftext|>


<commit_before>//! Abstractions for the events passed back from i3.

use reply;

/// Indicates the type of change.
pub enum WorkspaceChange {
    Focus,
    Init,
    Empty,
    Urgent
}

/// The data for the workspace event.
pub struct Workspace {
    /// The type of change.
    pub change: WorkspaceChange,
    /// Will be `Some` if the type of event affects the workspace.
    pub current: Option<reply::Workspace>,
    /// Will be `Some` only when `change == Focus` *and* there was a previous workspace.
    /// Note that if the previous workspace was empty it will get destroyed when switching, but
    /// will still appear here.
    pub old: Option<reply::Workspace>
}

/// Indicates the type of change (currently only unspecified).
pub enum OutputChange {
    Unspecified
}

/// The data for the output event.
pub struct Output {
    /// The type of change.
    pub change: OutputChange
}

/// The data for the mode event.
pub struct Mode {
    /// The name of current mode in use. It is the same as specified in config when creating a
    /// mode. The default mode is simply named default.
    pub change: String
}

/// The type of the window change.
pub enum WindowChange {
    /// The window has become managed by i3.
    New,
    /// The window has closed>.
    Close,
    /// The window has received input focus.
    Focus,
    /// The window's title has changed.
    Title,
    /// The window has entered or exited fullscreen mode.
    FullscreenMode,
    /// The window has changed its position in the tree.
    Move,
    /// The window has transitioned to or from floating.
    Floating,
    /// The window has become urgent or lost its urgent status.
    Urgent
}

/// The data for the window event.
pub struct Window {
    /// Indicates the type of change
    pub change: WindowChange,
    pub container: reply::Tree // FIXME: Figure out if the entire tree is passed or just the parent
}

/// The data for the barconfig_update event.
pub type BarConfig = reply::BarConfig;

pub enum InputType {
    Keyboard,
    Mouse
}

/// Contains details about the binding that was run.
pub struct Binding {
    /// The i3 command that is configured to run for this binding.
    pub command: String,

    /// The modifier keys that were configured with this binding.
    pub mods: Vec<String>,

    /// If the binding was configured with blindcode, this will be the key code that was given for
    /// the binding. If the binding is a mouse binding, it will be the number of times the mouse
    /// button was pressed. Otherwise it will be 0.
    pub input_code: i32,

    /// If this is a keyboard binding that was configured with bindsym, this field will contain the
    /// given symbol. Otherwise it will be None.
    pub symbol: Option<String>,

    /// Will be Keyboard or Mouse depending on whether this was a keyboard or mouse binding.
    pub input_type: InputType
}

/// The type of change.
pub enum BindingEventChange {
    Run
}

/// This event reports on the details of a binding that ran a command because of user input. The
/// change field indicates what sort of binding event was triggered (right now it will always be
/// "run" but may be expanded in the future).
pub struct BindingEvent {
    /// Indicates what sort of binding event was triggered (right now it will always be "run" but
    /// that may be expanded in the future).
    change: BindingEventChange,

}
<commit_msg>Work on event structs<commit_after>//! Abstractions for the events passed back from i3.

use reply;

/// Indicates the type of change.
pub enum WorkspaceChange {
    Focus,
    Init,
    Empty,
    Urgent
}

/// The data for the workspace event.
pub struct Workspace {
    /// The type of change.
    pub change: WorkspaceChange,
    /// Will be `Some` if the type of event affects the workspace.
    pub current: Option<reply::Workspace>,
    /// Will be `Some` only when `change == Focus` *and* there was a previous workspace.
    /// Note that if the previous workspace was empty it will get destroyed when switching, but
    /// will still appear here.
    pub old: Option<reply::Workspace>
}

/// Indicates the type of change (currently only unspecified).
pub enum OutputChange {
    Unspecified
}

/// The data for the output event.
pub struct Output {
    /// The type of change.
    pub change: OutputChange
}

/// The data for the mode event.
pub struct Mode {
    /// The name of current mode in use. It is the same as specified in config when creating a
    /// mode. The default mode is simply named default.
    pub change: String
}

/// The type of the window change.
pub enum WindowChange {
    /// The window has become managed by i3.
    New,
    /// The window has closed>.
    Close,
    /// The window has received input focus.
    Focus,
    /// The window's title has changed.
    Title,
    /// The window has entered or exited fullscreen mode.
    FullscreenMode,
    /// The window has changed its position in the tree.
    Move,
    /// The window has transitioned to or from floating.
    Floating,
    /// The window has become urgent or lost its urgent status.
    Urgent
}

/// The data for the window event.
pub struct Window {
    /// Indicates the type of change
    pub change: WindowChange,
    pub container: reply::Tree // FIXME: Figure out if the entire tree is passed or just the parent
}

/// The data for the barconfig_update event.
pub type BarConfig = reply::BarConfig;

pub enum InputType {
    Keyboard,
    Mouse
}

/// Contains details about the binding that was run.
pub struct Binding {
    /// The i3 command that is configured to run for this binding.
    pub command: String,

    /// The modifier keys that were configured with this binding.
    pub mods: Vec<String>,

    /// If the binding was configured with blindcode, this will be the key code that was given for
    /// the binding. If the binding is a mouse binding, it will be the number of times the mouse
    /// button was pressed. Otherwise it will be 0.
    pub input_code: i32,

    /// If this is a keyboard binding that was configured with bindsym, this field will contain the
    /// given symbol. Otherwise it will be None.
    pub symbol: Option<String>,

    /// Will be Keyboard or Mouse depending on whether this was a keyboard or mouse binding.
    pub input_type: InputType
}

/// The type of change.
pub enum BindingEventChange {
    Run
}

/// This event reports on the details of a binding that ran a command because of user input. The
/// change field indicates what sort of binding event was triggered (right now it will always be
/// "run" but may be expanded in the future).
pub struct BindingEvent {
    /// Indicates what sort of binding event was triggered (right now it will always be "run" but
    /// that may be expanded in the future).
    pub change: BindingEventChange,
    pub binding: Binding
}
<|endoftext|>