use serde::Serialize;
use crate::cli::lifecycle::{self, ServiceStatusOutput};
use crate::cli::service::StatusArgs;
use crate::cli::{
service_discord::DiscordLifecycle, service_gcal::GcalLifecycle,
service_onepass::OnePassLifecycle, service_telegram::TelegramLifecycle,
service_ymusic::YmusicLifecycle,
};
use zad::error::Result;
#[derive(Debug, Serialize)]
struct AggregateOutput {
command: &'static str,
ok: bool,
services: Vec<ServiceStatusOutput>,
}
pub async fn run_all(args: StatusArgs) -> Result<()> {
let (onepass, discord, gcal, telegram, ymusic) = tokio::join!(
lifecycle::status_for::<OnePassLifecycle>(),
lifecycle::status_for::<DiscordLifecycle>(),
lifecycle::status_for::<GcalLifecycle>(),
lifecycle::status_for::<TelegramLifecycle>(),
lifecycle::status_for::<YmusicLifecycle>(),
);
let services = vec![onepass?, discord?, gcal?, telegram?, ymusic?];
let ok = services
.iter()
.filter(|s| s.effective.is_some())
.all(|s| s.ok);
if args.json {
let out = AggregateOutput {
command: "service.status",
ok,
services,
};
println!("{}", serde_json::to_string_pretty(&out).unwrap());
} else {
print_human(ok, &services);
}
if !ok {
std::process::exit(1);
}
Ok(())
}
fn print_human(ok: bool, services: &[ServiceStatusOutput]) {
println!(
"zad service status: {}",
if ok {
"all configured services ok"
} else {
"one or more services FAILED"
}
);
for svc in services {
let effective = svc.effective.unwrap_or("(not configured)");
let state = match svc.effective {
None => "not configured".to_string(),
Some(_) if svc.ok => {
let name = svc
.global
.check
.as_ref()
.or(svc.local.check.as_ref())
.and_then(|c| c.authenticated_as.as_deref())
.unwrap_or("(unknown)");
format!("ok (authenticated as `{name}`)")
}
Some(_) => {
let err = svc
.global
.check
.as_ref()
.or(svc.local.check.as_ref())
.and_then(|c| c.error.as_deref())
.unwrap_or("(no detail)");
format!("FAILED ({err})")
}
};
println!(" {:<10} [{effective:<5}] {state}", svc.service);
}
}