<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, ArgGroup, App};

extern crate isbfc;
use isbfc::Token;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

struct CompileState {
    output: String,
    loopnum: i32,
    ifnum: i32,
    outbuffsize: i32,
}

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
            .short("S")
            .help("Assemble but do not link"))
        .arg(Arg::with_name("dump_ir")
            .long("dumpir")
            .help("Dump intermediate representation; for debugging"))
        .group(ArgGroup::with_name("actions").args(&["output_asm", "dump_ir"]))
        .arg(Arg::with_name("debugging_symbols")
            .short("g")
            .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
            .short("o")
            .help("Output file name")
            .takes_value(true)
            .empty_values(false)
            .value_name("file"))
        .arg(Arg::with_name("tape_size")
            .long("tape-size")
            .help("Size of tape; defaults to 8192")
            .takes_value(true)
            .empty_values(false)
            .value_name("bytes"))
        .arg(Arg::with_name("FILENAME")
            .help("Source file to compile")
            .required(true)
            .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let tokens = parse(&code);
    let tokens = optimize(tokens);

    if matches.is_present("dump_ir") {
        let output = dump_ir(tokens);
        if let Some(out_name) = matches.value_of("out_name") {
            let mut irfile = File::create(out_name).unwrap();
            irfile.write_all(&output.into_bytes()).unwrap();
        } else {
            print!("{}", output);
        }
    } else if matches.is_present("output_asm") {
        let output = compile(tokens, tape_size);
        let def_name = format!("{}.s", name);
        let out_name = matches.value_of("out_name").unwrap_or(&def_name);
        let mut asmfile = File::create(out_name).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        let output = compile(tokens, tape_size);
        let out_name = matches.value_of("out_name").unwrap_or(name);
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn compile_iter(state: &mut CompileState, tokens: Vec<Token>) {
    let mut outbuffpos = 0;
    for token in tokens {
        match token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset * 8))
                };
                if value == 1 && dest == "%r12" {
                    state.output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    state.output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    state.output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    state.output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            }
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx * 8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx * 8))
                };

                if mul != -1 && mul != 1 {
                    state.output.push_str(&format!(concat!("    movq {}, %rax\n",
                                                           "    movq ${}, %rdx\n",
                                                           "    mulq %rdx\n"),
                                                   src,
                                                   mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    state.output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    state.output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    state.output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            }
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    state.output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    state.output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    state.output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset * 8));
                }
            }
            Move(offset) => {
                if offset != 0 {
                    state.output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        state.output.push_str(&format!("    addq ${}, %rbx\n", offset * 8));
                    } else {
                        state.output.push_str(&format!("    subq ${}, %rbx\n", -offset * 8));
                    }
                    state.output.push_str("    movq (%rbx), %r12\n");
                }
            }
            Loop(content) => {
                state.loopnum += 1;
                let curloop = state.loopnum;
                state.output.push_str(&format!(concat!("    jmp endloop{}\n", "    loop{}:\n"),
                                               curloop,
                                               curloop));

                compile_iter(state, content);

                state.output.push_str(&format!(concat!("    endloop{}:\n",
                                                       "    test %r12, %r12\n",
                                                       "    jnz loop{}\n"),
                                               curloop,
                                               curloop))
            }
            If(offset, content) => {
                state.ifnum += 1;
                let curif = state.ifnum;
                if offset == 0 {
                    state.output.push_str("    test %r12, %r12\n");
                } else {
                    state.output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset * 8));
                }
                state.output.push_str(&format!("    jz endif{}\n", curif));

                compile_iter(state, content);

                state.output.push_str(&format!("    endif{}:\n", curif))
            }
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                state.loopnum += 1;
                state.output.push_str(&format!(concat!("    movq %r12, (%rbx)\n",
                                                       "    jmp endloop{}\n",
                                                       "    loop{}:\n"),
                                               state.loopnum,
                                               state.loopnum));
                if offset > 0 {
                    state.output.push_str(&format!("    addq ${}, %rbx\n", offset * 8));
                } else {
                    state.output.push_str(&format!("    subq ${}, %rbx\n", -offset * 8));
                }
                state.output.push_str(&format!(concat!("    endloop{}:\n",
                                                       "    cmp $0, (%rbx)\n",
                                                       "    jnz loop{}\n",
                                                       "    movq (%rbx), %r12\n"),
                                               state.loopnum,
                                               state.loopnum));
            }
            Input => {
                state.output.push_str(concat!("\n    xor %rax, %rax\n",
                                              "    xor %rdi, %rdi\n",
                                              "    movq %rbx, %rsi\n",
                                              "    movq $1, %rdx\n",
                                              "    syscall\n",
                                              "    movq (%rbx), %r12\n\n"))
            }
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    state.output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    state.output.push_str(&format!("    movq {}(%rbx), %rax\n", offset * 8));
                    state.output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    state.output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    state.output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            }
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                state.output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            }
            Output => {
                state.output.push_str(&format!(concat!("    movq $1, %rax\n",
                                                       "    movq $1, %rdi\n",
                                                       "    movq $strbuff, %rsi\n",
                                                       "    movq ${}, %rdx\n",
                                                       "    syscall\n\n"),
                                               outbuffpos));

                if state.outbuffsize < outbuffpos + 8 {
                    state.outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }
}

fn compile(tokens: Vec<Token>, tape_size: i32) -> String {
    println!("Compiling...");

    let mut state = CompileState {
        output: String::new(),
        loopnum: 0,
        ifnum: 0,
        outbuffsize: 0,
    };

    compile_iter(&mut state, tokens);

    // Exit syscall
    state.output.push_str(concat!("\n    movq $60, %rax\n",
                                  "    movq $0, %rdi\n",
                                  "    syscall\n"));

    format!(concat!(".section .bss\n",
                    "    .lcomm strbuff, {outbuffsize}\n",
                    "    .lcomm mem, {}\n",
                    "    .set startidx, mem + {}\n",
                    ".section .text\n",
                    ".global _start\n",
                    "_start:\n",
                    "    xor %r12, %r12\n",
                    "    movq $startidx, %rbx\n\n{}"),
            tape_size,
            tape_size / 2,
            state.output,
            outbuffsize = state.outbuffsize)
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn()
            .unwrap();
    }
}

fn dump_ir_iter(output: &mut String, tokens: Vec<Token>, indent_level: usize) {
    let indent = String::from_utf8(vec![b' '; indent_level * 4]).unwrap();
    for token in tokens {
        match token {
            Loop(content) => {
                output.push_str(&format!("{}Loop(content=[\n", indent));
                dump_ir_iter(output, content, indent_level + 1);
                output.push_str(&format!("{}])\n", indent));
            }
            If(offset, content) => {
                output.push_str(&format!("{}If(offset={}, content=[\n", offset, indent));
                dump_ir_iter(output, content, indent_level + 1);
                output.push_str(&format!("{}])\n", indent));
            }
            _ => output.push_str(&format!("{}{:?}\n", indent, token)),
        }
    }
}

fn dump_ir(tokens: Vec<Token>) -> String {
    let mut output = String::new();
    dump_ir_iter(&mut output, tokens, 0);
    output
}
<commit_msg>Fix handling of If in dumpir<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, ArgGroup, App};

extern crate isbfc;
use isbfc::Token;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

struct CompileState {
    output: String,
    loopnum: i32,
    ifnum: i32,
    outbuffsize: i32,
}

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
            .short("S")
            .help("Assemble but do not link"))
        .arg(Arg::with_name("dump_ir")
            .long("dumpir")
            .help("Dump intermediate representation; for debugging"))
        .group(ArgGroup::with_name("actions").args(&["output_asm", "dump_ir"]))
        .arg(Arg::with_name("debugging_symbols")
            .short("g")
            .help("Generate debugging information"))
        .arg(Arg::with_name("out_name")
            .short("o")
            .help("Output file name")
            .takes_value(true)
            .empty_values(false)
            .value_name("file"))
        .arg(Arg::with_name("tape_size")
            .long("tape-size")
            .help("Size of tape; defaults to 8192")
            .takes_value(true)
            .empty_values(false)
            .value_name("bytes"))
        .arg(Arg::with_name("FILENAME")
            .help("Source file to compile")
            .required(true)
            .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let tokens = parse(&code);
    let tokens = optimize(tokens);

    if matches.is_present("dump_ir") {
        let output = dump_ir(tokens);
        if let Some(out_name) = matches.value_of("out_name") {
            let mut irfile = File::create(out_name).unwrap();
            irfile.write_all(&output.into_bytes()).unwrap();
        } else {
            print!("{}", output);
        }
    } else if matches.is_present("output_asm") {
        let output = compile(tokens, tape_size);
        let def_name = format!("{}.s", name);
        let out_name = matches.value_of("out_name").unwrap_or(&def_name);
        let mut asmfile = File::create(out_name).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        let output = compile(tokens, tape_size);
        let out_name = matches.value_of("out_name").unwrap_or(name);
        let debug = matches.is_present("debugging_symbols");
        asm_and_link(&output, &name, &out_name, debug);
    }
}

