fn main()

in src/main.rs [312:596]


fn main() {
    let opt = Opt::parse();

    if let Some(path) = opt.llvm_path {
        LLVM_PATH.set(path).unwrap();
    }

    let filter_option = opt.filter.map(|filter| match filter {
        Filter::Covered => true,
        Filter::Uncovered => false,
    });
    let stdout = Path::new("stdout");
    let stderr = Path::new("stderr");

    if opt.log == stdout {
        let _ = TermLogger::init(
            opt.log_level.0,
            Config::default(),
            TerminalMode::Stdout,
            ColorChoice::Auto,
        );
    } else if opt.log == stderr {
        let _ = TermLogger::init(
            opt.log_level.0,
            Config::default(),
            TerminalMode::Stderr,
            ColorChoice::Auto,
        );
    } else if let Ok(file) = File::create(&opt.log) {
        let _ = WriteLogger::init(opt.log_level.0, Config::default(), file);
    } else {
        let _ = TermLogger::init(
            opt.log_level.0,
            Config::default(),
            TerminalMode::Stderr,
            ColorChoice::Auto,
        );
        error!(
            "Unable to create log file: {}. Switch to stderr",
            opt.log.display()
        );
    }

    let file_filter = FileFilter::new(
        opt.excl_line,
        opt.excl_start,
        opt.excl_stop,
        opt.excl_br_line,
        opt.excl_br_start,
        opt.excl_br_stop,
    );
    let demangle = !opt.no_demangle;

    panic::set_hook(Box::new(|panic_info| {
        let (filename, line) = panic_info
            .location()
            .map(|loc| (loc.file(), loc.line()))
            .unwrap_or(("<unknown>", 0));
        let cause = panic_info
            .payload()
            .downcast_ref::<String>()
            .map(String::deref);
        let cause = cause.unwrap_or_else(|| {
            panic_info
                .payload()
                .downcast_ref::<&str>()
                .copied()
                .unwrap_or("<cause unknown>")
        });
        error!("A panic occurred at {}:{}: {}", filename, line, cause);
    }));

    let num_threads: usize = opt.threads.unwrap_or_else(|| 1.max(num_cpus::get() - 1));
    let source_root = opt
        .source_dir
        .filter(|source_dir| source_dir != Path::new(""))
        .map(|source_dir| canonicalize_path(source_dir).expect("Source directory does not exist."));

    let prefix_dir = opt.prefix_dir.or_else(|| source_root.clone());

    let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
    let tmp_path = tmp_dir.path().to_owned();
    assert!(tmp_path.exists());

    let result_map: Arc<SyncCovResultMap> = Arc::new(Mutex::new(
        FxHashMap::with_capacity_and_hasher(20_000, Default::default()),
    ));
    let (sender, receiver) = bounded(2 * num_threads);
    let path_mapping: Arc<Mutex<Option<Value>>> = Arc::new(Mutex::new(None));

    let producer = {
        let sender: JobSender = sender.clone();
        let tmp_path = tmp_path.clone();
        let path_mapping_file = opt.path_mapping;
        let path_mapping = Arc::clone(&path_mapping);
        let paths = opt.paths;
        let is_llvm = opt.llvm;

        thread::Builder::new()
            .name(String::from("Producer"))
            .spawn(move || {
                let producer_path_mapping_buf = producer(
                    &tmp_path,
                    &paths,
                    &sender,
                    filter_option.is_some() && filter_option.unwrap(),
                    is_llvm,
                );

                let mut path_mapping = path_mapping.lock().unwrap();
                *path_mapping = if let Some(path) = path_mapping_file {
                    let file = File::open(path).unwrap();
                    Some(serde_json::from_reader(file).unwrap())
                } else {
                    producer_path_mapping_buf.map(|producer_path_mapping_buf| {
                        serde_json::from_slice(&producer_path_mapping_buf).unwrap()
                    })
                };
            })
            .unwrap()
    };

    let mut parsers = Vec::new();

    for i in 0..num_threads {
        let receiver = receiver.clone();
        let result_map = Arc::clone(&result_map);
        let working_dir = tmp_path.join(format!("{}", i));
        let source_root = source_root.clone();
        let binary_path = opt.binary_path.clone();
        let branch_enabled = opt.branch;
        let guess_directory = opt.guess_directory;

        let t = thread::Builder::new()
            .name(format!("Consumer {}", i))
            .spawn(move || {
                fs::create_dir(&working_dir).expect("Failed to create working directory");
                consumer(
                    &working_dir,
                    source_root.as_deref(),
                    &result_map,
                    receiver,
                    branch_enabled,
                    guess_directory,
                    binary_path.as_deref(),
                );
            })
            .unwrap();

        parsers.push(t);
    }

    if producer.join().is_err() {
        process::exit(1);
    }

    // Poison the receiver, now that the producer is finished.
    for _ in 0..num_threads {
        sender.send(None).unwrap();
    }

    for parser in parsers {
        if parser.join().is_err() {
            process::exit(1);
        }
    }

    let result_map_mutex = Arc::try_unwrap(result_map).unwrap();
    let result_map = result_map_mutex.into_inner().unwrap();

    let path_mapping_mutex = Arc::try_unwrap(path_mapping).unwrap();
    let path_mapping = path_mapping_mutex.into_inner().unwrap();

    let iterator = rewrite_paths(
        result_map,
        path_mapping,
        source_root.as_deref(),
        prefix_dir.as_deref(),
        opt.ignore_not_existing,
        &opt.ignore_dir,
        &opt.keep_dir,
        filter_option,
        file_filter,
    );
    let mut sorted_iterator: Option<Vec<ResultTuple>> = None;

    let service_number = opt.service_number.unwrap_or_default();
    let service_pull_request = opt.service_pull_request.unwrap_or_default();
    let commit_sha = opt.commit_sha.unwrap_or_default();

    let output_types = opt.output_types;

    let output_path = match output_types.len() {
        0 => unreachable!("Output types has a default value"),
        1 => opt.output_path.as_deref(),
        _ => match opt.output_path.as_deref() {
            Some(output_path) => {
                if output_path.is_dir() {
                    Some(output_path)
                } else {
                    panic!("output_path must be a directory when using multiple outputs");
                }
            }
            _ => None,
        },
    };

    for output_type in &output_types {
        let output_path = output_type.to_file_name(output_path);
        let results = if opt.sort_output_types.contains(output_type) {
            // compute and cache the sorted results if not already used
            sorted_iterator = sorted_iterator.or_else(|| {
                let mut results = iterator.clone();
                results.sort_by_key(|result| result.0.display().to_string());
                Some(results)
            });
            sorted_iterator.as_ref().unwrap()
        } else {
            &iterator
        };

        match output_type {
            OutputType::Ade => output_activedata_etl(results, output_path.as_deref(), demangle),
            OutputType::Lcov => output_lcov(results, output_path.as_deref(), demangle),
            OutputType::Coveralls => output_coveralls(
                results,
                opt.token.as_deref(),
                opt.service_name.as_deref(),
                &service_number,
                opt.service_job_id.as_deref(),
                &service_pull_request,
                opt.service_flag_name.as_deref(),
                &commit_sha,
                false,
                output_path.as_deref(),
                &opt.vcs_branch,
                opt.parallel,
                demangle,
            ),
            OutputType::CoverallsPlus => output_coveralls(
                results,
                opt.token.as_deref(),
                opt.service_name.as_deref(),
                &service_number,
                opt.service_job_id.as_deref(),
                &service_pull_request,
                opt.service_flag_name.as_deref(),
                &commit_sha,
                true,
                output_path.as_deref(),
                &opt.vcs_branch,
                opt.parallel,
                demangle,
            ),
            OutputType::Files => output_files(results, output_path.as_deref()),
            OutputType::Covdir => output_covdir(results, output_path.as_deref(), opt.precision),
            OutputType::Html => output_html(
                results,
                output_path.as_deref(),
                num_threads,
                opt.branch,
                opt.output_config_file.as_deref(),
                opt.precision,
                &opt.abs_link_prefix.clone(),
                opt.no_date,
                opt.html_resources,
            ),
            OutputType::Cobertura => output_cobertura(
                source_root.as_deref(),
                results,
                output_path.as_deref(),
                demangle,
                false,
            ),
            OutputType::CoberturaPretty => output_cobertura(
                source_root.as_deref(),
                results,
                output_path.as_deref(),
                demangle,
                true,
            ),
            OutputType::Markdown => output_markdown(results, output_path.as_deref(), opt.precision),
        };
    }
}