svccat 0.7.0

Detect drift between your declared service catalog and what actually lives in the repo.
Documentation
use crate::drift::{DriftItem, DriftKind, DriftReport, Severity};
use crate::ping::{PingResult, PingStatus};
use colored::Colorize;

pub fn render_check(report: &DriftReport, ping_results: &[PingResult]) {
    let errors = report.error_count();
    let warnings = report.warning_count();

    println!(
        "{}",
        format!(
            "svccat: {} declared, {} discovered  [{}]",
            report.declared, report.discovered, report.manifest
        )
        .bold()
    );
    println!();

    if report.drifts.is_empty() {
        println!("{}", "  OK  No drift detected".green().bold());
    } else {
        println!(
            "{}",
            format!(
                "  DRIFT DETECTED  ({} error{}, {} warning{})",
                errors,
                if errors == 1 { "" } else { "s" },
                warnings,
                if warnings == 1 { "" } else { "s" }
            )
            .yellow()
            .bold()
        );
        println!();

        for item in &report.drifts {
            print_drift_item(item);
        }

        println!();

        if errors > 0 {
            println!("{}", format!("  x  {} error(s)", errors).red().bold());
        }
        if warnings > 0 {
            println!("{}", format!("  !  {} warning(s)", warnings).yellow());
        }
    }

    if !ping_results.is_empty() {
        println!();
        render_ping_results(ping_results);
    }
}

pub fn render_ping_results(results: &[PingResult]) {
    println!("{}", "  PING RESULTS".bold());
    println!();
    for r in results {
        match &r.ping {
            PingStatus::Reachable { code } if *code < 400 => {
                println!(
                    "  {}  {}  {} ({})",
                    "".green().bold(),
                    "[PING]".green(),
                    r.service,
                    code
                );
            }
            PingStatus::Reachable { code } => {
                println!(
                    "  {}  {}  {}  {} ({})",
                    "!".yellow().bold(),
                    "[PING]".yellow(),
                    r.service,
                    r.url,
                    code
                );
            }
            PingStatus::Unreachable { reason } => {
                println!(
                    "  {}  {}  {}  {}{}",
                    "x".red().bold(),
                    "[PING]".red(),
                    r.service,
                    r.url,
                    reason
                );
            }
        }
    }
}

fn print_drift_item(item: &DriftItem) {
    let icon = match item.severity {
        Severity::Error => "x".red().bold(),
        Severity::Warning => "!".yellow().bold(),
    };

    let kind_label = match item.kind {
        DriftKind::DeclaredMissingFromRepo => "[MISSING]   ".red(),
        DriftKind::UndeclaredInRepo => "[UNDECLARED]".yellow(),
        DriftKind::MissingField => "[FIELD]     ".cyan(),
        DriftKind::MissingReferencedFile => "[REF]       ".yellow(),
        DriftKind::PolicyViolation => "[POLICY]    ".red(),
        DriftKind::DanglingDependency => "[DEPENDS]   ".red(),
        DriftKind::CircularDependency => "[CYCLE]     ".red(),
    };

    println!("  {}  {}  {}", icon, kind_label, item.message);
}