fn compile_iter(state: &mut CompileState, tokens: Vec<Token>) {
    let mut outbuffpos = 0;
    for token in tokens {
        match token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset * 8))
                };
                if value == 1 && dest == "%r12" {
                    state.output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    state.output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    state.output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    state.output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            }
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx * 8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx * 8))
                };

                if mul != -1 && mul != 1 {
                    state.output.push_str(&format!(concat!("    movq {}, %rax\n",
                                                           "    movq ${}, %rdx\n",
                                                           "    mulq %rdx\n"),
                                                   src,
                                                   mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    state.output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    state.output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    state.output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            }
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    state.output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    state.output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    state.output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset * 8));
                }
            }
            Move(offset) => {
                if offset != 0 {
                    state.output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        state.output.push_str(&format!("    addq ${}, %rbx\n", offset * 8));
                    } else {
                        state.output.push_str(&format!("    subq ${}, %rbx\n", -offset * 8));
                    }
                    state.output.push_str("    movq (%rbx), %r12\n");
                }
            }
            Loop(content) => {
                state.loopnum += 1;
                let curloop = state.loopnum;
                state.output.push_str(&format!(concat!("    jmp endloop{}\n", "    loop{}:\n"),
                                               curloop,
                                               curloop));

                compile_iter(state, content);

                state.output.push_str(&format!(concat!("    endloop{}:\n",
                                                       "    test %r12, %r12\n",
                                                       "    jnz loop{}\n"),
                                               curloop,
                                               curloop))
            }
            If(offset, content) => {
                state.ifnum += 1;
                let curif = state.ifnum;
                if offset == 0 {
                    state.output.push_str("    test %r12, %r12\n");
                } else {
                    state.output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset * 8));
                }
                state.output.push_str(&format!("    jz endif{}\n", curif));

                compile_iter(state, content);

                state.output.push_str(&format!("    endif{}:\n", curif))
            }
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                state.loopnum += 1;
                state.output.push_str(&format!(concat!("    movq %r12, (%rbx)\n",
                                                       "    jmp endloop{}\n",
                                                       "    loop{}:\n"),
                                               state.loopnum,
                                               state.loopnum));
                if offset > 0 {
                    state.output.push_str(&format!("    addq ${}, %rbx\n", offset * 8));
                } else {
                    state.output.push_str(&format!("    subq ${}, %rbx\n", -offset * 8));
                }
                state.output.push_str(&format!(concat!("    endloop{}:\n",
                                                       "    cmp $0, (%rbx)\n",
                                                       "    jnz loop{}\n",
                                                       "    movq (%rbx), %r12\n"),
                                               state.loopnum,
                                               state.loopnum));
            }
            Input => {
                state.output.push_str(concat!("\n    xor %rax, %rax\n",
                                              "    xor %rdi, %rdi\n",
                                              "    movq %rbx, %rsi\n",
                                              "    movq $1, %rdx\n",
                                              "    syscall\n",
                                              "    movq (%rbx), %r12\n\n"))
            }
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    state.output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    state.output.push_str(&format!("    movq {}(%rbx), %rax\n", offset * 8));
                    state.output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    state.output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    state.output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            }
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                state.output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            }
            Output => {
                state.output.push_str(&format!(concat!("    movq $1, %rax\n",
                                                       "    movq $1, %rdi\n",
                                                       "    movq $strbuff, %rsi\n",
                                                       "    movq ${}, %rdx\n",
                                                       "    syscall\n\n"),
                                               outbuffpos));

                if state.outbuffsize < outbuffpos + 8 {
                    state.outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }
}

fn compile(tokens: Vec<Token>, tape_size: i32) -> String {
    println!("Compiling...");

    let mut state = CompileState {
        output: String::new(),
        loopnum: 0,
        ifnum: 0,
        outbuffsize: 0,
    };

    compile_iter(&mut state, tokens);

    // Exit syscall
    state.output.push_str(concat!("\n    movq $60, %rax\n",
                                  "    movq $0, %rdi\n",
                                  "    syscall\n"));

    format!(concat!(".section .bss\n",
                    "    .lcomm strbuff, {outbuffsize}\n",
                    "    .lcomm mem, {}\n",
                    "    .set startidx, mem + {}\n",
                    ".section .text\n",
                    ".global _start\n",
                    "_start:\n",
                    "    xor %r12, %r12\n",
                    "    movq $startidx, %rbx\n\n{}"),
            tape_size,
            tape_size / 2,
            state.output,
            outbuffsize = state.outbuffsize)
}

fn asm_and_link(code: &str, name: &str, out_name: &str, debug: bool) {
    println!("Assembling...");

    let mut command = Command::new("as");
    if debug {
        command.arg("-g");
    }
    let mut child = command
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn()
            .unwrap();
    }
}

fn dump_ir_iter(output: &mut String, tokens: Vec<Token>, indent_level: usize) {
    let indent = String::from_utf8(vec![b' '; indent_level * 4]).unwrap();
    for token in tokens {
        match token {
            Loop(content) => {
                output.push_str(&format!("{}Loop(content=[\n", indent));
                dump_ir_iter(output, content, indent_level + 1);
                output.push_str(&format!("{}])\n", indent));
            }
            If(offset, content) => {
                output.push_str(&format!("{}If(offset={}, content=[\n", indent, offset));
                dump_ir_iter(output, content, indent_level + 1);
                output.push_str(&format!("{}])\n", indent));
            }
            _ => output.push_str(&format!("{}{:?}\n", indent, token)),
        }
    }
}

fn dump_ir(tokens: Vec<Token>) -> String {
    let mut output = String::new();
    dump_ir_iter(&mut output, tokens, 0);
    output
}
<|endoftext|>


<commit_before>extern crate device_tree_source;
#[macro_use]
extern crate clap;

mod change_tracker;

use std::process::Command;
use std::path::{Path, PathBuf};
use std::fs::remove_file;
use std::io::{self, BufRead, Write};
use std::iter::Iterator;
use std::fmt::{self, Display, Formatter};
// use std::fs::File;

use device_tree_source::parser::parse_dt;
use device_tree_source::tree::Offset;

use device_tree_source::include::{IncludeBounds, IncludeMethod, IncludeError, BoundsError, include_files};

use change_tracker::LabelStore;

const CPP_OUTPUT_NAME: &'static str = "dts_viewer_tmp.dts";

