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()),
)
}