use std::net::SocketAddr;
use miette::{IntoDiagnostic as _, Result, WrapErr as _, miette};
use reqwest::Method;
use serde_json::Value;
use crate::alertd::{certificates::TASK_NAME, commands};
pub async fn ask(addrs: &[SocketAddr], endpoint: &str, query: &[(&str, String)]) -> Result<Value> {
call(addrs, Method::GET, endpoint, query).await
}
pub async fn tell(addrs: &[SocketAddr], endpoint: &str, query: &[(&str, String)]) -> Result<Value> {
call(addrs, Method::POST, endpoint, query).await
}
async fn call(
addrs: &[SocketAddr],
method: Method,
endpoint: &str,
query: &[(&str, String)],
) -> Result<Value> {
let addrs = if addrs.is_empty() {
commands::default_server_addrs()
} else {
addrs.to_vec()
};
let (client, base) = commands::try_connect_daemon(&addrs).await.wrap_err(
"no alertd daemon is running here; certificates are collected by the daemon, so start it first",
)?;
let response = client
.request(method, format!("{base}/tasks/{TASK_NAME}/{endpoint}"))
.query(query)
.send()
.await
.into_diagnostic()
.wrap_err_with(|| format!("asking the daemon for {endpoint}"))?;
let status = response.status();
let body = response.text().await.into_diagnostic()?;
if status == reqwest::StatusCode::FORBIDDEN {
return Err(miette!(
"the daemon refused: {} (this command changes what canopy publishes, so run it as root)",
body.trim()
));
}
if !status.is_success() {
return Err(miette!("the daemon refused: {}", body.trim()));
}
serde_json::from_str(&body)
.into_diagnostic()
.wrap_err("parsing the daemon's answer")
}
pub fn list(heading: &str, rows: &[String]) {
println!("{heading}:");
if rows.is_empty() {
println!(" (none)");
}
for row in rows {
println!(" {row}");
}
}
pub fn text(value: &Value, key: &str) -> String {
match &value[key] {
Value::Null => "—".to_string(),
Value::String(s) => s.clone(),
other => other.to_string(),
}
}
pub fn joined(value: &Value, key: &str) -> String {
value[key]
.as_array()
.map(|items| {
items
.iter()
.filter_map(Value::as_str)
.collect::<Vec<_>>()
.join(", ")
})
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "(none)".to_string())
}