// General idea:
//  Run CPP
//  Parse file for DTS includes and replace with include contents
//  Find byte starts/ends for each file
//  Parse file to create device tree
//  create mapping of objects to byte offset starting points
//  - not perfect with offsets stored in object, but it will do
//  create mapping of full paths to objects
//  create mapping of labels to full paths
//  Parse device tree to create changes
//  Use mapping and file starts to pair changes to file byte offsets
//  Turn file byte offsets to file lines/cols
//  TODO: ???
//  TODO: Profit!
//  Oh, and somehow display the damn information
fn main() {
    let matches = clap_app!(dts_viewer =>
            (version: crate_version!())
            (author: "Gabriel s. <ga29smith@gmail.com>")
            (@arg file: +required "DTS file to parse")
            (@arg no_defaults: -n --no_defaults "Disable default includes. \
                An 'include' directory, if it exists, is automatically included")
            (@arg include: -I ... +takes_value "Additional files to pass to CPP as an include")
        )
        .get_matches();

    let file_name = matches.value_of("file").unwrap();

    let file = Path::new(file_name);
    let parent = file.parent().expect("Could not get parent directory of file");

    let mut cpp_command = Command::new("gcc");
    cpp_command.args(&["-H", "-E", "-nostdinc"])
        .args(&["-undef", "-D__DTS__", "-x", "assembler-with-cpp"])
        .args(&["-o", CPP_OUTPUT_NAME])
        .arg(&file_name);

    if !matches.is_present("no_defaults") {
        cpp_command.args(&["-I", parent.to_str().unwrap()]);
        let include_dir = parent.join("include/");
        if include_dir.is_dir() {
            cpp_command.args(&["-I", include_dir.to_str().unwrap()]);
        }
    }

    if let Some(includes) = matches.values_of("include") {
        for include in includes {
            let include_dir = parent.join(include);
            if include_dir.is_dir() {
                cpp_command.args(&["-I", include_dir.to_str().unwrap()]);
            }
        }
    }

    let include_output = cpp_command.output().expect("Failed to execute CPP");
    let cpp_stderr = String::from_utf8_lossy(&include_output.stderr);
    if !include_output.status.success() {
        println!("Failed to execute CPP. Error message is below.");
        print!("{}", cpp_stderr);
        return;
    }

    let (buffer, bounds) = match include_files(Path::new(CPP_OUTPUT_NAME)) {
        Ok(x) => x,
        Err(e) => {
            match e {
                IncludeError::IOError(err) => println!("IO error while trying to open file: {}", err),
                IncludeError::LinemarkerInDtsi(path) => println!("Extraneous linemarker found in DT include: {}", path.to_string_lossy()),
                IncludeError::ParseError(_) => println!("Failed to convert line to byte offset for bounds tracking."),
                IncludeError::NoBoundReturned(path) => println!("No bounds returned after parsing file: {}", path.to_string_lossy()),
            }
            return;
        }
    };

    if let Err(e) = remove_file(Path::new(CPP_OUTPUT_NAME)) {
        println!("-- Failed to delete temp file: {:?}", e);
    }

    // println!("{:#?}", bounds);
    // {
    //     let mut total_dts_dump = File::create("total_dts_dump.dts").unwrap();
    //     total_dts_dump.write_all(&buffer).unwrap()
    // }

    let include_tree = IncludeTree::bounds_to_tree(&bounds);
    if let Some(tree) = include_tree {
        println!("{}", tree);
    } else {
        println!("-- Could not constuct include tree from bounds!");
    }

    let (boot_info, ammends) = match parse_dt(&buffer) {
        Ok(dt) => dt,
        Err(err) => {
            println!("{:?}", err);
            return;
        }
    };

    // TODO: perform secondary checks and jazz (only smooth)
    //
    // checkes to do:
    // duplicate node names
    // duplicate property names
    // node name format (only one @)
    // unit addr vs reg/ranges property
    // duplicate labels -- handled by creation of alias store
    // duplicate explict phandles
    // name property does not match name
    // check if X is cell only
    // #address-cells
    // #size-cells
    // #interrupt-cells
    // check if X is string only
    // device_type
    // model
    // status
    // fixup addr size cells
    // check reg property format
    // check ranges property format
    // avoid default addr size?
    // obsolete chosen iterrupt controller
    //
    // maybe:
    // fixup phandle refs
    // fixup path refs

    let mut store = LabelStore::new();
    store.fill(&boot_info, &ammends);

    loop {
        print!("Enter alias or path: ");
        io::stdout().flush().expect("Error flushing stdout");

        let mut line = String::new();
        let stdin = io::stdin();
        stdin.lock().read_line(&mut line).expect("Error reading from stdin");
        let line = line.trim();

        if line.is_empty() {
            break;
        }

        let path = if line.starts_with('/') {
            Some(PathBuf::from(line))
        } else {
            let line = if line.starts_with('&') {
                &line[1..]
            } else {
                line
            };
            // TODO:
            // if label covers multiple paths (after saving old aliases is implemented)
            //     ask user which one they want as well as why this is being asked
            // show all changes along the way
            match store.path_from_label(line) {
                Some(path) => {
                    println!("Path: {}", path.to_string_lossy());
                    Some(path.to_owned())
                }
                None => {
                    println!("Label points to no path");
                    None
                }
            }
        };

        if let Some(path) = path {
            match store.changes_from_path(&path) {
                Some(changes) => {
                    for change in changes {
                        let offset = change.get_offset();
                        // println!("Start offset: {}", offset);
                        match bounds.binary_search_by(|b| {
                            use std::cmp::Ordering::*;
                            match (b.start().cmp(&offset), b.end().cmp(&offset)) {
                                (Less, Greater) | (Equal, Greater) => Equal,
                                (Greater, Greater) => Greater,
                                (Equal, Less) | (Less, Less) | (Less, Equal) | (Equal, Equal)
                                    => Less,
                                _ => unreachable!(),
                            }
                        }) {
                            Ok(off) => {
                                let bound = &bounds[off];
                                print!("File: {}", bound.child_path().to_string_lossy());

                                match bound.file_line_from_global(&buffer, offset) {
                                    Ok((line, col)) => {
                                        println!(", Line: {}, Column: {}", line, col)
                                    }
                                    Err(err) => match err {
                                        BoundsError::ParseError(_) =>
                                            println!("Offset ({}) could not be converted to line.",
                                                     offset),
                                        BoundsError::IOError(_) =>
                                            println!("Failed to open file: {}",
                                                     bound.child_path().to_string_lossy()),
                                        BoundsError::NotWithinBounds =>
                                            println!("File offset ({}) was supposed to be in\
                                                         bound. {:?}",
                                                     offset,
                                                     bound),
                                    }
                                }
                            }
                            Err(_) => println!("-- Could not find file for offset {}", offset),
                        }
                        println!("{}\n", change);
                    }
                }
                None => println!("Nothing at path"),
            }
        }
    }
}

#[derive(Debug)]
struct IncludeTree {
    path: PathBuf,
    method: IncludeMethod,
    includes: Vec<IncludeTree>,
}

impl IncludeTree {
    fn bounds_to_tree(bounds: &[IncludeBounds]) -> Option<IncludeTree> {
        if let Some(first) = bounds.first() {
            let mut tree = IncludeTree {
                path: first.child_path().to_owned(),
                includes: Vec::new(),
                method: first.include_method().clone(),
            };

            //TODO: we don't really need the filter, benchmark speed w/wo
            for sub_bounds in bounds.split(|b| b.child_path() == first.child_path())
                                    .filter(|s| !s.is_empty()) {
                if let Some(sub_tree) = Self::bounds_to_tree(sub_bounds) {
                    tree.includes.push(sub_tree);
                }
            }

            Some(tree)
        } else {
            None
        }
    }

    fn write(&self, f: &mut Formatter, prefix: &str) -> fmt::Result {
        let mut next_prefix = prefix.to_owned();
        next_prefix.push_str(" |-");

        let method = match self.method {
            IncludeMethod::CPP => "CPP",
            IncludeMethod::DTS => "DTS",
        };
        writeln!(f, "{} {}: {}", prefix, self.path.to_string_lossy(), method)?;
        for t in &self.includes {
            t.write(f, &next_prefix)?;
        }

        Ok(())
    }
}

impl Display for IncludeTree {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.write(f, "")
    }
}
<commit_msg>Remove CPP -H flag<commit_after>extern crate device_tree_source;
#[macro_use]
extern crate clap;

mod change_tracker;

use std::process::Command;
use std::path::{Path, PathBuf};
use std::fs::remove_file;
use std::io::{self, BufRead, Write};
use std::iter::Iterator;
use std::fmt::{self, Display, Formatter};
// use std::fs::File;

use device_tree_source::parser::parse_dt;
use device_tree_source::tree::Offset;

use device_tree_source::include::{IncludeBounds, IncludeMethod, IncludeError, BoundsError, include_files};

use change_tracker::LabelStore;

const CPP_OUTPUT_NAME: &'static str = "dts_viewer_tmp.dts";

