zad_cli/cli/
service_status.rs1use serde::Serialize;
15
16use crate::cli::lifecycle::{self, ServiceStatusOutput};
17use crate::cli::service::StatusArgs;
18use crate::cli::{
19 service_discord::DiscordLifecycle, service_gcal::GcalLifecycle,
20 service_onepass::OnePassLifecycle, service_telegram::TelegramLifecycle,
21 service_ymusic::YmusicLifecycle,
22};
23use zad::error::Result;
24
25#[derive(Debug, Serialize)]
26struct AggregateOutput {
27 command: &'static str,
28 ok: bool,
33 services: Vec<ServiceStatusOutput>,
34}
35
36pub async fn run_all(args: StatusArgs) -> Result<()> {
37 let (onepass, discord, gcal, telegram, ymusic) = tokio::join!(
42 lifecycle::status_for::<OnePassLifecycle>(),
43 lifecycle::status_for::<DiscordLifecycle>(),
44 lifecycle::status_for::<GcalLifecycle>(),
45 lifecycle::status_for::<TelegramLifecycle>(),
46 lifecycle::status_for::<YmusicLifecycle>(),
47 );
48 let services = vec![onepass?, discord?, gcal?, telegram?, ymusic?];
49
50 let ok = services
51 .iter()
52 .filter(|s| s.effective.is_some())
53 .all(|s| s.ok);
54
55 if args.json {
56 let out = AggregateOutput {
57 command: "service.status",
58 ok,
59 services,
60 };
61 println!("{}", serde_json::to_string_pretty(&out).unwrap());
62 } else {
63 print_human(ok, &services);
64 }
65
66 if !ok {
67 std::process::exit(1);
68 }
69 Ok(())
70}
71
72fn print_human(ok: bool, services: &[ServiceStatusOutput]) {
73 println!(
74 "zad service status: {}",
75 if ok {
76 "all configured services ok"
77 } else {
78 "one or more services FAILED"
79 }
80 );
81 for svc in services {
82 let effective = svc.effective.unwrap_or("(not configured)");
83 let state = match svc.effective {
84 None => "not configured".to_string(),
85 Some(_) if svc.ok => {
86 let name = svc
87 .global
88 .check
89 .as_ref()
90 .or(svc.local.check.as_ref())
91 .and_then(|c| c.authenticated_as.as_deref())
92 .unwrap_or("(unknown)");
93 format!("ok (authenticated as `{name}`)")
94 }
95 Some(_) => {
96 let err = svc
97 .global
98 .check
99 .as_ref()
100 .or(svc.local.check.as_ref())
101 .and_then(|c| c.error.as_deref())
102 .unwrap_or("(no detail)");
103 format!("FAILED ({err})")
104 }
105 };
106 println!(" {:<10} [{effective:<5}] {state}", svc.service);
107 }
108}