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

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") {
        dump_ir(tokens);
    } 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(tokens: Vec<Token>, tape_size: i32) -> String {
    println!("Compiling...");

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens.iter() {
        match *token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    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 {
                    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
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                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 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                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 outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    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, output, outbuffsize=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(tokens: Vec<Token>) {
    for token in tokens.iter() {
        match *token {
            Output =>
                println!("output"),
            Input =>
                println!("input"),
            Loop =>
                println!("loop"),
            EndLoop =>
                println!("endloop"),
            Move(offset) =>
                println!("move(offset={})", offset),
            Add(offset, value) =>
                println!("add(offset={}, value={})", offset, value),
            Set(offset, value) =>
                println!("set(offset={}, value={})", offset, value),
            MulCopy(src, dest, mul) =>
                println!("mulcopy(src={}, dest={}, mul={})", src, dest, mul),
            Scan(offset) =>
                println!("scan(offset={})", offset),
            LoadOut(offset, add) =>
                println!("loadout(offset={}, add={})", offset, add),
            LoadOutSet(value) =>
                println!("loadoutset(value={})", value),
            If(offset) =>
                println!("if(offset={})", offset),
            EndIf =>
                println!("endif"),
        }
    }
}
<commit_msg>Allow -o option with --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;

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(tokens: Vec<Token>, tape_size: i32) -> String {
    println!("Compiling...");

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens.iter() {
        match *token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    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 {
                    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
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                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 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                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 outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    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, output, outbuffsize=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(tokens: Vec<Token>) -> String {
    let mut output = String::new();
    for token in tokens.iter() {
        match *token {
            Output =>
                output.push_str("output\n"),
            Input =>
                output.push_str("input\n"),
            Loop =>
                output.push_str("loop\n"),
            EndLoop =>
                output.push_str("endloop\n"),
            Move(offset) =>
                output.push_str(&format!("move(offset={})\n", offset)),
            Add(offset, value) =>
                output.push_str(&format!("add(offset={}, value={})\n", offset, value)),
            Set(offset, value) =>
                output.push_str(&format!("set(offset={}, value={})\n", offset, value)),
            MulCopy(src, dest, mul) =>
                output.push_str(&format!("mulcopy(src={}, dest={}, mul={})\n", src, dest, mul)),
            Scan(offset) =>
                output.push_str(&format!("scan(offset={})\n", offset)),
            LoadOut(offset, add) =>
                output.push_str(&format!("loadout(offset={}, add={})\n", offset, add)),
            LoadOutSet(value) =>
                output.push_str(&format!("loadoutset(value={})\n", value)),
            If(offset) =>
                output.push_str(&format!("if(offset={})\n", offset)),
            EndIf =>
                output.push_str("endif\n"),
        }
    }
    output
}
<|endoftext|>


<commit_before>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

extern crate lrcat;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate docopt;

use std::collections::BTreeMap;
use std::path::PathBuf;

use docopt::Docopt;

use lrcat::{
    Catalog,
    CatalogVersion,
    Collection,
    Folders,
    Image,
    Keyword,
    LibraryFile,
    LrObject
};

const USAGE: &'static str = "
Usage:
  dumper <command> ([--all] | [--collections] [--libfiles] [--images] [--folders] [--keywords]) <path>

Options:
    --all          Select all objects
    --collections  Select only collections
    --libfiles     Select only library files
    --images       Select only images
    --folders      Select only folders
    --keywords     Select only keywords

Commands are:
    dump           Dump the objects
    audit          Audit mode: output what we ignored
";

#[derive(Debug, Deserialize)]
struct Args {
    arg_command: Command,
    arg_path: PathBuf,
    flag_all: bool,
    flag_collections: bool,
    flag_libfiles: bool,
    flag_images: bool,
    flag_folders: bool,
    flag_keywords: bool
}

#[derive(Debug, Deserialize)]
enum Command {
    Dump,
    Audit,
    Unknown(String)
}

fn main() {

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.argv(std::env::args()).deserialize())
        .unwrap_or_else(|e| e.exit());
    {
        match args.arg_command {
            Command::Dump =>
                process_dump(&args),
            Command::Audit =>
                process_audit(&args),
            _ =>
                ()
        };
    }
}

fn process_dump(args: &Args) {
    let mut catalog = Catalog::new(&args.arg_path);
    if catalog.open() {
        catalog.load_version();
        println!("Catalog:");
        println!("\tVersion: {} ({:?})", catalog.version,
                 catalog.catalog_version);
        println!("\tRoot keyword id: {}", catalog.root_keyword_id);

        if catalog.catalog_version != CatalogVersion::Lr4 {
            println!("Unsupported catalog version");
            return;
        }
        {
            let keywords = catalog.load_keywords();
            println!("\tKeywords count: {}", keywords.len());

            if args.flag_all || args.flag_keywords {
                dump_keywords(&keywords);
            }
        }

        {
            let folders = catalog.load_folders();
            if args.flag_all || args.flag_folders {
                dump_folders(&folders);
            }
        }

        {
            let libfiles = catalog.load_library_files();
            if args.flag_all || args.flag_libfiles {
                dump_libfiles(&libfiles);
            }
        }
        {
            let images = catalog.load_images();
            if args.flag_all || args.flag_images {
                dump_images(&images);
            }
        }
        {
            let collections = catalog.load_collections();
            if args.flag_all || args.flag_collections {
                dump_collections(&collections);
            }
        }
    }
}

fn dump_keywords(keywords: &BTreeMap<i64,Keyword>) {
    println!("Keywords");
    println!("+---------+--------------------------------------+---------+----------------------------");
    println!("+ id      + uuid                                 + parent  + name");
    println!("+---------+--------------------------------------+---------+----------------------------");
    for (id, keyword) in keywords {
        assert_eq!(*id, keyword.id());
        println!("+ {:>7} + {} + {:>7} + {:<26}", id, keyword.uuid(), keyword.parent, keyword.name);
    }
    println!("+---------+--------------------------------------+---------+----------------------------");
}

fn dump_folders(folders: &Folders) {
    println!("Root Folders");
    println!("+--------+--------------------------------------+------------------+----------------------------");
    println!(" id      + uuid                                 + name             + absolute path");
    println!("+--------+--------------------------------------+------------------+----------------------------");
    for root in &folders.roots {
        println!("+ {:>6} + {} + {:<16} + {:<26}", root.id(), root.uuid(), root.name, root.absolute_path);
    }
    println!("+--------+--------------------------------------+------------------+----------------------------");
    println!("Folders");
    println!("+--------+--------------------------------------+--------+----------------------------");
    println!("+ id     + uuid                                 + root   + path");
    println!("+--------+--------------------------------------+--------+----------------------------");
    for folder in &folders.folders {
        println!("+ {:>6} + {} + {:>6} + {:<26} + {:?}",
                 folder.id(), folder.uuid(), folder.root_folder,
                 folder.path_from_root, folder.content);
    }
    println!("+--------+--------------------------------------+--------+----------------------------");
}

fn dump_libfiles(libfiles: &Vec<LibraryFile>) {
    println!("Images");
    println!("+--------+--------------------------------------+--------+--------+------------------+----------+");
    println!(" id      + uuid                                 + folder + extens + basename         + sidecars +");
    println!("+--------+--------------------------------------+--------+--------+------------------+----------+");
    for libfile in libfiles {
        println!("+ {:>6} + {} + {:>6} + {:<6} + {:<16} + {:<8} +",
                 libfile.id(), libfile.uuid(), libfile.folder,
                 libfile.extension, libfile.basename,
                 libfile.sidecar_extensions);
    }
    println!("+--------+--------------------------------------+--------+--------+------------------+----------+");
}

fn dump_images(images: &Vec<Image>) {
    println!("Images");
    println!("+--------+--------------------------------------+--------+--------+----+----+");
    println!(" id      + uuid                                 + root   + format + or + P +");
    println!("+--------+--------------------------------------+--------+--------+----+----+");
    for image in images {
        println!("+ {:>6} + {} + {:>6} + {:<6} + {:<2} + {} +",
                 image.id(), image.uuid(), image.root_file,
                 image.file_format,
                 image.orientation.as_ref().unwrap_or(&String::from("")),
                 image.pick);
    }
    println!("+--------+--------------------------------------+--------+--------+----+----+");

}

fn dump_collections(collections: &Vec<Collection>) {
    println!("Collections");
    println!("+--------+--------------------------------------+--------+------");
    println!(" id      + name                                 + parent +");
    println!("+--------+--------------------------------------+--------+------");
    for collection in collections {
        println!("+ {:>6} + {:<36} + {:>6} + {}",
                 collection.id(), collection.name, collection.parent,
                 collection.system_only)
    }
    println!("+--------+--------------------------------------+--------+------");

}

fn process_audit(_: &Args) {

}
<commit_msg>dumper: dump keywords hierarchy<commit_after>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

extern crate lrcat;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate docopt;

use std::collections::BTreeMap;
use std::path::PathBuf;

use docopt::Docopt;

use lrcat::{
    Catalog,
    CatalogVersion,
    Collection,
    Folders,
    Image,
    Keyword, KeywordTree,
    LibraryFile,
    LrId,
    LrObject
};

const USAGE: &'static str = "
Usage:
  dumper <command> ([--all] | [--collections] [--libfiles] [--images] [--folders] [--keywords]) <path>

Options:
    --all          Select all objects
    --collections  Select only collections
    --libfiles     Select only library files
    --images       Select only images
    --folders      Select only folders
    --keywords     Select only keywords

Commands are:
    dump           Dump the objects
    audit          Audit mode: output what we ignored
";

#[derive(Debug, Deserialize)]
struct Args {
    arg_command: Command,
    arg_path: PathBuf,
    flag_all: bool,
    flag_collections: bool,
    flag_libfiles: bool,
    flag_images: bool,
    flag_folders: bool,
    flag_keywords: bool
}

#[derive(Debug, Deserialize)]
enum Command {
    Dump,
    Audit,
    Unknown(String)
}

fn main() {

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.argv(std::env::args()).deserialize())
        .unwrap_or_else(|e| e.exit());
    {
        match args.arg_command {
            Command::Dump =>
                process_dump(&args),
            Command::Audit =>
                process_audit(&args),
            _ =>
                ()
        };
    }
}

fn process_dump(args: &Args) {
    let mut catalog = Catalog::new(&args.arg_path);
    if catalog.open() {
        catalog.load_version();
        println!("Catalog:");
        println!("\tVersion: {} ({:?})", catalog.version,
                 catalog.catalog_version);
        println!("\tRoot keyword id: {}", catalog.root_keyword_id);

        if catalog.catalog_version != CatalogVersion::Lr4 {
            println!("Unsupported catalog version");
            return;
        }
        {
            let root_keyword_id = catalog.root_keyword_id;
            let keywordtree = catalog.load_keywords_tree();
            let keywords = catalog.load_keywords();
            println!("\tKeywords count: {}", keywords.len());

            if args.flag_all || args.flag_keywords {
                dump_keywords(root_keyword_id, &keywords, &keywordtree);
            }
        }

        {
            let folders = catalog.load_folders();
            if args.flag_all || args.flag_folders {
                dump_folders(&folders);
            }
        }

        {
            let libfiles = catalog.load_library_files();
            if args.flag_all || args.flag_libfiles {
                dump_libfiles(&libfiles);
            }
        }
        {
            let images = catalog.load_images();
            if args.flag_all || args.flag_images {
                dump_images(&images);
            }
        }
        {
            let collections = catalog.load_collections();
            if args.flag_all || args.flag_collections {
                dump_collections(&collections);
            }
        }
    }
}

fn print_keyword(level: i32, id: LrId, keywords: &BTreeMap<i64,Keyword>, tree: &KeywordTree) {
    if let Some(keyword) = keywords.get(&id) {
        let mut indent = String::from("");
        if level > 0 {
            for _ in 0..level-1 {
                indent.push(' ');
            }
            indent.push_str("+ ")
        }
        println!("| {:>7} | {} | {:>7} | {}{}", keyword.id(), keyword.uuid(),
                 keyword.parent, indent, keyword.name);
        let children = tree.children_for(id);
        for child in children {
            print_keyword(level + 1, child, keywords, tree);
        }
    }
}

fn dump_keywords(root: LrId, keywords: &BTreeMap<i64,Keyword>, tree: &KeywordTree) {
    println!("Keywords");
    println!("+---------+--------------------------------------+---------+----------------------------");
    println!("| id      | uuid                                 | parent  | name");
    println!("+---------+--------------------------------------+---------+----------------------------");
    print_keyword(0, root, keywords, tree);
    println!("+---------+--------------------------------------+---------+----------------------------");
}

fn dump_folders(folders: &Folders) {
    println!("Root Folders");
    println!("+--------+--------------------------------------+------------------+----------------------------");
    println!(" id      + uuid                                 + name             + absolute path");
    println!("+--------+--------------------------------------+------------------+----------------------------");
    for root in &folders.roots {
        println!("+ {:>6} + {} + {:<16} + {:<26}", root.id(), root.uuid(), root.name, root.absolute_path);
    }
    println!("+--------+--------------------------------------+------------------+----------------------------");
    println!("Folders");
    println!("+--------+--------------------------------------+--------+----------------------------");
    println!("+ id     + uuid                                 + root   + path");
    println!("+--------+--------------------------------------+--------+----------------------------");
    for folder in &folders.folders {
        println!("+ {:>6} + {} + {:>6} + {:<26} + {:?}",
                 folder.id(), folder.uuid(), folder.root_folder,
                 folder.path_from_root, folder.content);
    }
    println!("+--------+--------------------------------------+--------+----------------------------");
}

fn dump_libfiles(libfiles: &Vec<LibraryFile>) {
    println!("Images");
    println!("+--------+--------------------------------------+--------+--------+------------------+----------+");
    println!(" id      + uuid                                 + folder + extens + basename         + sidecars +");
    println!("+--------+--------------------------------------+--------+--------+------------------+----------+");
    for libfile in libfiles {
        println!("+ {:>6} + {} + {:>6} + {:<6} + {:<16} + {:<8} +",
                 libfile.id(), libfile.uuid(), libfile.folder,
                 libfile.extension, libfile.basename,
                 libfile.sidecar_extensions);
    }
    println!("+--------+--------------------------------------+--------+--------+------------------+----------+");
}

fn dump_images(images: &Vec<Image>) {
    println!("Images");
    println!("+--------+--------------------------------------+--------+--------+----+----+");
    println!(" id      + uuid                                 + root   + format + or + P +");
    println!("+--------+--------------------------------------+--------+--------+----+----+");
    for image in images {
        println!("+ {:>6} + {} + {:>6} + {:<6} + {:<2} + {} +",
                 image.id(), image.uuid(), image.root_file,
                 image.file_format,
                 image.orientation.as_ref().unwrap_or(&String::from("")),
                 image.pick);
    }
    println!("+--------+--------------------------------------+--------+--------+----+----+");

}

fn dump_collections(collections: &Vec<Collection>) {
    println!("Collections");
    println!("+--------+--------------------------------------+--------+------");
    println!(" id      + name                                 + parent +");
    println!("+--------+--------------------------------------+--------+------");
    for collection in collections {
        println!("+ {:>6} + {:<36} + {:>6} + {}",
                 collection.id(), collection.name, collection.parent,
                 collection.system_only)
    }
    println!("+--------+--------------------------------------+--------+------");

}

fn process_audit(_: &Args) {

}
<|endoftext|>


<commit_before>use globset;
use notify;
use std::{error::Error as StdError, fmt, io};

pub type Result<T> = ::std::result::Result<T, Error>;

pub enum Error {
    Canonicalization(String, io::Error),
    Glob(globset::Error),
    Io(io::Error),
    Notify(notify::Error),
}

impl StdError for Error {}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Self {
        Error::Glob(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::Io(err)
    }
}

impl From<notify::Error> for Error {
    fn from(err: notify::Error) -> Self {
        match err {
            notify::Error::Io(err) => Error::Io(err),
            other => Error::Notify(other),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{} error: {}",
            match self {
                Error::Canonicalization(_, _) => "Path",
                Error::Glob(_) => "Globset",
                Error::Io(_) => "I/O",
                Error::Notify(_) => "Notify",
            },
            match self {
                Error::Canonicalization(path, err) => {
                    format!("couldn't canonicalize '{}':\n{}", path, err)
                }
                err => format!("{}", err),
            }
        )
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}
<commit_msg>Add missing impl for StdError<commit_after>use globset;
use notify;
use std::{error::Error as StdError, fmt, io};

pub type Result<T> = ::std::result::Result<T, Error>;

pub enum Error {
    Canonicalization(String, io::Error),
    Glob(globset::Error),
    Io(io::Error),
    Notify(notify::Error),
}

impl StdError for Error {
    fn description (&self) -> &str {
        // This method is soft-deprecated and shouldn't be used,
        // see Display for the actual description.
        "a watchexec error"
    }
}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Self {
        Error::Glob(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::Io(err)
    }
}

impl From<notify::Error> for Error {
    fn from(err: notify::Error) -> Self {
        match err {
            notify::Error::Io(err) => Error::Io(err),
            other => Error::Notify(other),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{} error: {}",
            match self {
                Error::Canonicalization(_, _) => "Path",
                Error::Glob(_) => "Globset",
                Error::Io(_) => "I/O",
                Error::Notify(_) => "Notify",
            },
            match self {
                Error::Canonicalization(path, err) => {
                    format!("couldn't canonicalize '{}':\n{}", path, err)
                }
                err => format!("{}", err),
            }
        )
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}
<|endoftext|>


<commit_before>//! Contains error type for handling I/O and Errno errors
use std::io;
use std::error;
use std::fmt;
use nix;

use char_iter;

/// The error type for Rustyline errors that can arise from
/// I/O related errors or Errno when using the nix-rust library
#[derive(Debug)]
pub enum ReadlineError {
    /// I/O Error
    Io(io::Error),
    /// Error from syscall
    Errno(nix::Error),
    /// Chars Error
    Char(char_iter::CharsError),
    /// EOF (Ctrl-d)
    Eof,
    /// Ctrl-C
    Interrupted,
}

impl fmt::Display for ReadlineError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ReadlineError::Io(ref err) => err.fmt(f),
            ReadlineError::Errno(ref err) => write!(f, "Errno: {}", err.errno().desc()),
            ReadlineError::Char(ref err) => err.fmt(f),
            ReadlineError::Eof => write!(f, "EOF"),
            ReadlineError::Interrupted => write!(f, "Interrupted"),
        }
    }
}

impl error::Error for ReadlineError {
    fn description(&self) -> &str {
        match *self {
            ReadlineError::Io(ref err) => err.description(),
            ReadlineError::Errno(ref err) => err.errno().desc(),
            ReadlineError::Char(ref err) => err.description(),
            ReadlineError::Eof => "EOF",
            ReadlineError::Interrupted => "Interrupted",
        }
    }
}

impl From<io::Error> for ReadlineError {
    fn from(err: io::Error) -> ReadlineError {
        ReadlineError::Io(err)
    }
}

impl From<nix::Error> for ReadlineError {
    fn from(err: nix::Error) -> ReadlineError {
        ReadlineError::Errno(err)
    }
}

impl From<char_iter::CharsError> for ReadlineError {
    fn from(err: char_iter::CharsError) -> ReadlineError {
        ReadlineError::Char(err)
    }
}

impl ReadlineError {
    pub fn from_errno(errno: nix::errno::Errno) -> ReadlineError {
        ReadlineError::from(nix::Error::from_errno(errno))
    }
}
<commit_msg>Add conditional compilation for unix errors<commit_after>//! Contains error type for handling I/O and Errno errors
use std::io;
use std::error;
use std::fmt;
use nix;

use char_iter;

/// The error type for Rustyline errors that can arise from
/// I/O related errors or Errno when using the nix-rust library
#[derive(Debug)]
pub enum ReadlineError {
    /// I/O Error
    Io(io::Error),
    /// Chars Error
    Char(char_iter::CharsError),
    /// EOF (Ctrl-d)
    Eof,
    /// Ctrl-C
    Interrupted,
    /// Unix Error from syscall
    #[cfg(unix)]
    Errno(nix::Error),
}

impl fmt::Display for ReadlineError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ReadlineError::Io(ref err) => err.fmt(f),
            ReadlineError::Char(ref err) => err.fmt(f),
            ReadlineError::Eof => write!(f, "EOF"),
            ReadlineError::Interrupted => write!(f, "Interrupted"),
            #[cfg(unix)]
            ReadlineError::Errno(ref err) => write!(f, "Errno: {}", err.errno().desc()),
        }
    }
}