// General idea:
//  Run CPP
//  Parse file for DTS includes and replace with include contents
//  Find byte starts/ends for each file
//  Parse file to create device tree
//  create mapping of objects to byte offset starting points
//  - not perfect with offsets stored in object, but it will do
//  create mapping of full paths to objects
//  create mapping of labels to full paths
//  Parse device tree to create changes
//  Use mapping and file starts to pair changes to file byte offsets
//  Turn file byte offsets to file lines/cols
//  TODO: ???
//  TODO: Profit!
//  Oh, and somehow display the damn information
fn main() {
    let matches = clap_app!(dts_viewer =>
            (version: crate_version!())
            (author: "Gabriel s. <ga29smith@gmail.com>")
            (@arg file: +required "DTS file to parse")
            (@arg no_defaults: -n --no_defaults "Disable default includes. \
                An 'include' directory, if it exists, is automatically included")
            (@arg include: -I ... +takes_value "Additional files to pass to CPP as an include")
        )
        .get_matches();

    let file_name = matches.value_of("file").unwrap();

    let file = Path::new(file_name);
    let parent = file.parent().expect("Could not get parent directory of file");

    let mut cpp_command = Command::new("gcc");
    cpp_command.args(&["-E", "-nostdinc"])
        .args(&["-undef", "-D__DTS__", "-x", "assembler-with-cpp"])
        .args(&["-o", CPP_OUTPUT_NAME])
        .arg(&file_name);

    if !matches.is_present("no_defaults") {
        cpp_command.args(&["-I", parent.to_str().unwrap()]);
        let include_dir = parent.join("include/");
        if include_dir.is_dir() {
            cpp_command.args(&["-I", include_dir.to_str().unwrap()]);
        }
    }

    if let Some(includes) = matches.values_of("include") {
        for include in includes {
            let include_dir = parent.join(include);
            if include_dir.is_dir() {
                cpp_command.args(&["-I", include_dir.to_str().unwrap()]);
            }
        }
    }

    let include_output = cpp_command.output().expect("Failed to execute CPP");
    let cpp_stderr = String::from_utf8_lossy(&include_output.stderr);
    if !include_output.status.success() {
        println!("Failed to execute CPP. Error message is below.");
        print!("{}", cpp_stderr);
        return;
    }

    let (buffer, bounds) = match include_files(Path::new(CPP_OUTPUT_NAME)) {
        Ok(x) => x,
        Err(e) => {
            match e {
                IncludeError::IOError(err) => println!("IO error while trying to open file: {}", err),
                IncludeError::LinemarkerInDtsi(path) => println!("Extraneous linemarker found in DT include: {}", path.to_string_lossy()),
                IncludeError::ParseError(_) => println!("Failed to convert line to byte offset for bounds tracking."),
                IncludeError::NoBoundReturned(path) => println!("No bounds returned after parsing file: {}", path.to_string_lossy()),
            }
            return;
        }
    };

    if let Err(e) = remove_file(Path::new(CPP_OUTPUT_NAME)) {
        println!("-- Failed to delete temp file: {:?}", e);
    }

    // println!("{:#?}", bounds);
    // {
    //     let mut total_dts_dump = File::create("total_dts_dump.dts").unwrap();
    //     total_dts_dump.write_all(&buffer).unwrap()
    // }

    let include_tree = IncludeTree::bounds_to_tree(&bounds);
    if let Some(tree) = include_tree {
        println!("{}", tree);
    } else {
        println!("-- Could not constuct include tree from bounds!");
    }

    let (boot_info, ammends) = match parse_dt(&buffer) {
        Ok(dt) => dt,
        Err(err) => {
            println!("{:?}", err);
            return;
        }
    };

    // TODO: perform secondary checks and jazz (only smooth)
    //
    // checkes to do:
    // duplicate node names
    // duplicate property names
    // node name format (only one @)
    // unit addr vs reg/ranges property
    // duplicate labels -- handled by creation of alias store
    // duplicate explict phandles
    // name property does not match name
    // check if X is cell only
    // #address-cells
    // #size-cells
    // #interrupt-cells
    // check if X is string only
    // device_type
    // model
    // status
    // fixup addr size cells
    // check reg property format
    // check ranges property format
    // avoid default addr size?
    // obsolete chosen iterrupt controller
    //
    // maybe:
    // fixup phandle refs
    // fixup path refs

    let mut store = LabelStore::new();
    store.fill(&boot_info, &ammends);

    loop {
        print!("Enter alias or path: ");
        io::stdout().flush().expect("Error flushing stdout");

        let mut line = String::new();
        let stdin = io::stdin();
        stdin.lock().read_line(&mut line).expect("Error reading from stdin");
        let line = line.trim();

        if line.is_empty() {
            break;
        }

        let path = if line.starts_with('/') {
            Some(PathBuf::from(line))
        } else {
            let line = if line.starts_with('&') {
                &line[1..]
            } else {
                line
            };
            // TODO:
            // if label covers multiple paths (after saving old aliases is implemented)
            //     ask user which one they want as well as why this is being asked
            // show all changes along the way
            match store.path_from_label(line) {
                Some(path) => {
                    println!("Path: {}", path.to_string_lossy());
                    Some(path.to_owned())
                }
                None => {
                    println!("Label points to no path");
                    None
                }
            }
        };

        if let Some(path) = path {
            match store.changes_from_path(&path) {
                Some(changes) => {
                    for change in changes {
                        let offset = change.get_offset();
                        // println!("Start offset: {}", offset);
                        match bounds.binary_search_by(|b| {
                            use std::cmp::Ordering::*;
                            match (b.start().cmp(&offset), b.end().cmp(&offset)) {
                                (Less, Greater) | (Equal, Greater) => Equal,
                                (Greater, Greater) => Greater,
                                (Equal, Less) | (Less, Less) | (Less, Equal) | (Equal, Equal)
                                    => Less,
                                _ => unreachable!(),
                            }
                        }) {
                            Ok(off) => {
                                let bound = &bounds[off];
                                print!("File: {}", bound.child_path().to_string_lossy());

                                match bound.file_line_from_global(&buffer, offset) {
                                    Ok((line, col)) => {
                                        println!(", Line: {}, Column: {}", line, col)
                                    }
                                    Err(err) => match err {
                                        BoundsError::ParseError(_) =>
                                            println!("Offset ({}) could not be converted to line.",
                                                     offset),
                                        BoundsError::IOError(_) =>
                                            println!("Failed to open file: {}",
                                                     bound.child_path().to_string_lossy()),
                                        BoundsError::NotWithinBounds =>
                                            println!("File offset ({}) was supposed to be in\
                                                         bound. {:?}",
                                                     offset,
                                                     bound),
                                    }
                                }
                            }
                            Err(_) => println!("-- Could not find file for offset {}", offset),
                        }
                        println!("{}\n", change);
                    }
                }
                None => println!("Nothing at path"),
            }
        }
    }
}

#[derive(Debug)]
struct IncludeTree {
    path: PathBuf,
    method: IncludeMethod,
    includes: Vec<IncludeTree>,
}

impl IncludeTree {
    fn bounds_to_tree(bounds: &[IncludeBounds]) -> Option<IncludeTree> {
        if let Some(first) = bounds.first() {
            let mut tree = IncludeTree {
                path: first.child_path().to_owned(),
                includes: Vec::new(),
                method: first.include_method().clone(),
            };

            //TODO: we don't really need the filter, benchmark speed w/wo
            for sub_bounds in bounds.split(|b| b.child_path() == first.child_path())
                                    .filter(|s| !s.is_empty()) {
                if let Some(sub_tree) = Self::bounds_to_tree(sub_bounds) {
                    tree.includes.push(sub_tree);
                }
            }

            Some(tree)
        } else {
            None
        }
    }

    fn write(&self, f: &mut Formatter, prefix: &str) -> fmt::Result {
        let mut next_prefix = prefix.to_owned();
        next_prefix.push_str(" |-");

        let method = match self.method {
            IncludeMethod::CPP => "CPP",
            IncludeMethod::DTS => "DTS",
        };
        writeln!(f, "{} {}: {}", prefix, self.path.to_string_lossy(), method)?;
        for t in &self.includes {
            t.write(f, &next_prefix)?;
        }

        Ok(())
    }
}

impl Display for IncludeTree {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.write(f, "")
    }
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate env_logger;
extern crate iron;
#[macro_use]
extern crate log;
extern crate portier_broker;
#[macro_use(router)]
extern crate router;
extern crate rustc_serialize;

use portier_broker as broker;
use docopt::Docopt;
use iron::Iron;
use std::str::FromStr;
use std::sync::Arc;

/// Defines the program's version, as set by Cargo at compile time.
const VERSION: &'static str = env!("CARGO_PKG_VERSION");

/// Defines the program's usage string.
///
/// [Docopt](http://docopt.org) parses this and generates a custom argv parser.
const USAGE: &'static str = r#"
Portier Broker

Usage:
  portier_broker CONFIG
  portier_broker --version
  portier_broker --help

Options:
  --version       Print version information and exit
  --help          Print this help message and exit
"#;

/// Holds parsed command line parameters.
#[derive(RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_CONFIG: String,
}

/// The `main()` method. Will loop forever to serve HTTP requests.
fn main() {
    env_logger::init().unwrap();
    let args: Args = Docopt::new(USAGE)
                         .and_then(|d| d.version(Some(VERSION.to_string())).decode())
                         .unwrap_or_else(|e| e.exit());

    // Read the configuration from the provided file.
    let app = Arc::new(
        broker::AppConfig::from_json_file(&args.arg_CONFIG).unwrap()
    );

    let router = router!{
        // Human-targeted endpoints
        get "/" => broker::WelcomeHandler::new(&app),
        get "/.well-known/*" => broker::WellKnownHandler::new(&app),
        get "/confirm" => broker::ConfirmHandler::new(&app),

        // OpenID Connect provider endpoints
        get "/.well-known/openid-configuration" =>
               broker::OIDConfigHandler::new(&app),
        get "/keys.json" => broker::KeysHandler::new(&app),
        get "/auth" => broker::AuthHandler::new(&app),
        post "/auth" => broker::AuthHandler::new(&app),

        // OpenID Connect relying party endpoints
        get "/callback" => broker::CallbackHandler::new(&app),
    };

    let ipaddr = std::net::IpAddr::from_str(&app.listen_ip).unwrap();
    let socket = std::net::SocketAddr::new(ipaddr, app.listen_port);
    info!("listening on http://{}", socket);

    Iron::new(router).http("0.0.0.0:3333").unwrap();
}
<commit_msg>Actually listen on the configured ip / port<commit_after>extern crate docopt;
extern crate env_logger;
extern crate iron;
#[macro_use]
extern crate log;
extern crate portier_broker;
#[macro_use(router)]
extern crate router;
extern crate rustc_serialize;

use portier_broker as broker;
use docopt::Docopt;
use iron::Iron;
use std::str::FromStr;
use std::sync::Arc;

/// Defines the program's version, as set by Cargo at compile time.
const VERSION: &'static str = env!("CARGO_PKG_VERSION");

