use serde_json::Value;
use crate::error::SailError;
pub(crate) fn api_error_message(data: &Value, default: &str) -> String {
data.get("error")
.and_then(|e| e.get("message"))
.and_then(Value::as_str)
.filter(|s| !s.is_empty())
.unwrap_or(default)
.to_string()
}
pub(crate) fn is_resource_not_found(data: &Value) -> bool {
data.get("error")
.and_then(|e| e.get("type"))
.and_then(Value::as_str)
== Some("not_found_error")
}
pub(crate) fn raise_api_error(status: u16, data: &Value, context: &str) -> Result<(), SailError> {
if status < 300 {
return Ok(());
}
let mut message = api_error_message(data, "request failed");
if !context.is_empty() {
message = format!("{context}: {message}");
}
if status == 404 && is_resource_not_found(data) {
return Err(SailError::NotFound { message });
}
if status == 401 || status == 403 {
return Err(SailError::PermissionDenied { message });
}
if status == 400 {
return Err(SailError::InvalidArgument { message });
}
Err(SailError::Api {
status,
message,
body: data.clone(),
})
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn api_error_ladder_maps_statuses() {
let not_found = json!({"error": {"type": "not_found_error", "message": "gone"}});
assert!(matches!(
raise_api_error(404, ¬_found, ""),
Err(SailError::NotFound { .. })
));
let route_404 = json!({"error": {"message": "no route"}});
assert!(matches!(
raise_api_error(404, &route_404, ""),
Err(SailError::Api { .. })
));
let auth = json!({"error": {"message": "nope"}});
assert!(matches!(
raise_api_error(403, &auth, ""),
Err(SailError::PermissionDenied { .. })
));
assert!(matches!(
raise_api_error(400, &auth, ""),
Err(SailError::InvalidArgument { .. })
));
assert!(matches!(
raise_api_error(503, &auth, ""),
Err(SailError::Api { status: 503, .. })
));
assert!(raise_api_error(200, &json!({}), "").is_ok());
}
}