impl error::Error for ReadlineError {
    fn description(&self) -> &str {
        match *self {
            ReadlineError::Io(ref err) => err.description(),
            ReadlineError::Char(ref err) => err.description(),
            ReadlineError::Eof => "EOF",
            ReadlineError::Interrupted => "Interrupted",
            #[cfg(unix)]
            ReadlineError::Errno(ref err) => err.errno().desc(),
        }
    }
}

impl From<io::Error> for ReadlineError {
    fn from(err: io::Error) -> ReadlineError {
        ReadlineError::Io(err)
    }
}

#[cfg(unix)]
impl From<nix::Error> for ReadlineError {
    fn from(err: nix::Error) -> ReadlineError {
        ReadlineError::Errno(err)
    }
}

impl From<char_iter::CharsError> for ReadlineError {
    fn from(err: char_iter::CharsError) -> ReadlineError {
        ReadlineError::Char(err)
    }
}

impl ReadlineError {
    #[cfg(unix)]
    pub fn from_errno(errno: nix::errno::Errno) -> ReadlineError {
        ReadlineError::from(nix::Error::from_errno(errno))
    }
}
<|endoftext|>


<commit_before>//! Error types.

use byteorder;
use phf;
use std::error;
use std::convert::From;
use std::fmt;
use std::io;
use std::result;
use std::collections::HashMap;