/// Defines the program's usage string.
///
/// [Docopt](http://docopt.org) parses this and generates a custom argv parser.
const USAGE: &'static str = r#"
Portier Broker

Usage:
  portier_broker CONFIG
  portier_broker --version
  portier_broker --help

Options:
  --version       Print version information and exit
  --help          Print this help message and exit
"#;

/// Holds parsed command line parameters.
#[derive(RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_CONFIG: String,
}

/// The `main()` method. Will loop forever to serve HTTP requests.
fn main() {
    env_logger::init().unwrap();
    let args: Args = Docopt::new(USAGE)
                         .and_then(|d| d.version(Some(VERSION.to_string())).decode())
                         .unwrap_or_else(|e| e.exit());

    // Read the configuration from the provided file.
    let app = Arc::new(
        broker::AppConfig::from_json_file(&args.arg_CONFIG).unwrap()
    );

    let router = router!{
        // Human-targeted endpoints
        get "/" => broker::WelcomeHandler::new(&app),
        get "/.well-known/*" => broker::WellKnownHandler::new(&app),
        get "/confirm" => broker::ConfirmHandler::new(&app),

        // OpenID Connect provider endpoints
        get "/.well-known/openid-configuration" =>
               broker::OIDConfigHandler::new(&app),
        get "/keys.json" => broker::KeysHandler::new(&app),
        get "/auth" => broker::AuthHandler::new(&app),
        post "/auth" => broker::AuthHandler::new(&app),

        // OpenID Connect relying party endpoints
        get "/callback" => broker::CallbackHandler::new(&app),
    };

    let ipaddr = std::net::IpAddr::from_str(&app.listen_ip).unwrap();
    let socket = std::net::SocketAddr::new(ipaddr, app.listen_port);
    info!("listening on http://{}", socket);

    Iron::new(router).http(socket).unwrap();
}
<|endoftext|>


<commit_before>extern crate loc;

#[macro_use]
extern crate clap;
extern crate deque;
extern crate itertools;
extern crate num_cpus;
extern crate regex;
extern crate ignore;

use clap::{Arg, App, AppSettings};
use ignore::WalkBuilder;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::thread;

use deque::{Stealer, Stolen};
use regex::Regex;

use loc::*;

enum Work {
    File(String),
    Quit,
}

struct Worker {
    chan: Stealer<Work>,
}

#[derive(Clone)]
struct FileCount {
    path: String,
    lang: Lang,
    count: Count,
}

// This concurrency pattern ripped directly from ripgrep
impl Worker {
    fn run(self) -> Vec<FileCount> {
        let mut v: Vec<FileCount> = vec![];
        loop {
            match self.chan.steal() {
                // What causes these?
                Stolen::Empty | Stolen::Abort => continue,
                Stolen::Data(Work::Quit) => break,
                Stolen::Data(Work::File(path)) => {
                    let lang = lang_from_ext(&path);
                    if lang != Lang::Unrecognized {
                        let count = count(&path);
                        v.push(FileCount {
                            lang: lang,
                            path: String::from(path),
                            count: count,
                        });
                    }
                }
            };
        }
        v
    }
}

fn main() {

    let matches = App::new("count")
        .global_settings(&[AppSettings::ColoredHelp])
        .version(crate_version!())
        .author("Curtis Gagliardi <curtis@curtis.io>")
        .about("counts things quickly hopefully")
        // TODO(cgag): actually implement filtering
        .arg(Arg::with_name("exclude")
            .required(false)
            .multiple(true)
            .long("exclude")
            .value_name("REGEX")
            .takes_value(true)
            .help("Rust regex of files to exclude"))
        .arg(Arg::with_name("include")
            .required(false)
            .multiple(true)
            .long("include")
            .value_name("REGEX")
            .takes_value(true)
            .help("Rust regex matching files to include. Anything not matched will be excluded"))
        .arg(Arg::with_name("files")
             .required(false)
             .long("files")
             .takes_value(false)
             .help("Show stats for individual files"))
        .arg(Arg::with_name("sort")
            .required(false)
            .long("sort")
            .value_name("COLUMN")
            .takes_value(true)
            .help("Column to sort by"))
        .arg(Arg::with_name("unrestricted")
             .required(false)
             .multiple(true)
             .long("unrestricted")
             .short("u")
             .takes_value(false)
             .help("A single -u won't respect .gitignore (etc.) files. Two -u flags will additionally count hidden files and directories."))
        .arg(Arg::with_name("target")
            .multiple(true)
            .help("File or directory to count"))
        .get_matches();

    let targets = match matches.values_of("target") {
        Some(targets) => targets.collect(),
        None => vec!["."]
    };
    let sort = matches.value_of("sort").unwrap_or("code");
    let by_file = matches.is_present("files");
    let (use_ignore, ignore_hidden) = match matches.occurrences_of("unrestricted") {
        0 => (true,  true),
        1 => (false, true),
        2 => (false, false),
        _ => (false, false),
    };
    let exclude_regex = match matches.values_of("exclude") {
        Some(regex_strs) => {
            let combined_regex = regex_strs.map(|r| format!("({})", r)).collect::<Vec<String>>().join("|");
            match Regex::new(&combined_regex) {
                Ok(r) => Some(r),
                Err(e) => {
                    println!("Error processing exclude regex: {}", e);
                    std::process::exit(1);
                }
            }
        }
        None => None,
    };
    let include_regex = match matches.values_of("include") {
        Some(regex_strs) => {
            let combined_regex = regex_strs.map(|r| format!("({})", r)).collect::<Vec<String>>().join("|");
            match Regex::new(&combined_regex) {
                Ok(r) => Some(r),
                Err(e) => {
                    println!("Error processing include regex: {}", e);
                    std::process::exit(1);
                }
            }
        }
        None => None,
    };

    let threads = num_cpus::get();
    let mut workers = vec![];
    let (workq, stealer) = deque::new();
    for _ in 0..threads {
        let worker = Worker { chan: stealer.clone() };
        workers.push(thread::spawn(|| worker.run()));
    }

    for target in targets {
        // TODO(cgag): use WalkParallel?
        let walker = WalkBuilder::new(target).ignore(use_ignore)
                                             .git_ignore(use_ignore)
                                             .git_exclude(use_ignore)
                                             .hidden(ignore_hidden)
                                             .build();
        let files = walker
            .into_iter()
            .filter_map(|entry| entry.ok())
            .filter(|entry| entry.file_type().expect("no filetype").is_file())
            .map(|entry| String::from(entry.path().to_str().unwrap()))
            .filter(|path| match include_regex {
                None => true,
                Some(ref include) => include.is_match(path),
            })
            .filter(|path| match exclude_regex {
                None => true,
                Some(ref exclude) => !exclude.is_match(path),
            });

        for path in files {
            workq.push(Work::File(path));
        }
    }

    for _ in 0..workers.len() {
        workq.push(Work::Quit);
    }

    let mut filecounts: Vec<FileCount> = Vec::new();
    for worker in workers {
        filecounts.extend(worker.join().unwrap().iter().cloned())
    }

    // TODO(cgag): use insecure hashmaps or something
    let mut by_lang: HashMap<Lang, Vec<FileCount>> = HashMap::new();
    for fc in filecounts {
        match by_lang.entry(fc.lang) {
            Entry::Occupied(mut by_lang) => by_lang.get_mut().push(fc),
            Entry::Vacant(by_lang) => {
                by_lang.insert(vec![fc]);
            }
        };
    }

    let linesep = str_repeat("-", 80);

    if by_file {
        // print breakdown for each individual file
        println!("{}", linesep);
        println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 "Language",
                 "Files",
                 "Lines",
                 "Blank",
                 "Comment",
                 "Code");
        println!("{}", linesep);

        // TODO(cgag): do the summing first, so we can do additional sorting
        // by totals.
        for (lang, mut filecounts) in by_lang {
            let mut total = Count::default();
            for fc in &filecounts {
                total.merge(&fc.count);
            }

            println!("{}", linesep);
            println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                     lang,
                     filecounts.len(),
                     total.lines,
                     total.blank,
                     total.comment,
                     total.code);

            match sort {
                "code" => filecounts.sort_by(|fc1, fc2| fc2.count.code.cmp(&fc1.count.code)),
                "comment" => {
                    filecounts.sort_by(|fc1, fc2| fc2.count.comment.cmp(&fc1.count.comment))
                }
                "blank" => filecounts.sort_by(|fc1, fc2| fc2.count.blank.cmp(&fc1.count.blank)),
                "lines" => filecounts.sort_by(|fc1, fc2| fc2.count.lines.cmp(&fc1.count.lines)),
                // No sorting by language or files here. Need to do it at a higher level.
                _ => (),
            }

            println!("{}", linesep);
            for fc in filecounts {
                println!("|{0: <25} {1: >12} {2: >12} {3: >12} {4: >12}",
                         last_n_chars(&fc.path, 25),
                         fc.count.lines,
                         fc.count.blank,
                         fc.count.comment,
                         fc.count.code);
            }
        }
    } else {
        // print summary by language
        let mut lang_totals: HashMap<&Lang, LangTotal> = HashMap::new();
        for (lang, filecounts) in &by_lang {
            let mut lang_total = Count::default();
            for fc in filecounts {
                lang_total.merge(&fc.count);
            }
            lang_totals.insert(lang,
                               LangTotal {
                                   files: filecounts.len() as u32,
                                   count: lang_total,
                               });
        }

        let mut totals_by_lang = lang_totals.iter().collect::<Vec<(&&Lang, &LangTotal)>>();
        match sort {
            "language" => totals_by_lang.sort_by(|&(l1, _), &(l2, _)| l1.to_s().cmp(l2.to_s())),
            "files" => totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.files.cmp(&c1.files)),
            "code" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.code.cmp(&c1.count.code))
            }
            "comment" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.comment.cmp(&c1.count.comment))
            }
            "blank" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.blank.cmp(&c1.count.blank))
            }
            "lines" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.lines.cmp(&c1.count.lines))
            }
            _ => {
                println!("invalid sort option {}, sorting by code", sort);
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.code.cmp(&c1.count.code))
            }
        }

        print_totals_by_lang(&linesep, &totals_by_lang);
    }

}

