helix-toolbox 1.12.2

Build, configure and optimize a Linux machine from a terminal menu: installs, dotfiles, updates, backups and health checks, every change planned and confirmed first.
//! `helix-toolbox reliability` and `helix-toolbox trust`: how far entries have
//! been tested, and where an entry's installer and packages come from.

use anyhow::{Context, Result};
use helix_toolbox_catalog::Catalog;
use helix_toolbox_core::Entry;
use helix_toolbox_engine::{
    doctor, history,
    reliability::{Assessment, Evidence, Level},
    trust::{self, Build, Fact, Inspection},
    updates::Install,
};
pub use helix_toolbox_engine::{reliability::rows, trust::text};
use helix_toolbox_platform::LinuxInfo;
use serde_json::{Value, json};

/// This copy of Helix Toolbox, for the provenance of what it ships.
pub fn build() -> Build {
    Build {
        version: crate::VERSION.to_string(),
        release: option_env!("HELIX_TOOLBOX_VERSION").is_some(),
        commit: option_env!("HELIX_TOOLBOX_COMMIT").map(String::from),
        install: crate::this_places()
            .this
            .map(|this| this.install)
            .unwrap_or(Install::Other),
    }
}

/// Counts of each level, best first, for a summary line.
pub fn counts(rows: &[(&Entry, Assessment)]) -> Vec<(Level, usize)> {
    [
        Level::HardwareTested,
        Level::VmTested,
        Level::ContainerTested,
        Level::StandInTested,
        Level::NotYetValidated,
    ]
    .into_iter()
    .map(|level| {
        (
            level,
            rows.iter()
                .filter(|(_, assessment)| assessment.level == level)
                .count(),
        )
    })
    .filter(|(_, count)| *count > 0)
    .collect()
}

pub fn reliability(catalog: &Catalog, ids: &[String], critical: bool, as_json: bool) -> Result<()> {
    let evidence = Evidence::load(catalog.root());
    let records = history::read(1000).unwrap_or_default();
    let mut rows = rows(catalog, &evidence, &records);
    if !ids.is_empty() {
        for id in ids {
            crate::find(catalog, id)?;
        }
        rows.retain(|(entry, _)| ids.contains(&entry.id));
    }
    if critical {
        rows.retain(|(_, assessment)| assessment.critical);
    }
    if as_json {
        crate::json::print(
            "reliability",
            json!({
                "record": evidence.results.as_ref().map(|results| json!({
                    "run": results.run,
                    "url": results.url,
                    "commit": results.commit,
                    "finished": results.finished,
                    "distributions": results.images.keys().collect::<Vec<_>>(),
                })),
                "entries": rows.iter().map(|(entry, assessment)| json!({
                    "id": entry.id,
                    "name": entry.name,
                    "assessment": assessment_json(assessment),
                })).collect::<Vec<_>>(),
            }),
        );
        return Ok(());
    }
    println!("{}", evidence.summary());
    let summary: Vec<String> = counts(&rows)
        .into_iter()
        .map(|(level, count)| format!("{count} {}", level.label()))
        .collect();
    println!("{}\n", summary.join(", "));
    for (entry, assessment) in &rows {
        println!(
            "{:<18} {:<36} {:<11} {}{}",
            assessment.level.label(),
            entry.name,
            assessment.verified.as_deref().unwrap_or("-"),
            assessment.systems.join(", "),
            match (assessment.critical, assessment.here) {
                (true, Some(when)) => format!(
                    "  [boot, login or drivers; ran here {}]",
                    helix_toolbox_engine::date(when)
                ),
                (true, None) => "  [boot, login or drivers]".to_string(),
                (false, Some(when)) => format!("  [ran here {}]", helix_toolbox_engine::date(when)),
                (false, None) => String::new(),
            }
        );
    }
    Ok(())
}

pub fn assessment_json(assessment: &Assessment) -> Value {
    json!({
        "level": assessment.level,
        "label": assessment.level.label(),
        "systems": assessment.systems,
        "verified": assessment.verified,
        "evidence": assessment.evidence,
        "ran_here": assessment.here,
        "critical": assessment.critical,
    })
}

/// Inspect one entry on this machine.
pub fn inspection(catalog: &Catalog, platform: &LinuxInfo, entry: &Entry) -> Inspection {
    trust::inspect(
        entry,
        catalog.root(),
        platform,
        &build(),
        &doctor::Here,
        &Evidence::load(catalog.root()),
        &history::read(1000).unwrap_or_default(),
    )
}

pub fn trust(catalog: &Catalog, platform: &LinuxInfo, id: &str, as_json: bool) -> Result<()> {
    let entry = catalog
        .find(id)
        .with_context(|| format!("no entry named '{id}'; see helix-toolbox list"))?;
    let found = inspection(catalog, platform, entry);
    if as_json {
        let facts = |facts: &[Fact]| {
            facts
                .iter()
                .map(|fact| {
                    json!({
                        "standing": fact.standing,
                        "label": fact.standing.label(),
                        "subject": fact.subject,
                        "text": fact.text,
                    })
                })
                .collect::<Vec<_>>()
        };
        crate::json::print(
            "trust",
            json!({
                "id": found.id,
                "name": found.name,
                "kind": found.kind,
                "origin": found.origin,
                "file": found.file,
                "sha256": found.sha256,
                "installer_license": found.installer_license,
                "package_license": found.package_license,
                "provenance": facts(&found.provenance),
                "integrity": facts(&found.integrity),
                "downloads": facts(&found.downloads),
                "privileges": found.privileges,
                "validation": assessment_json(&found.validation),
            }),
        );
        return Ok(());
    }
    print!("{}", text(&found));
    Ok(())
}