ddbug 0.1.0

Display debugging information
Documentation
extern crate env_logger;
extern crate getopts;
#[macro_use]
extern crate log;

use std::env;
use std::io::{self, Write};

extern crate ddbug;

fn main() {
    env_logger::init().ok();

    let mut opts = getopts::Options::new();
    opts.optflag("", "diff", "print difference between two files");
    opts.optflag("", "sort-name", "sort entries by type and name");
    opts.optflag("", "sort-size", "sort entries by size");
    opts.optflag(
        "",
        "ignore-added",
        "don't display differences due to added functions/types/variables",
    );
    opts.optflag(
        "",
        "ignore-deleted",
        "don't display differences due to deleted functions/types/variables",
    );
    opts.optflag(
        "",
        "ignore-function-address",
        "don't display function differences due to address changes",
    );
    opts.optflag(
        "",
        "ignore-function-size",
        "don't display function differences due to size changes",
    );
    opts.optflag(
        "",
        "ignore-function-inline",
        "don't display function differences due to inline changes",
    );
    opts.optflag(
        "",
        "ignore-variable-address",
        "don't display variable differences due to address changes",
    );
    opts.optopt("", "inline-depth", "depth of inlined subroutine calls (0 to disable)", "DEPTH");
    opts.optopt("", "unit", "print only entries within the given unit", "UNIT");
    opts.optopt("", "name", "print only entries with the given name", "NAME");
    opts.optopt("", "namespace", "print only entries within the given namespace", "NAMESPACE");

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            error!("{}", e);
            print_usage(&opts);
        }
    };

    let diff = matches.opt_present("diff");
    let sort = if matches.opt_present("sort-name") {
        ddbug::Sort::Name
    } else if matches.opt_present("sort-size") {
        ddbug::Sort::Size
    } else {
        ddbug::Sort::None
    };
    let ignore_added = matches.opt_present("ignore-added");
    let ignore_deleted = matches.opt_present("ignore-deleted");
    let ignore_function_address = matches.opt_present("ignore-function-address");
    let ignore_function_size = matches.opt_present("ignore-function-size");
    let ignore_function_inline = matches.opt_present("ignore-function-inline");
    let ignore_variable_address = matches.opt_present("ignore-variable-address");
    let inline_depth = if let Some(inline_depth) = matches.opt_str("inline-depth") {
        match inline_depth.parse::<usize>() {
            Ok(inline_depth) => inline_depth,
            Err(e) => {
                error!("Invalid argument '{}' to option 'inline-depth': {}", inline_depth, e);
                print_usage(&opts);
            }
        }
    } else {
        1
    };
    let unit = matches.opt_str("unit");
    let unit = unit.as_ref().map(|s| &s[..]);
    let name = matches.opt_str("name");
    let name = name.as_ref().map(|s| &s[..]);
    let namespace = matches.opt_str("namespace");
    let namespace = match namespace {
        Some(ref namespace) => namespace.split("::").collect(),
        None => Vec::new(),
    };

    let flags = ddbug::Flags {
        sort: sort,
        ignore_added: ignore_added,
        ignore_deleted: ignore_deleted,
        ignore_function_address: ignore_function_address,
        ignore_function_size: ignore_function_size,
        ignore_function_inline: ignore_function_inline,
        ignore_variable_address: ignore_variable_address,
        inline_depth: inline_depth,
        unit: unit,
        name: name,
        namespace: namespace,
    };

    if diff {
        if matches.free.len() != 2 {
            error!(
                "Invalid filename arguments (expected 2 filenames, found {})",
                matches.free.len()
            );
            print_usage(&opts);
        }
        let path_a = &matches.free[0];
        let path_b = &matches.free[1];

        if let Err(e) = ddbug::parse_file(path_a, &mut |file_a| {
            if let Err(e) = ddbug::parse_file(
                path_b,
                &mut |file_b| diff_file(file_a, file_b, &flags),
            ) {
                error!("{}: {}", path_b, e);
            }
            Ok(())
        }) {
            error!("{}: {}", path_a, e);
        }
    } else {
        if matches.free.len() != 1 {
            error!(
                "Invalid filename arguments (expected 1 filename, found {})",
                matches.free.len()
            );
            print_usage(&opts);
        }
        let path = &matches.free[0];

        if let Err(e) = ddbug::parse_file(path, &mut |file| print_file(file, &flags)) {
            error!("{}: {}", path, e);
        }
    }
}

fn diff_file(
    file_a: &mut ddbug::File,
    file_b: &mut ddbug::File,
    flags: &ddbug::Flags,
) -> ddbug::Result<()> {
    let stdout = std::io::stdout();
    let mut writer = stdout.lock();
    if let Err(e) = ddbug::diff_file(&mut writer, file_a, file_b, flags) {
        error!("{}", e);
    }
    Ok(())
}

fn print_file(file: &mut ddbug::File, flags: &ddbug::Flags) -> ddbug::Result<()> {
    let stdout = std::io::stdout();
    let mut writer = stdout.lock();
    ddbug::print_file(&mut writer, file, flags)
}

fn print_usage(opts: &getopts::Options) -> ! {
    let brief = format!("Usage: {} <options> <file>", env::args().next().unwrap());
    write!(&mut io::stderr(), "{}", opts.usage(&brief)).ok();
    std::process::exit(1);
}