rtb_cli/init.rs
1//! Initialisers — the `init` subcommand's plug-in point.
2
3use async_trait::async_trait;
4use linkme::distributed_slice;
5use rtb_app::app::App;
6
7/// A pluggable bootstrap step run by the `init` subcommand.
8///
9/// Initialisers typically prompt for configuration values, write a
10/// user config file, set up OS keychain entries, generate SSH keys,
11/// etc. The `init` command iterates every registered initialiser in
12/// registration order, skipping any that report `is_configured ==
13/// true` unless the user passes `--force`.
14#[async_trait]
15pub trait Initialiser: Send + Sync + 'static {
16 /// Short identifier shown in `init` output.
17 fn name(&self) -> &'static str;
18
19 /// Returns `true` if this initialiser's prerequisites are already
20 /// met — e.g. the relevant config key is present.
21 async fn is_configured(&self, app: &App) -> bool;
22
23 /// Perform the bootstrap. Typically interactive.
24 async fn configure(&self, app: &App) -> miette::Result<()>;
25}
26
27/// Link-time registry of initialiser factories.
28///
29/// ```no_run
30/// use rtb_app::app::App;
31/// use rtb_app::linkme::distributed_slice;
32/// use rtb_cli::init::{Initialiser, INITIALISERS};
33///
34/// struct MyInitialiser;
35///
36/// #[async_trait::async_trait]
37/// impl Initialiser for MyInitialiser {
38/// fn name(&self) -> &'static str {
39/// "my-initialiser"
40/// }
41///
42/// async fn is_configured(&self, _app: &App) -> bool {
43/// false
44/// }
45///
46/// async fn configure(&self, _app: &App) -> miette::Result<()> {
47/// Ok(())
48/// }
49/// }
50///
51/// #[distributed_slice(INITIALISERS)]
52/// fn register() -> Box<dyn Initialiser> { Box::new(MyInitialiser) }
53/// ```
54#[distributed_slice]
55pub static INITIALISERS: [fn() -> Box<dyn Initialiser>];