ic_agent/agent/
status.rs

1//! Types for interacting with the status endpoint of a replica. See [`Status`] for details.
2
3use std::{collections::BTreeMap, fmt::Debug};
4
5/// Value returned by the status endpoint of a replica. This is a loose mapping to CBOR values.
6/// Because the agent should not return [`serde_cbor::Value`] directly across API boundaries,
7/// we reimplement it as [`Value`] here.
8#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
9pub enum Value {
10    /// See [`Null`](serde_cbor::Value::Null).
11    Null,
12    /// See [`String`](serde_cbor::Value::Text).
13    String(String),
14    /// See [`Integer`](serde_cbor::Value::Integer).
15    Integer(i64),
16    /// See [`Bool`](serde_cbor::Value::Bool).
17    Bool(bool),
18    /// See [`Bytes`](serde_cbor::Value::Bytes).
19    Bytes(Vec<u8>),
20    /// See [`Vec`](serde_cbor::Value::Array).
21    Vec(Vec<Value>),
22    /// See [`Map`](serde_cbor::Value::Map).
23    Map(BTreeMap<String, Box<Value>>),
24}
25
26impl std::fmt::Display for Value {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        match self {
29            Value::Null => f.write_str("null"),
30            Value::String(s) => f.write_fmt(format_args!(r#""{}""#, s.escape_debug())),
31            Value::Integer(i) => f.write_str(&i.to_string()),
32            Value::Bool(true) => f.write_str("true"),
33            Value::Bool(false) => f.write_str("false"),
34            Value::Bytes(b) => f.debug_list().entries(b).finish(),
35            Value::Vec(v) => f.debug_list().entries(v).finish(),
36            Value::Map(m) => f.debug_map().entries(m).finish(),
37        }
38    }
39}
40
41/// The structure returned by [`super::Agent::status`], containing the information returned
42/// by the status endpoint of a replica.
43#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
44pub struct Status {
45    /// Optional. The precise git revision of the Internet Computer Protocol implementation.
46    pub impl_version: Option<String>,
47
48    /// Optional.  The health status of the replica.  One hopes it's "healthy".
49    pub replica_health_status: Option<String>,
50
51    /// Optional.  The root (public) key used to verify certificates.
52    pub root_key: Option<Vec<u8>>,
53
54    /// Contains any additional values that the replica gave as status.
55    pub values: BTreeMap<String, Box<Value>>,
56}
57
58impl std::fmt::Display for Status {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        f.write_str("{\n")?;
61        let mut first = true;
62        for (key, value) in &self.values {
63            if first {
64                first = false;
65            } else {
66                f.write_str(",\n")?;
67            }
68            f.write_fmt(format_args!(r#"  "{}": "#, key.escape_debug()))?;
69            std::fmt::Display::fmt(&value, f)?;
70        }
71        f.write_str("\n}")
72    }
73}
74
75fn cbor_value_to_value(value: &serde_cbor::Value) -> Result<Value, ()> {
76    match value {
77        serde_cbor::Value::Null => Ok(Value::Null),
78        serde_cbor::Value::Bool(b) => Ok(Value::Bool(*b)),
79        serde_cbor::Value::Integer(i) => Ok(Value::Integer(*i as i64)),
80        serde_cbor::Value::Bytes(b) => Ok(Value::Bytes(b.to_owned())),
81        serde_cbor::Value::Text(s) => Ok(Value::String(s.to_owned())),
82        serde_cbor::Value::Array(a) => Ok(Value::Vec(
83            a.iter()
84                .map(cbor_value_to_value)
85                .collect::<Result<Vec<Value>, ()>>()
86                .map_err(|_| ())?,
87        )),
88        serde_cbor::Value::Map(m) => {
89            let mut map = BTreeMap::new();
90            for (key, value) in m {
91                let k = match key {
92                    serde_cbor::Value::Text(t) => t.to_owned(),
93                    serde_cbor::Value::Integer(i) => i.to_string(),
94                    _ => return Err(()),
95                };
96                let v = Box::new(cbor_value_to_value(value)?);
97
98                map.insert(k, v);
99            }
100            Ok(Value::Map(map))
101        }
102        serde_cbor::Value::Tag(_, v) => cbor_value_to_value(v.as_ref()),
103        _ => Err(()),
104    }
105}
106
107impl std::convert::TryFrom<&serde_cbor::Value> for Status {
108    type Error = ();
109
110    fn try_from(value: &serde_cbor::Value) -> Result<Self, ()> {
111        let v = cbor_value_to_value(value)?;
112
113        match v {
114            Value::Map(map) => {
115                let impl_version: Option<String> = map.get("impl_version").and_then(|v| {
116                    if let Value::String(s) = v.as_ref() {
117                        Some(s.to_owned())
118                    } else {
119                        None
120                    }
121                });
122                let replica_health_status: Option<String> =
123                    map.get("replica_health_status").and_then(|v| {
124                        if let Value::String(s) = v.as_ref() {
125                            Some(s.to_owned())
126                        } else {
127                            None
128                        }
129                    });
130                let root_key: Option<Vec<u8>> = map.get("root_key").and_then(|v| {
131                    if let Value::Bytes(bytes) = v.as_ref() {
132                        Some(bytes.to_owned())
133                    } else {
134                        None
135                    }
136                });
137
138                Ok(Status {
139                    impl_version,
140                    replica_health_status,
141                    root_key,
142                    values: map,
143                })
144            }
145            _ => Err(()),
146        }
147    }
148}