1use std::collections::HashMap;
2use std::fmt::Display;
3
4pub type Result<T> = core::result::Result<T, anyhow::Error>;
5
6#[derive(Debug, Clone)]
7pub struct Kuma {
8 pub url: String,
9 pub auth: String,
10}
11
12impl Kuma {
13 pub fn new(url: impl Into<String>, auth: impl Into<String>) -> Self {
14 let mut url = url.into();
15
16 if url.contains("https://") {
17 (0..8).for_each(|_| {
18 url.remove(0);
19 });
20 };
21
22 Self {
23 url,
24 auth: format!("https://:{}", auth.into()),
25 }
26 }
27
28 pub async fn get_abstract(&self) -> DataHouse {
30 if let Ok(data) = self.get().await {
31 return data;
32 };
33 DataHouse {
34 entries: HashMap::new(),
35 state: HouseState::Offline,
36 }
37 }
38 pub async fn get(&self) -> Result<DataHouse> {
40 let uri = format!("{}@{}", self.auth, self.url);
41 println!("URI: {uri}");
42 let data: Vec<Data> = reqwest::get(uri)
43 .await?
44 .text()
45 .await?
46 .split('\n')
47 .filter(|a| a.contains("monitor_status{monitor_name="))
48 .map(|a| {
49 let mut a: Vec<String> = a.split("").map(String::from).collect();
50 for _ in 0..15 {
51 a.remove(0);
52 }
53 a.into_iter().fold(String::new(), |mut a, b| {
54 a += &b;
55 a
56 })
57 })
58 .map(|a| {
59 let mut a: Vec<String> = a
60 .replace(['"', '\\'], "")
61 .split(',')
62 .filter_map(|a| a.split('=').map(String::from).last())
63 .enumerate()
64 .map(|a| {
65 if a.0 != 4 {
66 return a.1;
67 }
68 a.1.split("")
69 .filter_map(|a| a.parse::<u32>().ok())
70 .sum::<u32>()
71 .to_string()
72 })
73 .collect();
74 a.remove(3);
75 a
76 })
77 .filter_map(|a| {
78 Some(Data {
79 monitor_name: a.first()?.clone(),
80 monitor_type: MonitorType::from_str(a.get(1)?),
81 monitor_url: a.get(2)?.clone(),
82 status: Status::from_str(a.get(3)?)?,
83 })
84 })
85 .collect();
86
87 Ok(DataHouse::from_vec(data))
88 }
89}
90#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
91pub struct Data {
92 pub monitor_name: String,
93 pub monitor_type: MonitorType,
94 pub monitor_url: String,
95 pub status: Status,
96}
97
98#[derive(Debug, Clone)]
99pub struct DataHouse {
100 pub entries: HashMap<String, Data>,
101 pub state: HouseState,
102}
103impl DataHouse {
104 pub fn offline_services(&self) -> HashMap<String, Data> {
105 self.clone()
106 .entries
107 .into_iter()
108 .filter(|a| a.1.status == Status::Offline)
109 .collect()
110 }
111 fn from_vec(input: Vec<Data>) -> Self {
112 let mut map = HashMap::new();
113 for x in input {
114 map.insert(x.monitor_name.clone(), x);
115 }
116
117 let (offline_count, online_count) = map.values().fold((0, 0), |(mut of, mut on), b| {
118 match b.status {
119 Status::Online => on += 1,
120 Status::Offline => of += 1,
121 };
122 (of, on)
123 });
124 Self {
125 entries: map,
126 state: match (offline_count, online_count) {
127 (0, 0) => HouseState::Offline,
128 (0, _) => HouseState::Online,
129 (_, 0) => HouseState::Offline,
130 (a, _) => HouseState::Degraded(a),
131 },
132 }
133 }
134}
135#[derive(Debug, Clone, PartialEq)]
136pub enum HouseState {
137 Offline,
139 Degraded(usize),
141 Online, }
144impl Display for HouseState {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 let str = match self {
147 HouseState::Offline => "Offline",
148 HouseState::Degraded(_) => "Degraded",
149 HouseState::Online => "Online",
150 }
151 .to_string();
152 write!(f, "{}", str)
153 }
154}
155impl HouseState {
156 pub fn is_degraded(&self) -> Option<usize> {
157 match self {
158 HouseState::Offline | HouseState::Online => None,
159 HouseState::Degraded(a) => Some(*a),
160 }
161 }
162}
163#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
164pub enum MonitorType {
165 Http,
166 Other,
167}
168impl Display for MonitorType {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 let str = match self {
171 MonitorType::Http => "HTTP/HTTPS",
172 MonitorType::Other => "Other",
173 }
174 .to_string();
175 write!(f, "{}", str)
176 }
177}
178impl MonitorType {
179 fn from_str(i: impl Into<String>) -> Self {
180 match i.into().as_str() {
181 "http" => MonitorType::Http,
182 _ => MonitorType::Other,
183 }
184 }
185}
186#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq)]
187pub enum Status {
188 Online,
189 Offline,
190}
191impl Display for Status {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 let str = match self {
194 Status::Online => "Online",
195 Status::Offline => "Offline",
196 }
197 .to_string();
198 write!(f, "{}", str)
199 }
200}
201impl Status {
202 fn from_str(i: impl Into<String>) -> Option<Self> {
203 match i.into().parse::<u32>().ok()? {
204 0 => Some(Status::Offline),
205 1 => Some(Status::Online),
206 _ => None,
207 }
208 }
209}