fn last_n_chars(s: &str, n: usize) -> String {
    if s.len() <= n {
        return String::from(s);
    }
    s.chars().skip(s.len() - n).collect::<String>()
}

fn str_repeat(s: &str, n: usize) -> String {
    std::iter::repeat(s).take(n).collect::<Vec<_>>().join("")
}

fn print_totals_by_lang(linesep: &str, totals_by_lang: &[(&&Lang, &LangTotal)]) {
    println!("{}", linesep);
    println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
             "Language",
             "Files",
             "Lines",
             "Blank",
             "Comment",
             "Code");
    println!("{}", linesep);

    for &(lang, total) in totals_by_lang {
        println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 lang,
                 total.files,
                 total.count.lines,
                 total.count.blank,
                 total.count.comment,
                 total.count.code);
    }

    let mut totals = LangTotal {
        files: 0,
        count: Count::default(),
    };
    for &(_, total) in totals_by_lang {
        totals.files += total.files;
        totals.count.code += total.count.code;
        totals.count.blank += total.count.blank;
        totals.count.comment += total.count.comment;
        totals.count.lines += total.count.lines;
    }

    println!("{}", linesep);
    println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
             "Total",
             totals.files,
             totals.count.lines,
             totals.count.blank,
             totals.count.comment,
             totals.count.code);
    println!("{}", linesep);
}
<commit_msg>Uncover shadowed variable.<commit_after>extern crate loc;

#[macro_use]
extern crate clap;
extern crate deque;
extern crate itertools;
extern crate num_cpus;
extern crate regex;
extern crate ignore;

use clap::{Arg, App, AppSettings};
use ignore::WalkBuilder;

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::thread;

use deque::{Stealer, Stolen};
use regex::Regex;

use loc::*;

enum Work {
    File(String),
    Quit,
}

struct Worker {
    chan: Stealer<Work>,
}

#[derive(Clone)]
struct FileCount {
    path: String,
    lang: Lang,
    count: Count,
}

// This concurrency pattern ripped directly from ripgrep
impl Worker {
    fn run(self) -> Vec<FileCount> {
        let mut v: Vec<FileCount> = vec![];
        loop {
            match self.chan.steal() {
                // What causes these?
                Stolen::Empty | Stolen::Abort => continue,
                Stolen::Data(Work::Quit) => break,
                Stolen::Data(Work::File(path)) => {
                    let lang = lang_from_ext(&path);
                    if lang != Lang::Unrecognized {
                        let count = count(&path);
                        v.push(FileCount {
                            lang: lang,
                            path: String::from(path),
                            count: count,
                        });
                    }
                }
            };
        }
        v
    }
}

fn main() {

    let matches = App::new("count")
        .global_settings(&[AppSettings::ColoredHelp])
        .version(crate_version!())
        .author("Curtis Gagliardi <curtis@curtis.io>")
        .about("counts things quickly hopefully")
        // TODO(cgag): actually implement filtering
        .arg(Arg::with_name("exclude")
            .required(false)
            .multiple(true)
            .long("exclude")
            .value_name("REGEX")
            .takes_value(true)
            .help("Rust regex of files to exclude"))
        .arg(Arg::with_name("include")
            .required(false)
            .multiple(true)
            .long("include")
            .value_name("REGEX")
            .takes_value(true)
            .help("Rust regex matching files to include. Anything not matched will be excluded"))
        .arg(Arg::with_name("files")
             .required(false)
             .long("files")
             .takes_value(false)
             .help("Show stats for individual files"))
        .arg(Arg::with_name("sort")
            .required(false)
            .long("sort")
            .value_name("COLUMN")
            .takes_value(true)
            .help("Column to sort by"))
        .arg(Arg::with_name("unrestricted")
             .required(false)
             .multiple(true)
             .long("unrestricted")
             .short("u")
             .takes_value(false)
             .help("A single -u won't respect .gitignore (etc.) files. Two -u flags will additionally count hidden files and directories."))
        .arg(Arg::with_name("target")
            .multiple(true)
            .help("File or directory to count"))
        .get_matches();

    let targets = match matches.values_of("target") {
        Some(targets) => targets.collect(),
        None => vec!["."]
    };
    let sort = matches.value_of("sort").unwrap_or("code");
    let by_file = matches.is_present("files");
    let (use_ignore, ignore_hidden) = match matches.occurrences_of("unrestricted") {
        0 => (true,  true),
        1 => (false, true),
        2 => (false, false),
        _ => (false, false),
    };
    let exclude_regex = match matches.values_of("exclude") {
        Some(regex_strs) => {
            let combined_regex = regex_strs.map(|r| format!("({})", r)).collect::<Vec<String>>().join("|");
            match Regex::new(&combined_regex) {
                Ok(r) => Some(r),
                Err(e) => {
                    println!("Error processing exclude regex: {}", e);
                    std::process::exit(1);
                }
            }
        }
        None => None,
    };
    let include_regex = match matches.values_of("include") {
        Some(regex_strs) => {
            let combined_regex = regex_strs.map(|r| format!("({})", r)).collect::<Vec<String>>().join("|");
            match Regex::new(&combined_regex) {
                Ok(r) => Some(r),
                Err(e) => {
                    println!("Error processing include regex: {}", e);
                    std::process::exit(1);
                }
            }
        }
        None => None,
    };

    let threads = num_cpus::get();
    let mut workers = vec![];
    let (workq, stealer) = deque::new();
    for _ in 0..threads {
        let worker = Worker { chan: stealer.clone() };
        workers.push(thread::spawn(|| worker.run()));
    }

    for target in targets {
        // TODO(cgag): use WalkParallel?
        let walker = WalkBuilder::new(target).ignore(use_ignore)
                                             .git_ignore(use_ignore)
                                             .git_exclude(use_ignore)
                                             .hidden(ignore_hidden)
                                             .build();
        let files = walker
            .into_iter()
            .filter_map(|entry| entry.ok())
            .filter(|entry| entry.file_type().expect("no filetype").is_file())
            .map(|entry| String::from(entry.path().to_str().unwrap()))
            .filter(|path| match include_regex {
                None => true,
                Some(ref include) => include.is_match(path),
            })
            .filter(|path| match exclude_regex {
                None => true,
                Some(ref exclude) => !exclude.is_match(path),
            });

        for path in files {
            workq.push(Work::File(path));
        }
    }

    for _ in 0..workers.len() {
        workq.push(Work::Quit);
    }

    let mut filecounts: Vec<FileCount> = Vec::new();
    for worker in workers {
        filecounts.extend(worker.join().unwrap().iter().cloned())
    }

    // TODO(cgag): use insecure hashmaps or something
    let mut by_lang: HashMap<Lang, Vec<FileCount>> = HashMap::new();
    for fc in filecounts {
        match by_lang.entry(fc.lang) {
            Entry::Occupied(mut elem) => elem.get_mut().push(fc),
            Entry::Vacant(elem) => {
                elem.insert(vec![fc]);
            }
        };
    }

    let linesep = str_repeat("-", 80);

    if by_file {
        // print breakdown for each individual file
        println!("{}", linesep);
        println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 "Language",
                 "Files",
                 "Lines",
                 "Blank",
                 "Comment",
                 "Code");
        println!("{}", linesep);

        // TODO(cgag): do the summing first, so we can do additional sorting
        // by totals.
        for (lang, mut filecounts) in by_lang {
            let mut total = Count::default();
            for fc in &filecounts {
                total.merge(&fc.count);
            }

            println!("{}", linesep);
            println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                     lang,
                     filecounts.len(),
                     total.lines,
                     total.blank,
                     total.comment,
                     total.code);

            match sort {
                "code" => filecounts.sort_by(|fc1, fc2| fc2.count.code.cmp(&fc1.count.code)),
                "comment" => {
                    filecounts.sort_by(|fc1, fc2| fc2.count.comment.cmp(&fc1.count.comment))
                }
                "blank" => filecounts.sort_by(|fc1, fc2| fc2.count.blank.cmp(&fc1.count.blank)),
                "lines" => filecounts.sort_by(|fc1, fc2| fc2.count.lines.cmp(&fc1.count.lines)),
                // No sorting by language or files here. Need to do it at a higher level.
                _ => (),
            }

            println!("{}", linesep);
            for fc in filecounts {
                println!("|{0: <25} {1: >12} {2: >12} {3: >12} {4: >12}",
                         last_n_chars(&fc.path, 25),
                         fc.count.lines,
                         fc.count.blank,
                         fc.count.comment,
                         fc.count.code);
            }
        }
    } else {
        // print summary by language
        let mut lang_totals: HashMap<&Lang, LangTotal> = HashMap::new();
        for (lang, filecounts) in &by_lang {
            let mut lang_total = Count::default();
            for fc in filecounts {
                lang_total.merge(&fc.count);
            }
            lang_totals.insert(lang,
                               LangTotal {
                                   files: filecounts.len() as u32,
                                   count: lang_total,
                               });
        }

        let mut totals_by_lang = lang_totals.iter().collect::<Vec<(&&Lang, &LangTotal)>>();
        match sort {
            "language" => totals_by_lang.sort_by(|&(l1, _), &(l2, _)| l1.to_s().cmp(l2.to_s())),
            "files" => totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.files.cmp(&c1.files)),
            "code" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.code.cmp(&c1.count.code))
            }
            "comment" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.comment.cmp(&c1.count.comment))
            }
            "blank" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.blank.cmp(&c1.count.blank))
            }
            "lines" => {
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.lines.cmp(&c1.count.lines))
            }
            _ => {
                println!("invalid sort option {}, sorting by code", sort);
                totals_by_lang.sort_by(|&(_, c1), &(_, c2)| c2.count.code.cmp(&c1.count.code))
            }
        }

        print_totals_by_lang(&linesep, &totals_by_lang);
    }

}

