adnv 1.5.0

CLI that tracks, checks and auto-updates infrastructure dependency versions across Terraform, Helm, Docker, Git and npm
Documentation
//! Table rendering shared by `outdated` and `update`.

use comfy_table::{Cell, Table};

use crate::sources::resolve::ResolvedSource;
use crate::sources::{VersionedSource, print_errors};
use crate::{Results, error::ErrorWithData};

/// The table both operations print, differing only in their columns and rows.
fn new_table(header: Vec<&str>) -> Table {
    use comfy_table::{ColumnConstraint, ContentArrangement, Width, presets::UTF8_FULL};

    let mut table = Table::new();
    table
        .load_style(UTF8_FULL)
        .set_content_arrangement(ContentArrangement::DynamicFullWidth)
        .set_width(100)
        .set_header(header);

    if let Some(column) = table.column_mut(0) {
        column.set_constraint(ColumnConstraint::UpperBoundary(Width::Fixed(30)));
    }

    table
}

/// Groups the resolved sources by kind, prints one table per group, then the
/// errors. `columns` names the columns after the first, which holds the kind.
pub(crate) fn display_results<F>(
    results: Results<ResolvedSource, ErrorWithData<VersionedSource>>,
    columns: &[&str],
    row: F,
) where
    F: Fn(&ResolvedSource) -> Vec<Cell>,
{
    use itertools::Itertools;

    let (sources, errors) = results.split();

    let groups = sources
        .into_iter()
        .into_group_map_by(|source| source.source.source.to_string())
        .into_iter()
        .sorted_unstable_by_key(|(kind, _)| kind.clone());

    for (kind, mut sources) in groups {
        let mut header = vec![kind.as_str()];
        header.extend_from_slice(columns);

        let mut table = new_table(header);

        // Sources with an available update first.
        sources.sort_by_key(|source| std::cmp::Reverse(source.wanted.is_some()));

        for source in &sources {
            table.add_row(row(source));
        }

        println!("{table}");
    }

    print_errors(errors);
}

/// The columns both operations share, in order.
pub(crate) fn common_cells(source: &ResolvedSource) -> Vec<Cell> {
    let current = source.source.current.as_deref().unwrap_or("N/A");
    let wanted = source
        .wanted
        .as_deref()
        .map_or_else(|| "N/A".to_owned(), ToOwned::to_owned);

    vec![
        Cell::new(source.source.source.describe()),
        Cell::new(current),
        Cell::new(wanted),
        Cell::new(source.latest.clone()),
        // Only parsed sources carry a location.
        Cell::new(
            source
                .source
                .source
                .location()
                .map_or_else(|| "-".to_owned(), ToString::to_string),
        ),
    ]
}