use {Result, DbErrorNew};
use types::Type;

include!(concat!(env!("OUT_DIR"), "/sqlstate.rs"));

/// A Postgres error or notice.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct DbError {
    severity: String,
    code: SqlState,
    message: String,
    detail: Option<String>,
    hint: Option<String>,
    position: Option<ErrorPosition>,
    where_: Option<String>,
    schema: Option<String>,
    table: Option<String>,
    column: Option<String>,
    datatype: Option<String>,
    constraint: Option<String>,
    file: String,
    line: u32,
    routine: String,
}

impl DbErrorNew for DbError {
    fn new_raw(fields: Vec<(u8, String)>) -> result::Result<DbError, ()> {
        let mut map: HashMap<_, _> = fields.into_iter().collect();
        Ok(DbError {
            severity: try!(map.remove(&b'S').ok_or(())),
            code: SqlState::from_code(try!(map.remove(&b'C').ok_or(()))),
            message: try!(map.remove(&b'M').ok_or(())),
            detail: map.remove(&b'D'),
            hint: map.remove(&b'H'),
            position: match map.remove(&b'P') {
                Some(pos) => Some(ErrorPosition::Normal(try!(pos.parse().map_err(|_| ())))),
                None => match map.remove(&b'p') {
                    Some(pos) => Some(ErrorPosition::Internal {
                        position: try!(pos.parse().map_err(|_| ())),
                        query: try!(map.remove(&b'q').ok_or(()))
                    }),
                    None => None
                }
            },
            where_: map.remove(&b'W'),
            schema: map.remove(&b's'),
            table: map.remove(&b't'),
            column: map.remove(&b'c'),
            datatype: map.remove(&b'd'),
            constraint: map.remove(&b'n'),
            file: try!(map.remove(&b'F').ok_or(())),
            line: try!(map.remove(&b'L').and_then(|l| l.parse().ok()).ok_or(())),
            routine: try!(map.remove(&b'R').ok_or(())),
        })
    }

