1use serde::{Deserialize, Serialize};
12
13use super::HIVE_DEFAULT_URL;
14use crate::UtcTimestamp;
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(deny_unknown_fields)]
19pub struct HiveStatusInstanceV1 {
20 pub id: String,
21 pub label: String,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(deny_unknown_fields)]
27pub struct HiveStatusOrgV1 {
28 pub name: String,
29}
30
31#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
33#[serde(deny_unknown_fields)]
34pub struct HiveStatusDraftsV1 {
35 pub queued: u32,
37 pub refused: u32,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(deny_unknown_fields)]
45pub struct HiveStatusFilterPackV1 {
46 pub pack_id: String,
47 pub version: u64,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(rename_all = "snake_case")]
53pub enum HiveBusControlActionV1 {
54 Hold,
55 Stop,
56 Veto,
57 #[serde(other)]
58 Unknown,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(deny_unknown_fields)]
64pub struct HiveStatusBusControlV1 {
65 pub target_agent: Option<String>,
67 pub action: HiveBusControlActionV1,
68 pub target_ref: Option<String>,
70 pub reason: String,
71 pub issued_by: String,
72 pub since: UtcTimestamp,
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(rename_all = "snake_case")]
78pub enum HiveBusControlsInactiveV1 {
79 NotEnrolled,
80 LeaseLapsed,
81 #[serde(other)]
82 Unknown,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87#[serde(deny_unknown_fields)]
88pub struct HiveCliStatusV1 {
89 pub available: bool,
91 pub connected: bool,
93 pub instance: Option<HiveStatusInstanceV1>,
94 pub org: Option<HiveStatusOrgV1>,
95 pub last_seen: Option<UtcTimestamp>,
97 pub url: String,
99 #[serde(default)]
101 pub drafts: HiveStatusDraftsV1,
102 #[serde(default)]
104 pub filter_packs: Vec<HiveStatusFilterPackV1>,
105 #[serde(default)]
107 pub policy_refusal: Option<String>,
108 #[serde(default)]
111 pub web_url: Option<String>,
112 #[serde(default, skip_serializing_if = "Vec::is_empty")]
115 pub bus_controls: Vec<HiveStatusBusControlV1>,
116 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub bus_controls_inactive: Option<HiveBusControlsInactiveV1>,
120 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub metrics_v2: Option<HiveStatusMetricsV2>,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127#[serde(deny_unknown_fields)]
128pub struct HiveStatusMetricsV2 {
129 pub sharing: super::metrics_v2::SharingStateV2,
132 pub last_sent_at: Option<String>,
133 pub buffered_buckets: u64,
134 pub dropped_buckets: u64,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
138 pub released: Option<bool>,
139}
140
141impl HiveCliStatusV1 {
142 pub fn not_connected(url: Option<&str>) -> Self {
144 Self {
145 available: true,
146 connected: false,
147 instance: None,
148 org: None,
149 last_seen: None,
150 url: url.unwrap_or(HIVE_DEFAULT_URL).to_string(),
151 drafts: HiveStatusDraftsV1::default(),
152 filter_packs: Vec::new(),
153 policy_refusal: None,
154 web_url: Some(url.unwrap_or(HIVE_DEFAULT_URL).to_string()),
155 bus_controls: Vec::new(),
156 bus_controls_inactive: None,
157 metrics_v2: None,
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn not_connected_prints_nulls() {
168 assert_eq!(
169 serde_json::to_string(&HiveCliStatusV1::not_connected(None)).unwrap(),
170 r#"{"available":true,"connected":false,"instance":null,"org":null,"last_seen":null,"url":"https://hive.leanctx.com","drafts":{"queued":0,"refused":0},"filter_packs":[],"policy_refusal":null,"web_url":"https://hive.leanctx.com"}"#
171 );
172 }
173
174 #[test]
175 fn bus_controls_appear_only_when_present() {
176 let mut status = HiveCliStatusV1::not_connected(None);
177 let quiet = serde_json::to_string(&status).unwrap();
178 assert!(!quiet.contains("bus_controls"));
179 status.bus_controls_inactive = Some(HiveBusControlsInactiveV1::LeaseLapsed);
180 assert!(
181 serde_json::to_string(&status)
182 .unwrap()
183 .contains(r#""bus_controls_inactive":"lease_lapsed""#)
184 );
185 }
186
187 #[test]
188 fn a_1_0_status_still_reads() {
189 let old = r#"{"available":true,"connected":false,"instance":null,"org":null,"last_seen":null,"url":"https://hive.leanctx.com"}"#;
190 let status: HiveCliStatusV1 = serde_json::from_str(old).unwrap();
191 assert_eq!(
192 status,
193 HiveCliStatusV1 {
194 web_url: None,
195 ..HiveCliStatusV1::not_connected(None)
196 }
197 );
198 }
199}