bot-forge 1.0.2

Rust CLI for installing agent skills and developer tools from configurable forms.
Documentation
use std::path::PathBuf;

use crate::backends::apt_mirror::{
    AptMirrorPreview, apply_apt_mirror, apt_mirror_preview, check_apt_mirror, restore_apt_mirror,
};
use crate::cli::actions::{confirm_action, print_human, value_after};
use crate::config::load_config_with_overlays;
use crate::ui::{self, Document, StatusKind};

pub(crate) fn cmd_apt_mirror(args: &[String]) -> Result<(), String> {
    let Some(action) = args.first().map(String::as_str) else {
        return Err(
            "apt-mirror requires the show, check, apply, or restore subcommand".to_string(),
        );
    };
    if !matches!(action, "show" | "check" | "apply" | "restore") {
        return Err(format!(
            "unknown apt-mirror subcommand: {action}; expected show, check, apply, or restore"
        ));
    }

    let mut config_path = None;
    let mut overlays = Vec::new();
    let mut yes = false;
    let mut index = 1;
    while index < args.len() {
        match args[index].as_str() {
            "-y" | "--yes" if matches!(action, "apply" | "restore") => yes = true,
            "--config" => {
                index += 1;
                config_path = Some(PathBuf::from(value_after(args, index, "--config")?));
            }
            "--overlay" => {
                index += 1;
                overlays.push(PathBuf::from(value_after(args, index, "--overlay")?));
            }
            value => return Err(format!("unknown apt-mirror option: {value}")),
        }
        index += 1;
    }

    let loaded = load_config_with_overlays(config_path.as_deref(), &overlays)
        .map_err(|error| error.to_string())?;
    let preview = apt_mirror_preview(loaded.document.apt_mirror.as_ref())
        .map_err(|error| error.to_string())?;
    if action == "restore" {
        print_apt_mirror_preview(&preview)?;
        if !yes && !confirm_action("Replace the APT source file with the bot-forge backup?")? {
            return print_human(
                Document::with_subtitle("bot-forge", "apt mirror")
                    .status(StatusKind::Info, "Restore cancelled"),
            );
        }
        restore_apt_mirror(&preview.source_file).map_err(|error| error.to_string())?;
        return print_human(
            Document::with_subtitle("bot-forge", "apt mirror")
                .status(StatusKind::Success, "Restored from backup")
                .labeled_path("Source", preview.source_file.display().to_string()),
        );
    }
    print_apt_mirror_preview(&preview)?;

    match action {
        "show" => Ok(()),
        "check" => {
            ui::stdout_status(
                StatusKind::Info,
                "Validating APT mirror in a temporary directory.",
            );
            check_apt_mirror(&preview, |message| {
                ui::stderr_status(StatusKind::Warning, message)
            })
            .map_err(|error| error.to_string())?;
            print_human(
                Document::with_subtitle("bot-forge", "apt mirror check")
                    .status(StatusKind::Success, "Validation passed"),
            )
        }
        "apply" => {
            ui::stdout_status(
                StatusKind::Info,
                "Validating APT mirror in a temporary directory.",
            );
            check_apt_mirror(&preview, |message| {
                ui::stderr_status(StatusKind::Warning, message)
            })
            .map_err(|error| error.to_string())?;
            ui::stdout_status(StatusKind::Success, "APT mirror validation passed.");
            if !yes
                && !confirm_action(
                    "Write the source file after validation? Existing system sources will not be removed or disabled.",
                )?
            {
                return print_human(
                    Document::with_subtitle("bot-forge", "apt mirror")
                        .status(StatusKind::Info, "Write cancelled"),
                );
            }
            apply_apt_mirror(&preview).map_err(|error| error.to_string())?;
            print_human(
                Document::with_subtitle("bot-forge", "apt mirror")
                    .status(StatusKind::Success, "Write complete")
                    .labeled_path("Source", preview.source_file.display().to_string()),
            )
        }
        _ => unreachable!(),
    }
}

fn print_apt_mirror_preview(preview: &AptMirrorPreview) -> Result<(), String> {
    print_human(
        Document::with_subtitle("bot-forge", "apt mirror")
            .field(
                "Platform",
                format!(
                    "{} {} ({})",
                    preview.distribution, preview.codename, preview.architecture
                ),
            )
            .labeled_path("Source", preview.source_file.display().to_string())
            .blank()
            .section("bot-forge.sources")
            .code(preview.source_contents.trim_end()),
    )
}