    fn new_connect<T>(fields: Vec<(u8, String)>) -> result::Result<T, ConnectError> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(ConnectError::DbError(err)),
            Err(()) => Err(ConnectError::IoError(::bad_response())),
        }
    }

    fn new<T>(fields: Vec<(u8, String)>) -> Result<T> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(Error::DbError(err)),
            Err(()) => Err(Error::IoError(::bad_response())),
        }
    }
}

impl DbError {
    /// The field contents are ERROR, FATAL, or PANIC (in an error message),
    /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a
    /// localized translation of one of these.
    pub fn severity(&self) -> &str {
        &self.severity
    }

    /// The SQLSTATE code for the error.
    pub fn code(&self) -> &SqlState {
        &self.code
    }

    /// The primary human-readable error message. This should be accurate but
    /// terse (typically one line).
    pub fn message(&self) -> &str {
        &self.message
    }

    /// An optional secondary error message carrying more detail about the
    /// problem. Might run to multiple lines.
    pub fn detail(&self) -> Option<&str> {
        self.detail.as_ref().map(|s| &**s)
    }

    /// An optional suggestion what to do about the problem. This is intended
    /// to differ from Detail in that it offers advice (potentially
    /// inappropriate) rather than hard facts. Might run to multiple lines.
    pub fn hint(&self) -> Option<&str> {
        self.hint.as_ref().map(|s| &**s)
    }

    /// An optional error cursor position into either the original query string
    /// or an internally generated query.
    pub fn position(&self) -> Option<&ErrorPosition> {
        self.position.as_ref()
    }