fn last_n_chars(s: &str, n: usize) -> String {
    if s.len() <= n {
        return String::from(s);
    }
    s.chars().skip(s.len() - n).collect::<String>()
}

fn str_repeat(s: &str, n: usize) -> String {
    std::iter::repeat(s).take(n).collect::<Vec<_>>().join("")
}

fn print_totals_by_lang(linesep: &str, totals_by_lang: &[(&&Lang, &LangTotal)]) {
    println!("{}", linesep);
    println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
             "Language",
             "Files",
             "Lines",
             "Blank",
             "Comment",
             "Code");
    println!("{}", linesep);

    for &(lang, total) in totals_by_lang {
        println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
                 lang,
                 total.files,
                 total.count.lines,
                 total.count.blank,
                 total.count.comment,
                 total.count.code);
    }

    let mut totals = LangTotal {
        files: 0,
        count: Count::default(),
    };
    for &(_, total) in totals_by_lang {
        totals.files += total.files;
        totals.count.code += total.count.code;
        totals.count.blank += total.count.blank;
        totals.count.comment += total.count.comment;
        totals.count.lines += total.count.lines;
    }

    println!("{}", linesep);
    println!(" {0: <17} {1: >8} {2: >12} {3: >12} {4: >12} {5: >12}",
             "Total",
             totals.files,
             totals.count.lines,
             totals.count.blank,
             totals.count.comment,
             totals.count.code);
    println!("{}", linesep);
}
<|endoftext|>


<commit_before>// main.rs
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;

use actix::prelude::*;
use actix_web::middleware::Logger;
use actix_web::{web, App, HttpServer};
use diesel::{r2d2::ConnectionManager, PgConnection};
use dotenv::dotenv;

mod models;

use crate::models::DbExecutor;

fn main() -> std::io::Result<()> {
    dotenv().ok();
    std::env::set_var(
        "RUST_LOG",
        "armchar_enthusiasts=debug,actix_web=info,actix_server=info",
    );
    env_logger::init();
    let sys = actix_rt::System::new("example");

    let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");

    let manager = ConnectionManager::<PgConnection>::new(database_url);
    let pool = r2d2::Pool::builder()
        .build(manager)
        .expect("Failed to create pool.");

    let address: Addr<DbExecutor> = SyncArbiter::start(4, move || DbExecutor(pool.clone()));

    let endpoint = "0.0.0.0:28000";
    info!("Starting alfred person service at: {:?}", endpoint);

    HttpServer::new(move || {
        App::new()
            // add database pool as data/state to the app
            .data(address.clone())
            // setup logger for requests
            .wrap(Logger::default())
            .service(web::scope("/").service(web::resource("/auth").route(web::get().to(|| {}))))
    })
    .bind(endpoint)?
    .start();

    sys.run()
}<commit_msg>handle RUST_LOG env var<commit_after>#[macro_use]
extern crate diesel;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;

use actix::prelude::*;
use actix_web::middleware::Logger;
use actix_web::{web, App, HttpServer};
use diesel::{r2d2::ConnectionManager, PgConnection};
use dotenv::dotenv;

mod models;

use crate::models::DbExecutor;

fn main() -> std::io::Result<()> {
    dotenv().ok();
    let key = "RUST_LOG";
    match std::env::var(key) {
        Ok(val) => println!("using {}={:?}", key, val),
        Err(_) => {
            std::env::set_var(
                key,
                "armchair_enthusiasts=debug,actix_web=info,actix_server=info",
            );
            println!("setting default RUST_LOG values");
        }
    }
    env_logger::init();
    let sys = actix_rt::System::new("main");

    let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");

    let manager = ConnectionManager::<PgConnection>::new(database_url);
    let pool = r2d2::Pool::builder()
        .build(manager)
        .expect("Failed to create pool.");

    let address: Addr<DbExecutor> = SyncArbiter::start(4, move || DbExecutor(pool.clone()));

    let endpoint = "0.0.0.0:28000";
    info!("Starting server at: {:?}", endpoint);

    HttpServer::new(move || {
        App::new()
            .data(address.clone())
            .wrap(Logger::default())
            .service(web::scope("/").service(web::resource("/auth").route(web::get().to(|| {}))))
    })
    .bind(endpoint)?
    .start();

    sys.run()
}<|endoftext|>


<commit_before>#![feature(macro_rules)]

extern crate getopts;
use getopts::{optflag,optopt,getopts,OptGroup,usage};
use std::os;
use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::io;
use std::io::fs::PathExtensions;

fn get_env() -> HashMap<String, String> {
    let mut env = HashMap::new();
    env.extend(os::env().into_iter().filter(|&(ref key, ref value)| {
        key.as_slice().starts_with("MATES_")
    }));
    env
}

fn from_env<'a>(env: &'a HashMap<String, String>, key: &str) -> Option<&'a String> {
    env.find_equiv(&key)
}

fn expect_env<'a>(env: &'a HashMap<String, String>, key: &str) -> &'a String {
    from_env(env, key).expect(
        format!("The {} environment variable must be set.", key).as_slice()
    )
}

macro_rules! expect_or_this(
    ($iterator:expr, $this: expr) => (
        match $iterator {
            Some(x) => x,
            None => $this
        }
    )
)

fn build_index(outfile: &Path, dir: &Path) {
    if !dir.is_dir() {
        fail!("MATES_DIR must be a directory.");
    };

    let mut outf = io::File::create(outfile);
    let entries = match io::fs::readdir(dir) {
        Ok(x) => x,
        Err(f) => { fail!(f.desc) }
    };
    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }
        let itemstr = match io::File::open(entry).read_to_string() {
            Ok(x) => x,
            Err(f) => { fail!(format!("Failed to open {}: {}", entry.display(), f.desc)) }
        };
        let item = parse_item(&itemstr);
        let name = item.fullname();
        match item.emails() {
            Some(emails) => {
                for email in emails.iter() {
                    outf.write_str(format!("{}\t{}\n", email.value, name).as_slice());
                };
            },
            None => ()
        };
    };
}

struct PropertyValue {
    params: String,
    value: String,
}

struct Item {
    props: HashMap<String, Vec<PropertyValue>>
}

impl Item {
    fn single_value(&self, key: &String) -> Option<String> {
        match self.props.find(key) {
            Some(x) => { if x.len() > 0 { Some(x[0].value.clone()) } else { None } },
            None => { None }
        }
    }
    fn fullname(&self) -> String {
        match self.single_value(&"FN".into_string()) {
            Some(x) => { x }
            None => { "".into_string() }
        }
    }

    fn emails(&self) -> Option<&Vec<PropertyValue>> {
        self.props.find(&"EMAIL".into_string())
    }
}

