use std::collections::BTreeSet;
use anyhow::Result;
use console::style;
use ryra_core::{DiffKind, DiffResult};
pub async fn run(services: &[String]) -> Result<()> {
let targets = resolve_targets(services)?;
if targets.is_empty() {
println!("No services installed.");
return Ok(());
}
let mut any_drift = false;
let mut any_change = false;
for service in &targets {
let diff = ryra_core::diff_service(service).await?;
print_one(&diff);
if !diff.drifted().is_empty() {
any_drift = true;
}
if !diff.is_clean() {
any_change = true;
}
}
if !any_change {
println!("Everything up to date.");
} else if any_drift {
println!();
println!(
"{} hand-edited files would block `ryra upgrade` — re-run with --force to overwrite, or back up your changes first.",
style("!").red().bold()
);
} else {
println!();
println!("Run `ryra upgrade` to apply.");
}
Ok(())
}
fn print_one(diff: &DiffResult) {
let header = if diff.is_clean() {
format!("{} (clean)", style(&diff.service).bold())
} else {
style(&diff.service).bold().to_string()
};
println!("{header}");
if diff.is_clean() {
return;
}
for entry in &diff.entries {
match entry.kind {
DiffKind::Unchanged => {}
DiffKind::Added => println!(
" {} {} {}",
style("+").green().bold(),
entry.path.display(),
style("added").green()
),
DiffKind::Modified => println!(
" {} {} {}",
style("~").yellow(),
entry.path.display(),
style("modified").yellow()
),
DiffKind::Removed => println!(
" {} {} {}",
style("-").red(),
entry.path.display(),
style("removed").red()
),
DiffKind::Drift => println!(
" {} {} {}",
style("!").red().bold(),
entry.path.display(),
style("drift (hand-edited)").red().bold()
),
}
}
for add in &diff.env_additions {
println!(
" {} env: {}={} {}",
style("+").green().bold(),
add.key,
add.value,
style("registry-added env var").green()
);
}
}
fn resolve_targets(services: &[String]) -> Result<Vec<String>> {
if !services.is_empty() {
for s in services {
if !ryra_core::is_service_installed(s) {
anyhow::bail!("service '{s}' is not installed");
}
}
let mut out: Vec<String> = services.to_vec();
let mut seen = BTreeSet::new();
out.retain(|s| seen.insert(s.clone()));
return Ok(out);
}
let installed = ryra_core::list_installed()?;
Ok(installed.into_iter().map(|s| s.name).collect())
}