polonius 0.3.0

Core definition for the Rust borrow checker
use crate::dump;
use crate::facts::{Loan, Point, Region};
use crate::intern;
use crate::tab_delim;
use failure::Error;
use polonius_engine::{Algorithm, AllFacts, Output};
use std::path::Path;
use std::time::{Duration, Instant};
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "borrow-check")]
pub struct Opt {
    #[structopt(
        short = "a",
        env = "POLONIUS_ALGORITHM",
        default_value = "naive",
        raw(possible_values = "&Algorithm::variants()", case_insensitive = "true")
    )]
    algorithm: Algorithm,
    #[structopt(long = "show-tuples", help = "Show output tuples on stdout")]
    show_tuples: bool,
    #[structopt(long = "skip-timing", help = "Do not display timing results")]
    skip_timing: bool,
    #[structopt(
        short = "v",
        long = "verbose",
        help = "Show intermediate output tuples and not just errors"
    )]
    verbose: bool,
    #[structopt(
        long = "graphviz_file",
        help = "Generate a graphviz file to visualize the computation"
    )]
    graphviz_file: Option<String>,
    #[structopt(
        short = "o",
        long = "output",
        help = "Directory where to output resulting tuples"
    )]
    output_directory: Option<String>,
    #[structopt(raw(required = "true"))]
    fact_dirs: Vec<String>,
}

macro_rules! attempt {
    ($($tokens:tt)*) => {
        (|| Ok({ $($tokens)* }))()
    };
}

pub fn main(opt: Opt) -> Result<(), Error> {
    let output_directory = opt
        .output_directory
        .as_ref()
        .map(|x| Path::new(x).to_owned());
    let graphviz_file = opt.graphviz_file.as_ref().map(|x| Path::new(x).to_owned());
    for facts_dir in &opt.fact_dirs {
        let tables = &mut intern::InternerTables::new();

        let result: Result<
            (
                Duration,
                AllFacts<Region, Loan, Point>,
                Output<Region, Loan, Point>,
            ),
            Error,
        > = attempt! {
            let verbose = opt.verbose;
            let all_facts =
                tab_delim::load_tab_delimited_facts(tables, &Path::new(&facts_dir))?;
            let algorithm = opt.algorithm;
            let graphviz_output = graphviz_file.is_some();
            let (duration, output) =
                timed(|| Output::compute(&all_facts, algorithm, verbose || graphviz_output));
            (duration, all_facts, output)
        };

        match result {
            Ok((duration, all_facts, output)) => {
                println!("--------------------------------------------------");
                println!("Directory: {}", facts_dir);
                if !opt.skip_timing {
                    let seconds = duration.as_secs() as f64;
                    let millis = f64::from(duration.subsec_nanos()) * 0.000_000_001_f64;
                    println!("Time: {:0.3}s", seconds + millis);
                }
                if opt.show_tuples {
                    dump::dump_output(&output, &output_directory, tables)
                        .expect("Failed to write output");
                }
                if let Some(ref graphviz_file) = graphviz_file {
                    dump::graphviz(&output, &all_facts, graphviz_file, tables)
                        .expect("Failed to write GraphViz");
                }
            }

            Err(error) => {
                eprintln!("`{}`: {}", facts_dir, error);
            }
        }
    }

    Ok(())
}

fn timed<T>(op: impl FnOnce() -> T) -> (Duration, T) {
    let start = Instant::now();
    let output = op();
    let duration = start.elapsed();
    (duration, output)
}