use anyhow::{Result, bail};
pub(in crate::client::http_client) async fn response_or_body_error(
resp: super::DaemonResponse,
) -> Result<super::DaemonResponse> {
let status = resp.status();
if status.is_success() {
return Ok(resp);
}
let body = resp.text().await.unwrap_or_default();
match extract_message(&body) {
Some(msg) => bail!("{status}: {msg}"),
None => bail!("{status}"),
}
}
fn extract_message(body: &str) -> Option<String> {
let trimmed = body.trim();
if trimmed.is_empty() {
return None;
}
if let Ok(value) = serde_json::from_str::<serde_json::Value>(trimmed) {
for key in ["error", "message"] {
if let Some(s) = value.get(key).and_then(|v| v.as_str()) {
return Some(s.to_string());
}
}
}
Some(trimmed.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extract_message_reads_json_error_field() {
assert_eq!(
extract_message(r#"{"error":"tag must not be blank"}"#).as_deref(),
Some("tag must not be blank")
);
}
#[test]
fn extract_message_reads_json_message_field() {
assert_eq!(
extract_message(r#"{"message":"nope"}"#).as_deref(),
Some("nope")
);
}
#[test]
fn extract_message_falls_back_to_plain_text() {
assert_eq!(
extract_message("repo_url must not be empty").as_deref(),
Some("repo_url must not be empty")
);
}
#[test]
fn extract_message_empty_body_is_none() {
assert_eq!(extract_message(" "), None);
assert_eq!(extract_message(""), None);
}
#[test]
fn extract_message_non_object_json_falls_back_to_raw_text() {
assert_eq!(extract_message("[1,2,3]").as_deref(), Some("[1,2,3]"));
}
}