1use async_trait::async_trait;
9use linkme::distributed_slice;
10use rtb_app::app::App;
11use rtb_app::command::{Command, CommandSpec, BUILTIN_COMMANDS};
12use rtb_app::features::Feature;
13
14use crate::health;
15use crate::init::INITIALISERS;
16
17pub struct VersionCmd;
23
24#[async_trait]
25impl Command for VersionCmd {
26 fn spec(&self) -> &CommandSpec {
27 static SPEC: CommandSpec = CommandSpec {
28 name: "version",
29 about: "Print tool version information",
30 feature: Some(Feature::Version),
31 ..CommandSpec::DEFAULT
32 };
33 &SPEC
34 }
35
36 async fn run(&self, app: App) -> miette::Result<()> {
37 let v = &app.version;
38 println!("{} {}", app.metadata.name, v.version);
39 if let Some(commit) = v.commit.as_deref() {
40 println!(" commit: {commit}");
41 }
42 if let Some(date) = v.date.as_deref() {
43 println!(" built: {date}");
44 }
45 println!(" target: {}-{}", std::env::consts::ARCH, std::env::consts::OS);
46 Ok(())
47 }
48}
49
50#[distributed_slice(BUILTIN_COMMANDS)]
51fn __register_version() -> Box<dyn Command> {
52 Box::new(VersionCmd)
53}
54
55pub struct DoctorCmd;
61
62#[async_trait]
63impl Command for DoctorCmd {
64 fn spec(&self) -> &CommandSpec {
65 static SPEC: CommandSpec = CommandSpec {
66 name: "doctor",
67 about: "Run diagnostic health checks",
68 feature: Some(Feature::Doctor),
69 ..CommandSpec::DEFAULT
70 };
71 &SPEC
72 }
73
74 async fn run(&self, app: App) -> miette::Result<()> {
75 let report = health::run_all(&app).await;
76 print!("{}", report.render());
77 if report.is_ok() {
78 Ok(())
79 } else {
80 Err(miette::miette!(
81 code = "rtb::doctor::failed",
82 help = "see the report above for which checks failed",
83 "one or more health checks failed"
84 ))
85 }
86 }
87}
88
89#[distributed_slice(BUILTIN_COMMANDS)]
90fn __register_doctor() -> Box<dyn Command> {
91 Box::new(DoctorCmd)
92}
93
94pub struct InitCmd;
100
101#[async_trait]
102impl Command for InitCmd {
103 fn spec(&self) -> &CommandSpec {
104 static SPEC: CommandSpec = CommandSpec {
105 name: "init",
106 about: "Run first-time bootstrap and setup",
107 feature: Some(Feature::Init),
108 ..CommandSpec::DEFAULT
109 };
110 &SPEC
111 }
112
113 async fn run(&self, app: App) -> miette::Result<()> {
114 if INITIALISERS.is_empty() {
115 println!("no initialisers registered — nothing to do");
116 return Ok(());
117 }
118 for factory in INITIALISERS {
119 let init = factory();
120 if init.is_configured(&app).await {
121 println!(" [SKIP] {} — already configured", init.name());
122 continue;
123 }
124 println!(" [RUN] {}", init.name());
125 init.configure(&app).await?;
126 }
127 Ok(())
128 }
129}
130
131#[distributed_slice(BUILTIN_COMMANDS)]
132fn __register_init() -> Box<dyn Command> {
133 Box::new(InitCmd)
134}
135
136