1use std::{collections::BTreeMap, fmt::Debug};
4
5#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
9pub enum Value {
10 Null,
12 String(String),
14 Integer(i64),
16 Bool(bool),
18 Bytes(Vec<u8>),
20 Vec(Vec<Value>),
22 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#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
44pub struct Status {
45 pub impl_version: Option<String>,
47
48 pub replica_health_status: Option<String>,
50
51 pub root_key: Option<Vec<u8>>,
53
54 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}