    /// An indication of the context in which the error occurred. Presently
    /// this includes a call stack traceback of active procedural language
    /// functions and internally-generated queries. The trace is one entry per
    /// line, most recent first.
    pub fn where_(&self) -> Option<&str> {
        self.where_.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific database object, the name
    /// of the schema containing that object, if any. (PostgreSQL 9.3+)
    pub fn schema(&self) -> Option<&str> {
        self.schema.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific table, the name of the
    /// table. (Refer to the schema name field for the name of the table's
    /// schema.) (PostgreSQL 9.3+)
    pub fn table(&self) -> Option<&str> {
        self.table.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific table column, the name of
    /// the column. (Refer to the schema and table name fields to identify the
    /// table.) (PostgreSQL 9.3+)
    pub fn column(&self) -> Option<&str> {
        self.column.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific data type, the name of the
    /// data type. (Refer to the schema name field for the name of the data
    /// type's schema.) (PostgreSQL 9.3+)
    pub fn datatype(&self) -> Option<&str> {
        self.datatype.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific constraint, the name of the
    /// constraint. Refer to fields listed above for the associated table or
    /// domain. (For this purpose, indexes are treated as constraints, even if
    /// they weren't created with constraint syntax.) (PostgreSQL 9.3+)
    pub fn constraint(&self) -> Option<&str> {
        self.constraint.as_ref().map(|s| &**s)
    }

    /// The file name of the source-code location where the error was reported.
    pub fn file(&self) -> &str {
        &self.file
    }

    /// The line number of the source-code location where the error was
    /// reported.
    pub fn line(&self) -> u32 {
        self.line
    }

    /// The name of the source-code routine reporting the error.
    pub fn routine(&self) -> &str {
        &self.routine
    }
}

impl fmt::Display for DbError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}: {}", self.severity, self.message)
    }
}

impl error::Error for DbError {
    fn description(&self) -> &str {
        &self.message
    }
}

/// Reasons a new Postgres connection could fail.
#[derive(Debug)]
pub enum ConnectError {
    /// The provided URL could not be parsed.
    InvalidUrl(String),
    /// The URL was missing a user.
    MissingUser,
    /// An error from the Postgres server itself.
    DbError(DbError),
    /// A password was required but not provided in the URL.
    MissingPassword,
    /// The Postgres server requested an authentication method not supported
    /// by the driver.
    UnsupportedAuthentication,
    /// The Postgres server does not support SSL encryption.
    NoSslSupport,
    /// There was an error initializing the SSL session.
    SslError(Box<error::Error+Sync+Send>),
    /// There was an error communicating with the server.
    IoError(io::Error),
}

impl fmt::Display for ConnectError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            ConnectError::InvalidUrl(ref msg) => write!(fmt, ": {}", msg),
            _ => Ok(())
        }
    }
}

impl error::Error for ConnectError {
    fn description(&self) -> &str {
        match *self {
            ConnectError::InvalidUrl(_) => "Invalid URL",
            ConnectError::MissingUser => "User missing in URL",
            ConnectError::DbError(_) => "An error from the Postgres server itself",
            ConnectError::MissingPassword => "The server requested a password but none was provided",
            ConnectError::UnsupportedAuthentication => {
                "The server requested an unsupported authentication method"
            }
            ConnectError::NoSslSupport => "The server does not support SSL",
            ConnectError::SslError(_) => "Error initiating SSL session",
            ConnectError::IoError(_) => "Error communicating with server",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConnectError::DbError(ref err) => Some(err),
            ConnectError::SslError(ref err) => Some(&**err),
            ConnectError::IoError(ref err) => Some(err),
            _ => None
        }
    }
}

impl From<io::Error> for ConnectError {
    fn from(err: io::Error) -> ConnectError {
        ConnectError::IoError(err)
    }
}

impl From<DbError> for ConnectError {
    fn from(err: DbError) -> ConnectError {
        ConnectError::DbError(err)
    }
}

impl From<byteorder::Error> for ConnectError {
    fn from(err: byteorder::Error) -> ConnectError {
        ConnectError::IoError(From::from(err))
    }
}

/// Represents the position of an error in a query.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
    /// A position in the original query.
    Normal(u32),
    /// A position in an internally generated query.
    Internal {
        /// The byte position.
        position: u32,
        /// A query generated by the Postgres server.
        query: String
    }
}

/// An error encountered when communicating with the Postgres server.
#[derive(Debug)]
pub enum Error {
    /// An error reported by the Postgres server.
    DbError(DbError),
    /// An error communicating with the Postgres server.
    IoError(io::Error),
    /// An attempt was made to convert between incompatible Rust and Postgres
    /// types.
    WrongType(Type),
    /// An attempt was made to read from a column that does not exist.
    InvalidColumn,
    /// An error converting between Postgres and Rust types.
    Conversion(Box<error::Error+Sync+Send>),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            Error::WrongType(ref ty) => write!(fmt, ": saw type {:?}", ty),
            _ => Ok(()),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::DbError(_) => "An error reported by the Postgres server",
            Error::IoError(_) => "An error communicating with the Postgres server",
            Error::WrongType(_) => "Unexpected type",
            Error::InvalidColumn => "Invalid column",
            Error::Conversion(_) => "An error converting between Postgres and Rust types",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::DbError(ref err) => Some(err),
            Error::IoError(ref err) => Some(err),
            Error::Conversion(ref err) => Some(&**err),
            _ => None
        }
    }
}

impl From<DbError> for Error {
    fn from(err: DbError) -> Error {
        Error::DbError(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::IoError(err)
    }
}

impl From<byteorder::Error> for Error {
    fn from(err: byteorder::Error) -> Error {
        Error::IoError(From::from(err))
    }
}
<commit_msg>Make error Display impls more descriptive<commit_after>//! Error types.

use byteorder;
use phf;
use std::error;
use std::convert::From;
use std::fmt;
use std::io;
use std::result;
use std::collections::HashMap;

use {Result, DbErrorNew};
use types::Type;

include!(concat!(env!("OUT_DIR"), "/sqlstate.rs"));

/// A Postgres error or notice.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct DbError {
    severity: String,
    code: SqlState,
    message: String,
    detail: Option<String>,
    hint: Option<String>,
    position: Option<ErrorPosition>,
    where_: Option<String>,
    schema: Option<String>,
    table: Option<String>,
    column: Option<String>,
    datatype: Option<String>,
    constraint: Option<String>,
    file: String,
    line: u32,
    routine: String,
}

