paru 0.99.2

Aur helper and pacman wrapper
use crate::config::{Colors, Config};
use crate::download::cache_info_with_warnings;
use crate::fmt::{date, opt, print_indent};
use crate::util::split_repo_aur_targets;
use crate::{esprintln, exec};
use crate::{sprint, sprintln};

use alpm_utils::Targ;
use ansi_term::Style;
use anyhow::Error;
use raur_ext::Package;
use term_size::dimensions_stdout;

pub fn info(conf: &mut Config, verbose: bool) -> Result<i32, Error> {
    let targets = conf.targets.clone();
    let targets = targets.iter().map(Targ::from).collect::<Vec<_>>();

    let (repo, aur) = split_repo_aur_targets(conf, &targets);
    let mut ret = 0;

    let aur = if !aur.is_empty() {
        let color = conf.color;
        let aur = aur.iter().map(|t| t.pkg).collect::<Vec<_>>();
        let warnings =
            cache_info_with_warnings(&conf.raur, &mut conf.cache, &aur, &conf.pacman.ignore_pkg)?;
        for pkg in &warnings.missing {
            esprintln!(
                "{} package '{}' was not found",
                color.error.paint("error:"),
                pkg,
            );
        }
        ret = !warnings.missing.is_empty() as i32;
        warnings.pkgs
    } else {
        Vec::new()
    };

    if !repo.is_empty() {
        let targets = repo.into_iter().map(|t| t.to_string()).collect::<Vec<_>>();
        let mut args = conf.pacman_args();
        args.targets.clear();
        args.targets(targets.iter().map(|t| t.as_str()));
        ret |= exec::pacman(conf, &args)?.code();
    }

    if !aur.is_empty() {
        print_aur_info(conf, verbose, &aur)?;
    }

    Ok(ret)
}

pub fn print_aur_info(conf: &mut Config, verbose: bool, pkgs: &[Package]) -> Result<(), Error> {
    let color = conf.color;
    let cols = dimensions_stdout().map(|x| x.0);
    let print = |k: &str, v: &str| print(color, 18, cols, k, v);
    let print_list = |k: &str, v: &[_]| print_list(color, 18, cols, k, v);

    for pkg in pkgs {
        print("Repository", "aur");
        print("Name", &pkg.name);
        print("Description", opt(&pkg.description));
        print("URL", opt(&pkg.url));
        print(
            "AUR URL",
            conf.aur_url
                .join(&format!("packages/{}", pkg.package_base))?
                .as_str(),
        );
        print_list("Groups", &pkg.groups);
        print_list("Licenses", &pkg.license);
        print_list("Provides", &pkg.provides);
        print_list("Depends On", &pkg.depends);
        print_list("Make Deps", &pkg.make_depends);
        print_list("Check Deps", &pkg.check_depends);
        print_list("Optional Deps", &pkg.opt_depends);
        print_list("Conflicts With", &pkg.conflicts);
        print("Maintainer", opt(&pkg.maintainer));
        print("Votes", &pkg.num_votes.to_string());
        print("Popularity", &pkg.popularity.to_string());
        print("First Submitted", &date(pkg.first_submitted));
        print("Last Modified", &date(pkg.last_modified));
        print(
            "Out Of Date",
            pkg.out_of_date
                .map(date)
                .as_ref()
                .map(String::as_ref)
                .unwrap_or("No"),
        );

        if verbose {
            print("ID", &pkg.id.to_string());
            print("Package Base ID", &pkg.package_base_id.to_string());
            print_list("Keywords", &pkg.keywords);
            print("Snapshot URL", conf.aur_url.join(&pkg.url_path)?.as_str());
        }

        sprintln!();
    }

    Ok(())
}

fn print(color: Colors, indent: usize, cols: Option<usize>, k: &str, v: &str) {
    print_info(color, false, indent, cols, k, v.split_whitespace());
}

fn print_list(color: Colors, indent: usize, cols: Option<usize>, k: &str, v: &[String]) {
    if v.is_empty() {
        print(color, indent, cols, k, "None");
    } else {
        print_info(color, true, indent, cols, k, v.iter().map(|s| s.as_str()));
    }
}

fn print_info<'a>(
    color: Colors,
    list: bool,
    indent: usize,
    cols: Option<usize>,
    key: &str,
    value: impl IntoIterator<Item = &'a str>,
) {
    let prefix = format!("{:<padding$}: ", key, padding = indent - 2);
    sprint!("{}", color.field.paint(&prefix));

    let sep = if list { "  " } else { " " };
    print_indent(Style::new(), prefix.len(), prefix.len(), cols, sep, value)
}