Skip to main content

soroban_cli/commands/network/
health.rs

1use crate::commands::global;
2use crate::config::{self, network};
3use crate::output::{Format, Output};
4
5#[derive(thiserror::Error, Debug)]
6pub enum Error {
7    #[error(transparent)]
8    Config(#[from] config::Error),
9    #[error(transparent)]
10    Network(#[from] network::Error),
11    #[error(transparent)]
12    Rpc(#[from] crate::rpc::Error),
13    #[error(transparent)]
14    Serde(#[from] serde_json::Error),
15}
16
17#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum, Default)]
18pub enum OutputFormat {
19    /// Text output of network health status
20    #[default]
21    Text,
22    /// JSON result of the RPC request
23    Json,
24    /// Formatted (multiline) JSON output of the RPC request
25    JsonFormatted,
26}
27
28impl From<OutputFormat> for Format {
29    fn from(value: OutputFormat) -> Self {
30        match value {
31            OutputFormat::Text => Format::Readable,
32            OutputFormat::Json => Format::Json,
33            OutputFormat::JsonFormatted => Format::JsonFormatted,
34        }
35    }
36}
37
38#[derive(Debug, clap::Parser, Clone)]
39#[group(skip)]
40pub struct Cmd {
41    #[command(flatten)]
42    pub config: config::ArgsLocatorAndNetwork,
43    /// Format of the output
44    #[arg(long, default_value = "text")]
45    pub output: OutputFormat,
46}
47
48impl Cmd {
49    pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
50        let output = Output::new(self.output.into(), global_args.quiet);
51
52        // On failure, surface "Unhealthy" for humans then propagate the error so
53        // the process exits non-zero and the top-level handler renders it (as the
54        // structured JSON envelope in JSON mode). A reachable-but-unhealthy node
55        // instead returns `Ok` with a status, handled below.
56        let resp = match self.config.get_network()?.rpc_client()?.get_health().await {
57            Ok(resp) => resp,
58            Err(err) => {
59                output.readable(|print| print.errorln("Unhealthy"));
60                return Err(err.into());
61            }
62        };
63
64        output.readable(|print| {
65            if resp.status.eq_ignore_ascii_case("healthy") {
66                print.checkln("Healthy");
67            } else {
68                print.warnln(format!("Status: {}", resp.status));
69            }
70            print.infoln(format!("Latest ledger: {}", resp.latest_ledger));
71        });
72        output.json_value(&resp)?;
73
74        Ok(())
75    }
76}