pacsea/sources/status/
mod.rs1use crate::state::ArchStatusColor;
4use std::fmt::Write;
5
6mod api;
8mod html;
10pub mod translate;
12mod utils;
14
15use api::{parse_status_api_summary, parse_uptimerobot_api};
16use html::{is_aur_down_in_monitors, parse_arch_status_from_html};
17use utils::{extract_aur_today_percent, extract_aur_today_rect_color, severity_max};
18
19type Result<T> = super::Result<T>;
21
22#[allow(clippy::missing_const_for_fn)]
36pub async fn fetch_arch_status_text() -> Result<(String, ArchStatusColor)> {
37 let api_url = "https://status.archlinux.org/api/v2/summary.json";
39 let api_result =
40 tokio::task::spawn_blocking(move || crate::util::curl::curl_json(api_url)).await;
41
42 if let Ok(Ok(v)) = api_result {
43 let (mut text, mut color, suffix) = parse_status_api_summary(&v);
44
45 if let Ok(Ok(html)) = tokio::task::spawn_blocking(|| {
47 crate::util::curl::curl_text("https://status.archlinux.org")
48 })
49 .await
50 {
51 if is_aur_down_in_monitors(&html) {
54 let aur_pct_opt = extract_aur_today_percent(&html);
55 let aur_pct_suffix = aur_pct_opt
56 .map(|p| format!(" — AUR today: {p}%"))
57 .unwrap_or_default();
58 let text = format!("Status: AUR Down{aur_pct_suffix}");
59 return Ok((text, ArchStatusColor::IncidentSevereToday));
60 }
61
62 let aur_pct_opt = extract_aur_today_percent(&html);
64 if let Some(p) = aur_pct_opt {
65 let _ = write!(text, " — AUR today: {p}%");
66 }
67
68 if let Some(rect_color) = extract_aur_today_rect_color(&html) {
71 let api_says_operational = matches!(
73 v.get("components")
74 .and_then(|c| c.as_array())
75 .and_then(|arr| arr.iter().find(|c| {
76 c.get("name")
77 .and_then(|n| n.as_str())
78 .is_some_and(|n| n.to_lowercase().contains("aur"))
79 }))
80 .and_then(|c| c.get("status").and_then(|s| s.as_str())),
81 Some("operational")
82 );
83
84 if api_says_operational
85 && matches!(
86 rect_color,
87 ArchStatusColor::IncidentToday | ArchStatusColor::IncidentSevereToday
88 )
89 {
90 color = rect_color;
92 let text_lower = text.to_lowercase();
94 let pct_suffix = if text_lower.contains("aur today") {
95 String::new() } else {
97 aur_pct_opt
98 .map(|p| format!(" — AUR today: {p}%"))
99 .unwrap_or_default()
100 };
101 match rect_color {
102 ArchStatusColor::IncidentSevereToday => {
103 if !text_lower.contains("outage") && !text_lower.contains("issues") {
104 text = format!("AUR issues detected (see status){pct_suffix}");
105 }
106 }
107 ArchStatusColor::IncidentToday => {
108 if !text_lower.contains("degraded")
109 && !text_lower.contains("outage")
110 && !text_lower.contains("issues")
111 {
112 text = format!("AUR degraded (see status){pct_suffix}");
113 }
114 }
115 _ => {}
116 }
117 } else {
118 color = severity_max(color, rect_color);
120 }
121 }
122 }
123
124 if let Some(sfx) = suffix
125 && !text.to_lowercase().contains(&sfx.to_lowercase())
126 {
127 text = format!("{text} {sfx}");
128 }
129
130 return Ok((text, color));
131 }
132
133 let uptimerobot_api_url = "https://status.archlinux.org/api/getMonitorList/vmM5ruWEAB";
135 let uptimerobot_result =
136 tokio::task::spawn_blocking(move || crate::util::curl::curl_json(uptimerobot_api_url))
137 .await;
138
139 if let Ok(Ok(v)) = uptimerobot_result
140 && let Some((mut text, mut color)) = parse_uptimerobot_api(&v)
141 {
142 if let Ok(Ok(html)) = tokio::task::spawn_blocking(|| {
145 crate::util::curl::curl_text("https://status.archlinux.org")
146 })
147 .await
148 && is_aur_down_in_monitors(&html)
149 {
150 let aur_pct_opt = extract_aur_today_percent(&html);
151 let aur_pct_suffix = aur_pct_opt
152 .map(|p| format!(" — AUR today: {p}%"))
153 .unwrap_or_default();
154 text = format!("Status: AUR Down{aur_pct_suffix}");
155 color = ArchStatusColor::IncidentSevereToday;
156 }
157 return Ok((text, color));
158 }
159
160 let url = "https://status.archlinux.org";
162 let body = tokio::task::spawn_blocking(move || crate::util::curl::curl_text(url)).await??;
163
164 let (text, color) = parse_arch_status_from_html(&body);
167 Ok((text, color))
170}