impl DbErrorNew for DbError {
    fn new_raw(fields: Vec<(u8, String)>) -> result::Result<DbError, ()> {
        let mut map: HashMap<_, _> = fields.into_iter().collect();
        Ok(DbError {
            severity: try!(map.remove(&b'S').ok_or(())),
            code: SqlState::from_code(try!(map.remove(&b'C').ok_or(()))),
            message: try!(map.remove(&b'M').ok_or(())),
            detail: map.remove(&b'D'),
            hint: map.remove(&b'H'),
            position: match map.remove(&b'P') {
                Some(pos) => Some(ErrorPosition::Normal(try!(pos.parse().map_err(|_| ())))),
                None => match map.remove(&b'p') {
                    Some(pos) => Some(ErrorPosition::Internal {
                        position: try!(pos.parse().map_err(|_| ())),
                        query: try!(map.remove(&b'q').ok_or(()))
                    }),
                    None => None
                }
            },
            where_: map.remove(&b'W'),
            schema: map.remove(&b's'),
            table: map.remove(&b't'),
            column: map.remove(&b'c'),
            datatype: map.remove(&b'd'),
            constraint: map.remove(&b'n'),
            file: try!(map.remove(&b'F').ok_or(())),
            line: try!(map.remove(&b'L').and_then(|l| l.parse().ok()).ok_or(())),
            routine: try!(map.remove(&b'R').ok_or(())),
        })
    }

    fn new_connect<T>(fields: Vec<(u8, String)>) -> result::Result<T, ConnectError> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(ConnectError::DbError(err)),
            Err(()) => Err(ConnectError::IoError(::bad_response())),
        }
    }

    fn new<T>(fields: Vec<(u8, String)>) -> Result<T> {
        match DbError::new_raw(fields) {
            Ok(err) => Err(Error::DbError(err)),
            Err(()) => Err(Error::IoError(::bad_response())),
        }
    }
}

impl DbError {
    /// The field contents are ERROR, FATAL, or PANIC (in an error message),
    /// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a
    /// localized translation of one of these.
    pub fn severity(&self) -> &str {
        &self.severity
    }

    /// The SQLSTATE code for the error.
    pub fn code(&self) -> &SqlState {
        &self.code
    }

    /// The primary human-readable error message. This should be accurate but
    /// terse (typically one line).
    pub fn message(&self) -> &str {
        &self.message
    }

    /// An optional secondary error message carrying more detail about the
    /// problem. Might run to multiple lines.
    pub fn detail(&self) -> Option<&str> {
        self.detail.as_ref().map(|s| &**s)
    }

    /// An optional suggestion what to do about the problem. This is intended
    /// to differ from Detail in that it offers advice (potentially
    /// inappropriate) rather than hard facts. Might run to multiple lines.
    pub fn hint(&self) -> Option<&str> {
        self.hint.as_ref().map(|s| &**s)
    }

    /// An optional error cursor position into either the original query string
    /// or an internally generated query.
    pub fn position(&self) -> Option<&ErrorPosition> {
        self.position.as_ref()
    }

    /// An indication of the context in which the error occurred. Presently
    /// this includes a call stack traceback of active procedural language
    /// functions and internally-generated queries. The trace is one entry per
    /// line, most recent first.
    pub fn where_(&self) -> Option<&str> {
        self.where_.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific database object, the name
    /// of the schema containing that object, if any. (PostgreSQL 9.3+)
    pub fn schema(&self) -> Option<&str> {
        self.schema.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific table, the name of the
    /// table. (Refer to the schema name field for the name of the table's
    /// schema.) (PostgreSQL 9.3+)
    pub fn table(&self) -> Option<&str> {
        self.table.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific table column, the name of
    /// the column. (Refer to the schema and table name fields to identify the
    /// table.) (PostgreSQL 9.3+)
    pub fn column(&self) -> Option<&str> {
        self.column.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific data type, the name of the
    /// data type. (Refer to the schema name field for the name of the data
    /// type's schema.) (PostgreSQL 9.3+)
    pub fn datatype(&self) -> Option<&str> {
        self.datatype.as_ref().map(|s| &**s)
    }

    /// If the error was associated with a specific constraint, the name of the
    /// constraint. Refer to fields listed above for the associated table or
    /// domain. (For this purpose, indexes are treated as constraints, even if
    /// they weren't created with constraint syntax.) (PostgreSQL 9.3+)
    pub fn constraint(&self) -> Option<&str> {
        self.constraint.as_ref().map(|s| &**s)
    }

    /// The file name of the source-code location where the error was reported.
    pub fn file(&self) -> &str {
        &self.file
    }

    /// The line number of the source-code location where the error was
    /// reported.
    pub fn line(&self) -> u32 {
        self.line
    }

    /// The name of the source-code routine reporting the error.
    pub fn routine(&self) -> &str {
        &self.routine
    }
}

impl fmt::Display for DbError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{}: {}", self.severity, self.message)
    }
}

impl error::Error for DbError {
    fn description(&self) -> &str {
        &self.message
    }
}

/// Reasons a new Postgres connection could fail.
#[derive(Debug)]
pub enum ConnectError {
    /// The provided URL could not be parsed.
    InvalidUrl(String),
    /// The URL was missing a user.
    MissingUser,
    /// An error from the Postgres server itself.
    DbError(DbError),
    /// A password was required but not provided in the URL.
    MissingPassword,
    /// The Postgres server requested an authentication method not supported
    /// by the driver.
    UnsupportedAuthentication,
    /// The Postgres server does not support SSL encryption.
    NoSslSupport,
    /// There was an error initializing the SSL session.
    SslError(Box<error::Error+Sync+Send>),
    /// There was an error communicating with the server.
    IoError(io::Error),
}

impl fmt::Display for ConnectError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            ConnectError::InvalidUrl(ref msg) => write!(fmt, ": {}", msg),
            ConnectError::DbError(ref err) => write!(fmt, ": {}", err),
            ConnectError::SslError(ref err) => write!(fmt, ": {}", err),
            ConnectError::IoError(ref err) => write!(fmt, ": {}", err),
            _ => Ok(())
        }
    }
}

impl error::Error for ConnectError {
    fn description(&self) -> &str {
        match *self {
            ConnectError::InvalidUrl(_) => "Invalid URL",
            ConnectError::MissingUser => "User missing in URL",
            ConnectError::DbError(_) => "Error reported by Postgres",
            ConnectError::MissingPassword => "The server requested a password but none was provided",
            ConnectError::UnsupportedAuthentication => {
                "The server requested an unsupported authentication method"
            }
            ConnectError::NoSslSupport => "The server does not support SSL",
            ConnectError::SslError(_) => "Error initiating SSL session",
            ConnectError::IoError(_) => "Error communicating with the server",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConnectError::DbError(ref err) => Some(err),
            ConnectError::SslError(ref err) => Some(&**err),
            ConnectError::IoError(ref err) => Some(err),
            _ => None
        }
    }
}

