openmeteo-rs 1.0.0

Rust client for the Open-Meteo weather API.
Documentation
use std::time::Duration;

use serde::Deserialize;

use crate::error::map_reqwest_error;
use crate::{Error, Result};

pub(crate) async fn read_success_body(
    response: reqwest::Response,
    timeout: Duration,
) -> Result<Vec<u8>> {
    let status = response.status();

    if status.as_u16() == 429 {
        let retry_after = response
            .headers()
            .get(reqwest::header::RETRY_AFTER)
            .and_then(|value| value.to_str().ok())
            .and_then(|value| value.parse::<u64>().ok())
            .map(Duration::from_secs);
        return Err(Error::RateLimited { retry_after });
    }

    if !status.is_success() {
        let status_code = status.as_u16();
        let text = response.text().await.unwrap_or_default();
        let reason = serde_json::from_str::<ApiErrorBody>(&text)
            .ok()
            .and_then(|body| body.reason)
            .filter(|reason| !reason.is_empty())
            .unwrap_or_else(|| {
                if text.is_empty() {
                    status.to_string()
                } else {
                    text
                }
            });
        return Err(Error::Api {
            status: status_code,
            reason,
        });
    }

    Ok(response
        .bytes()
        .await
        .map_err(|err| map_reqwest_error(err, timeout))?
        .to_vec())
}

#[derive(Debug, Deserialize)]
struct ApiErrorBody {
    reason: Option<String>,
}