fn parse_item(s: &String) -> Item {
    let mut linebuffer = String::new();
    let mut line: String;
    let mut is_continuation: bool;
    let mut rv = Item {
        props: HashMap::new()
    };

    for strline in s.as_slice().split('\n') {
        line = strline.into_string();
        is_continuation = false;
        while line.as_slice().char_at(0).is_whitespace() {
            is_continuation = true;
            line.remove(0);
        };

        if !is_continuation && linebuffer.len() > 0 {
            let (propkey, propvalue) = parse_line(&linebuffer);
            match rv.props.entry(propkey) {
                Occupied(values) => { values.into_mut().push(propvalue); },
                Vacant(values) => { values.set(vec![propvalue]); }
            };
            linebuffer.clear();
        };

        linebuffer.push_str(line.as_slice());
    };
    rv
}

fn parse_line(s: &String) -> (String, PropertyValue) {
    let mut kv_splitresult = s.as_slice().splitn(1, ':');
    let key_and_params = kv_splitresult.next().expect("");
    let value = expect_or_this!(kv_splitresult.next(), "");

    let mut kp_splitresult = key_and_params.splitn(1, ';');
    let key = kp_splitresult.next().expect("");
    let params = expect_or_this!(kp_splitresult.next(), "");

    (key.into_string(), PropertyValue {
        value: value.into_string(),
        params: params.into_string()
    })
}

fn main() {
    let args = os::args();

    let program = args[0].as_slice();
    let opts = [
        optflag("i", "index", "Create index."),
        optflag("h", "help", "Print help."),
        optopt("m", "mutt-search", "Search in index, for mutt search.", "")
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    let env = get_env();

    let print_usage = || {
        println!("{}", usage(program, opts));
        println!("Environment variables:");
        println!("- MATES_INDEX: Path to index file, which is basically a cache of all");
        println!("               contacts.");
        println!("- MATES_DIR:   The vdir to use.");
        println!("- MATES_GREP:  The grep executable to use.");
    };

    if matches.opt_present("h") {
        print_usage();
    } else if matches.opt_present("index") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let mates_dir = expect_env(&env, "MATES_DIR");
        println!("Rebuilding index file \"{}\"...", index_file);
        build_index(
            &Path::new(index_file.as_slice()),
            &Path::new(mates_dir.as_slice())
        );
    } else if matches.opt_present("mutt-search") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let default_grep = "grep".into_string();
        let grep_cmd = expect_or_this!(from_env(&env, "MATES_GREP"), &default_grep);

        // FIXME: Better way to write this? We already checked for presence of mutt-search before
        let query = matches.opt_str("mutt-search").expect("This should never happen and yet it did.");
        let process = match match io::Command::new(grep_cmd.as_slice())
            .arg(query.as_slice())
            .arg(index_file.as_slice())
            .stdout(io::process::InheritFd(1))
            .stderr(io::process::InheritFd(2))
            .spawn() {
                Ok(child) => child,
                Err(e) => fail!("Failed to execute grep command: {}", e),
            }.wait() {
                Ok(code) => {
                    if !code.success() {
                        fail!("Grep command exited with code {}, aborting.", code);
                    };
                },
                Err(e) => fail!("Failed to execute grep command: {}", e),
            };
    } else {
        print_usage();
    };
}
<commit_msg>Remove warnings<commit_after>#![feature(macro_rules)]

extern crate getopts;
use getopts::{optflag,optopt,getopts,usage};
use std::os;
use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::io;
use std::io::fs::PathExtensions;

fn get_env() -> HashMap<String, String> {
    let mut env = HashMap::new();
    env.extend(os::env().into_iter().filter(|&(ref key, ref value)| {
        key.as_slice().starts_with("MATES_")
    }));
    env
}

fn from_env<'a>(env: &'a HashMap<String, String>, key: &str) -> Option<&'a String> {
    env.find_equiv(&key)
}

fn expect_env<'a>(env: &'a HashMap<String, String>, key: &str) -> &'a String {
    from_env(env, key).expect(
        format!("The {} environment variable must be set.", key).as_slice()
    )
}

macro_rules! expect_or_this(
    ($iterator:expr, $this: expr) => (
        match $iterator {
            Some(x) => x,
            None => $this
        }
    )
)

fn build_index(outfile: &Path, dir: &Path) {
    if !dir.is_dir() {
        fail!("MATES_DIR must be a directory.");
    };

    let mut outf = io::File::create(outfile);
    let entries = match io::fs::readdir(dir) {
        Ok(x) => x,
        Err(f) => { fail!(f.desc) }
    };
    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }
        let itemstr = match io::File::open(entry).read_to_string() {
            Ok(x) => x,
            Err(f) => { fail!(format!("Failed to open {}: {}", entry.display(), f.desc)) }
        };
        let item = parse_item(&itemstr);
        let name = item.fullname();
        match item.emails() {
            Some(emails) => {
                for email in emails.iter() {
                    outf.write_str(format!("{}\t{}\n", email.value, name).as_slice());
                };
            },
            None => ()
        };
    };
}

struct PropertyValue {
    params: String,
    value: String,
}

struct Item {
    props: HashMap<String, Vec<PropertyValue>>
}

impl Item {
    fn single_value(&self, key: &String) -> Option<String> {
        match self.props.find(key) {
            Some(x) => { if x.len() > 0 { Some(x[0].value.clone()) } else { None } },
            None => { None }
        }
    }
    fn fullname(&self) -> String {
        match self.single_value(&"FN".into_string()) {
            Some(x) => { x }
            None => { "".into_string() }
        }
    }

    fn emails(&self) -> Option<&Vec<PropertyValue>> {
        self.props.find(&"EMAIL".into_string())
    }
}

fn parse_item(s: &String) -> Item {
    let mut linebuffer = String::new();
    let mut line: String;
    let mut is_continuation: bool;
    let mut rv = Item {
        props: HashMap::new()
    };

    for strline in s.as_slice().split('\n') {
        line = strline.into_string();
        is_continuation = false;
        while line.as_slice().char_at(0).is_whitespace() {
            is_continuation = true;
            line.remove(0);
        };

        if !is_continuation && linebuffer.len() > 0 {
            let (propkey, propvalue) = parse_line(&linebuffer);
            match rv.props.entry(propkey) {
                Occupied(values) => { values.into_mut().push(propvalue); },
                Vacant(values) => { values.set(vec![propvalue]); }
            };
            linebuffer.clear();
        };

        linebuffer.push_str(line.as_slice());
    };
    rv
}

fn parse_line(s: &String) -> (String, PropertyValue) {
    let mut kv_splitresult = s.as_slice().splitn(1, ':');
    let key_and_params = kv_splitresult.next().expect("");
    let value = expect_or_this!(kv_splitresult.next(), "");

    let mut kp_splitresult = key_and_params.splitn(1, ';');
    let key = kp_splitresult.next().expect("");
    let params = expect_or_this!(kp_splitresult.next(), "");

    (key.into_string(), PropertyValue {
        value: value.into_string(),
        params: params.into_string()
    })
}

fn main() {
    let args = os::args();

    let program = args[0].as_slice();
    let opts = [
        optflag("i", "index", "Create index."),
        optflag("h", "help", "Print help."),
        optopt("m", "mutt-search", "Search in index, for mutt search.", "")
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    let env = get_env();

    let print_usage = || {
        println!("{}", usage(program, opts));
        println!("Environment variables:");
        println!("- MATES_INDEX: Path to index file, which is basically a cache of all");
        println!("               contacts.");
        println!("- MATES_DIR:   The vdir to use.");
        println!("- MATES_GREP:  The grep executable to use.");
    };

    if matches.opt_present("h") {
        print_usage();
    } else if matches.opt_present("index") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let mates_dir = expect_env(&env, "MATES_DIR");
        println!("Rebuilding index file \"{}\"...", index_file);
        build_index(
            &Path::new(index_file.as_slice()),
            &Path::new(mates_dir.as_slice())
        );
    } else if matches.opt_present("mutt-search") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let default_grep = "grep".into_string();
        let grep_cmd = expect_or_this!(from_env(&env, "MATES_GREP"), &default_grep);

        // FIXME: Better way to write this? We already checked for presence of mutt-search before
        let query = matches.opt_str("mutt-search").expect("This should never happen and yet it did.");
        match match io::Command::new(grep_cmd.as_slice())
            .arg(query.as_slice())
            .arg(index_file.as_slice())
            .stdout(io::process::InheritFd(1))
            .stderr(io::process::InheritFd(2))
            .spawn() {
                Ok(child) => child,
                Err(e) => fail!("Failed to execute grep command: {}", e),
            }.wait() {
                Ok(code) => {
                    if !code.success() {
                        fail!("Grep command exited with code {}, aborting.", code);
                    };
                },
                Err(e) => fail!("Failed to execute grep command: {}", e),
            };
    } else {
        print_usage();
    };
}
<|endoftext|>