impl From<io::Error> for ConnectError {
    fn from(err: io::Error) -> ConnectError {
        ConnectError::IoError(err)
    }
}

impl From<DbError> for ConnectError {
    fn from(err: DbError) -> ConnectError {
        ConnectError::DbError(err)
    }
}

impl From<byteorder::Error> for ConnectError {
    fn from(err: byteorder::Error) -> ConnectError {
        ConnectError::IoError(From::from(err))
    }
}

/// Represents the position of an error in a query.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ErrorPosition {
    /// A position in the original query.
    Normal(u32),
    /// A position in an internally generated query.
    Internal {
        /// The byte position.
        position: u32,
        /// A query generated by the Postgres server.
        query: String
    }
}

/// An error encountered when communicating with the Postgres server.
#[derive(Debug)]
pub enum Error {
    /// An error reported by the Postgres server.
    DbError(DbError),
    /// An error communicating with the Postgres server.
    IoError(io::Error),
    /// An attempt was made to convert between incompatible Rust and Postgres
    /// types.
    WrongType(Type),
    /// An attempt was made to read from a column that does not exist.
    InvalidColumn,
    /// An error converting between Postgres and Rust types.
    Conversion(Box<error::Error+Sync+Send>),
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        try!(fmt.write_str(error::Error::description(self)));
        match *self {
            Error::DbError(ref err) => write!(fmt, ": {}", err),
            Error::IoError(ref err) => write!(fmt, ": {}", err),
            Error::WrongType(ref ty) => write!(fmt, ": saw type {:?}", ty),
            Error::Conversion(ref err) => write!(fmt, ": {}", err),
            _ => Ok(()),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::DbError(_) => "Error reported by Postgres",
            Error::IoError(_) => "Error communicating with the server",
            Error::WrongType(_) => "Unexpected type",
            Error::InvalidColumn => "Invalid column",
            Error::Conversion(_) => "Error converting between Postgres and Rust types",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::DbError(ref err) => Some(err),
            Error::IoError(ref err) => Some(err),
            Error::Conversion(ref err) => Some(&**err),
            _ => None
        }
    }
}

impl From<DbError> for Error {
    fn from(err: DbError) -> Error {
        Error::DbError(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::IoError(err)
    }
}

impl From<byteorder::Error> for Error {
    fn from(err: byteorder::Error) -> Error {
        Error::IoError(From::from(err))
    }
}
<|endoftext|>


<commit_before>//! Defines error types.
use crate::types::DataType;
use pyo3::{exceptions as exc, PyErr, PyErrArguments, PyErrValue, PyObject, Python, ToPyObject};
use std::fmt;

/// Represents a dimension and dtype of numpy array.
///
/// Only for error formatting.
#[derive(Debug)]
pub(crate) struct ArrayDim {
    dim: Option<usize>,
    dtype: Option<DataType>,
}

impl fmt::Display for ArrayDim {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let ArrayDim { dim, dtype } = self;
        match (dim, dtype) {
            (Some(dim), Some(dtype)) => write!(f, "dim={:?}, dtype={:?}", dim, dtype),
            (None, Some(dtype)) => write!(f, "dim=_, dtype={:?}", dtype),
            (Some(dim), None) => write!(f, "dim={:?}, dtype=Unknown", dim),
            (None, None) => write!(f, "dim=_, dtype=Unknown"),
        }
    }
}

/// Represents that shapes of the given arrays don't match.
#[derive(Debug)]
pub struct ShapeError {
    from: ArrayDim,
    to: ArrayDim,
}

impl ShapeError {
    pub(crate) fn new(
        from_type: i32,
        from_dim: usize,
        to_type: DataType,
        to_dim: Option<usize>,
    ) -> Self {
        ShapeError {
            from: ArrayDim {
                dim: Some(from_dim),
                dtype: DataType::from_i32(from_type),
            },
            to: ArrayDim {
                dim: to_dim,
                dtype: Some(to_type),
            },
        }
    }
}

impl fmt::Display for ShapeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let ShapeError { from, to } = self;
        write!(f, "Shape Mismatch:\n from=({}), to=({})", from, to)
    }
}

macro_rules! impl_pyerr {
    ($err_type: ty) => {
        impl std::error::Error for $err_type {}

        impl PyErrArguments for $err_type {
            fn arguments(&self, py: Python) -> PyObject {
                format!("{}", self).to_object(py)
            }
        }

        impl std::convert::From<$err_type> for PyErr {
            fn from(err: $err_type) -> PyErr {
                PyErr::from_value::<exc::PyTypeError>(PyErrValue::from_err_args(err))
            }
        }
    };
}

impl_pyerr!(ShapeError);

/// Represents that given vec cannot be treated as array.
#[derive(Debug)]
pub struct FromVecError {
    len1: usize,
    len2: usize,
}

impl FromVecError {
    pub(crate) fn new(len1: usize, len2: usize) -> Self {
        FromVecError { len1, len2 }
    }
}

impl fmt::Display for FromVecError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let FromVecError { len1, len2 } = self;
        write!(
            f,
            "Invalid lenension as an array\n Expected the same length {}, but found {}",
            len1, len2
        )
    }
}

impl_pyerr!(FromVecError);

/// Represents that the array is not contiguous.
#[derive(Debug)]
pub struct NotContiguousError;

impl fmt::Display for NotContiguousError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "The given array is not contiguous",)
    }
}

impl_pyerr!(NotContiguousError);
<commit_msg>Finish migration of error.rs<commit_after>//! Defines error types.
use crate::types::DataType;
use pyo3::{exceptions as exc, PyErr, PyErrArguments, PyObject, Python, ToPyObject};
use std::fmt;

/// Represents a dimension and dtype of numpy array.
///
/// Only for error formatting.
#[derive(Debug)]
pub(crate) struct ArrayDim {
    dim: Option<usize>,
    dtype: Option<DataType>,
}

impl fmt::Display for ArrayDim {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let ArrayDim { dim, dtype } = self;
        match (dim, dtype) {
            (Some(dim), Some(dtype)) => write!(f, "dim={:?}, dtype={:?}", dim, dtype),
            (None, Some(dtype)) => write!(f, "dim=_, dtype={:?}", dtype),
            (Some(dim), None) => write!(f, "dim={:?}, dtype=Unknown", dim),
            (None, None) => write!(f, "dim=_, dtype=Unknown"),
        }
    }
}

/// Represents that shapes of the given arrays don't match.
#[derive(Debug)]
pub struct ShapeError {
    from: ArrayDim,
    to: ArrayDim,
}

