please-install 1.2.0

A unified interface package manager for many OSes
Documentation
use std::collections::BTreeMap;

use eyre::{eyre, Result};

use crate::package::Package;
use crate::track::load_tracks;
use crate::pls_command::PlsCommand;
use crate::run_command::run_command;
use crate::vendor_data::VendorData;
use crate::vendors::Vendor;

pub fn reinstall_all(
    vendor: Option<Vendor>,
    yes: bool,
    su: bool,
    dry_run: bool,
    pager: &Option<String>,
    pls_command: &PlsCommand,
) -> Result<i32> {
    let tracks = tracks_by_vendor()?;
    let mut status = 0;

    for (v, packages) in tracks {
        if let Some(vendor) = vendor {
            if vendor != v {
                continue;
            }
        }
        let vendor_data: VendorData = v.try_into()?;
        let command = PlsCommand::ReinstallAll.format(
            vendor_data,
            &packages,
            yes,
            pager,
        );
        if command.is_empty() {
            continue;
        }

        if dry_run {
            println!("{}", command);
            continue;
        }

        let mut current = run_command(&command, su, &pager, pls_command)?;
        if current != 0 {
            current = rerun_reinstall_all_on_failure(vendor_data, yes, &pager, su, pls_command);
        }

        if current != 0 {
            status = current;
        }
    }

    Ok(status)
}

fn tracks_by_vendor() -> Result<BTreeMap<Vendor, Vec<Package>>> {
    let mut res = BTreeMap::new();
    let tracks = load_tracks()?;
    if tracks.is_empty() {
        return Err(eyre!("no known installed packages"));
    }
    for track in tracks {
        res
            .entry(track.vendor)
            .or_insert(vec![])
            .push(track.clone());
    }
    for (_, packages) in res.iter_mut() {
        packages.sort();
    }
    Ok(res)
}

fn rerun_reinstall_all_on_failure(
    vendor_data: VendorData,
    yes: bool,
    pager: &Option<String>,
    su: bool,
    pls_command: &PlsCommand,
) -> i32 {
    let tracks = load_tracks().unwrap_or_default();
    let mut response = 0;
    for track in tracks {
        let command = PlsCommand::Install.format(vendor_data, &vec![track], yes, pager);
        match run_command(&command, su, pager, pls_command) {
            Ok(status) => {
                if status != 0 {
                    response = status;
                }
            },
            Err(err) => {
                eprintln!("error running command: {}", err);
                response = 1;
            }
        }
    }
    response
}