//! Error type returned by every fallible [`crate::Client`] method.

use thiserror::Error;

/// All errors that can occur while talking to the ipdata.info API.
#[derive(Debug, Error)]
pub enum Error {
    /// Non-2xx HTTP response. `message` is taken from the API's
    /// `{"error": "..."}` body when present, otherwise the raw response body.
    #[error("ipdata: HTTP {status}: {message}")]
    Api { status: u16, message: String },

    /// The request could not be sent (DNS, TCP, TLS, timeout, ...).
    #[error("ipdata: request failed: {0}")]
    Transport(String),

    /// The response body could not be decoded as JSON.
    #[error("ipdata: failed to decode response: {0}")]
    Decode(String),
}

/// Extracts the `error` field from a JSON error body, falling back to the raw
/// body text when it isn't present or isn't valid JSON.
pub(crate) fn api_error_message(body: &str) -> String {
    #[derive(serde::Deserialize)]
    struct ErrBody {
        #[serde(default)]
        error: String,
    }
    match serde_json::from_str::<ErrBody>(body) {
        Ok(e) if !e.error.is_empty() => e.error,
        _ => body.to_string(),
    }
}