impl ShapeError {
    pub(crate) fn new(
        from_type: i32,
        from_dim: usize,
        to_type: DataType,
        to_dim: Option<usize>,
    ) -> Self {
        ShapeError {
            from: ArrayDim {
                dim: Some(from_dim),
                dtype: DataType::from_i32(from_type),
            },
            to: ArrayDim {
                dim: to_dim,
                dtype: Some(to_type),
            },
        }
    }
}

impl fmt::Display for ShapeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let ShapeError { from, to } = self;
        write!(f, "Shape Mismatch:\n from=({}), to=({})", from, to)
    }
}

macro_rules! impl_pyerr {
    ($err_type: ty) => {
        impl std::error::Error for $err_type {}

        impl PyErrArguments for $err_type {
            fn arguments(self, py: Python) -> PyObject {
                format!("{}", self).to_object(py)
            }
        }

        impl std::convert::From<$err_type> for PyErr {
            fn from(err: $err_type) -> PyErr {
                exc::PyTypeError::new_err(err)
            }
        }
    };
}

impl_pyerr!(ShapeError);

/// Represents that given vec cannot be treated as array.
#[derive(Debug)]
pub struct FromVecError {
    len1: usize,
    len2: usize,
}

impl FromVecError {
    pub(crate) fn new(len1: usize, len2: usize) -> Self {
        FromVecError { len1, len2 }
    }
}

impl fmt::Display for FromVecError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let FromVecError { len1, len2 } = self;
        write!(
            f,
            "Invalid lenension as an array\n Expected the same length {}, but found {}",
            len1, len2
        )
    }
}

impl_pyerr!(FromVecError);

/// Represents that the array is not contiguous.
#[derive(Debug)]
pub struct NotContiguousError;

impl fmt::Display for NotContiguousError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "The given array is not contiguous",)
    }
}

impl_pyerr!(NotContiguousError);
<|endoftext|>


<commit_before>extern crate tempdir;
extern crate hyper;
extern crate grnenvlib;
#[macro_use]
extern crate clap;

use clap::{Arg, App, AppSettings, SubCommand};
use grnenvlib::command;

fn main() {
    default_main();
}

fn cli() -> App<'static, 'static> {
    App::new("grnenv-rs")
        .version(crate_version!())
        .author(crate_authors!())
        .about("A tiny tool for switch multiple Groonga versions.")
        .setting(AppSettings::AllowExternalSubcommands)
        .subcommand(SubCommand::with_name("init").about("Prepare grnenv-rs."))
        .subcommand(SubCommand::with_name("install")
            .about("Install a given Groonga version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("switch")
            .about("Switch Groonga with given version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("versions").about("Display installed Groonga versions"))
        .subcommand(SubCommand::with_name("uninstall")
            .about("Uninstall a given Groonga version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("list").about("Display installable Groonga versions"))

}

#[cfg(not(windows))]
fn default_main() {
    let mut app = cli();
    let matches = app.clone().get_matches();
    match matches.subcommand() {
        ("init", _) => command::unix::init(),
        ("install", Some(m)) => command::unix::install(m),
        ("switch", Some(m)) => command::unix::switch(m),
        ("versions", _) => command::common::versions(),
        ("uninstall", Some(m)) => command::unix::uninstall(m),
        ("list", _) => command::unix::list(),
        (external, Some(ext_m)) => command::common::execute_external_command(external, ext_m),
        (_, _) => {
            let _ = app.print_help();
        },
    }
}

#[cfg(windows)]
fn default_main() {
    let mut app = cli();
    let matches = app.clone().get_matches();
    match matches.subcommand() {
        ("init", _) => command::windows::init(),
        ("install", Some(m)) => command::windows::install(m),
        ("switch", Some(m)) => command::windows::switch(m),
        ("versions", _) => command::common::versions(),
        ("uninstall", Some(m)) => command::windows::uninstall(m),
        ("list", _) => command::windows::list(),
        (external, Some(ext_m)) => command::common::execute_external_command(external, ext_m),
        (_, _) => {
            let _ = app.print_help();
        },
    }
}
<commit_msg>Remove a needless newline<commit_after>extern crate tempdir;
extern crate hyper;
extern crate grnenvlib;
#[macro_use]
extern crate clap;

use clap::{Arg, App, AppSettings, SubCommand};
use grnenvlib::command;

fn main() {
    default_main();
}

fn cli() -> App<'static, 'static> {
    App::new("grnenv-rs")
        .version(crate_version!())
        .author(crate_authors!())
        .about("A tiny tool for switch multiple Groonga versions.")
        .setting(AppSettings::AllowExternalSubcommands)
        .subcommand(SubCommand::with_name("init").about("Prepare grnenv-rs."))
        .subcommand(SubCommand::with_name("install")
            .about("Install a given Groonga version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("switch")
            .about("Switch Groonga with given version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("versions").about("Display installed Groonga versions"))
        .subcommand(SubCommand::with_name("uninstall")
            .about("Uninstall a given Groonga version and arch")
            .arg(Arg::with_name("VERSION").required(true))
            .arg(Arg::with_name("arch")
                .short("a")
                .long("arch")
                .value_name("ARCH")
                .help("Select architectures. e.g.) x86, x64")
                .takes_value(true)))
        .subcommand(SubCommand::with_name("list").about("Display installable Groonga versions"))
}

#[cfg(not(windows))]
fn default_main() {
    let mut app = cli();
    let matches = app.clone().get_matches();
    match matches.subcommand() {
        ("init", _) => command::unix::init(),
        ("install", Some(m)) => command::unix::install(m),
        ("switch", Some(m)) => command::unix::switch(m),
        ("versions", _) => command::common::versions(),
        ("uninstall", Some(m)) => command::unix::uninstall(m),
        ("list", _) => command::unix::list(),
        (external, Some(ext_m)) => command::common::execute_external_command(external, ext_m),
        (_, _) => {
            let _ = app.print_help();
        },
    }
}

#[cfg(windows)]
fn default_main() {
    let mut app = cli();
    let matches = app.clone().get_matches();
    match matches.subcommand() {
        ("init", _) => command::windows::init(),
        ("install", Some(m)) => command::windows::install(m),
        ("switch", Some(m)) => command::windows::switch(m),
        ("versions", _) => command::common::versions(),
        ("uninstall", Some(m)) => command::windows::uninstall(m),
        ("list", _) => command::windows::list(),
        (external, Some(ext_m)) => command::common::execute_external_command(external, ext_m),
        (_, _) => {
            let _ = app.print_help();
        },
    }
}
<|endoftext|>