crawlora 1.49.0-sdk.3

Official async Rust client for the Crawlora public web data API
use reqwest::{header::HeaderMap, StatusCode};
use serde::de::DeserializeOwned;
use serde_json::Value;
use std::time::Duration;

use crate::Error;

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ResponseMode {
    #[default]
    Auto,
    Json,
    Text,
    Bytes,
}

#[derive(Clone, Debug)]
pub struct RequestOptions {
    pub headers: HeaderMap,
    pub timeout: Option<Duration>,
    pub max_retries: Option<usize>,
    pub response_mode: ResponseMode,
    pub retry_non_idempotent: bool,
}

impl Default for RequestOptions {
    fn default() -> Self {
        Self {
            headers: HeaderMap::new(),
            timeout: None,
            max_retries: None,
            response_mode: ResponseMode::Auto,
            retry_non_idempotent: false,
        }
    }
}

#[derive(Clone, Debug)]
pub enum ResponseBody {
    Json(Value),
    Text(String),
    Bytes(Vec<u8>),
}

impl ResponseBody {
    pub fn as_json(&self) -> Option<&Value> {
        match self {
            Self::Json(value) => Some(value),
            _ => None,
        }
    }

    pub fn into_json(self) -> Result<Value, Error> {
        match self {
            Self::Json(value) => Ok(value),
            Self::Text(text) => Ok(serde_json::from_str(&text)?),
            Self::Bytes(bytes) => Ok(serde_json::from_slice(&bytes)?),
        }
    }
}

#[derive(Clone, Debug)]
pub struct ApiResponse {
    pub status: StatusCode,
    pub headers: HeaderMap,
    pub body: ResponseBody,
}

impl ApiResponse {
    pub fn json<T: DeserializeOwned>(&self) -> Result<T, Error> {
        match &self.body {
            ResponseBody::Json(value) => Ok(serde_json::from_value(value.clone())?),
            ResponseBody::Text(text) => Ok(serde_json::from_str(text)?),
            ResponseBody::Bytes(bytes) => Ok(serde_json::from_slice(bytes)?),
        }
    }

    pub fn into_json<T: DeserializeOwned>(self) -> Result<T, Error> {
        match self.body {
            ResponseBody::Json(value) => Ok(serde_json::from_value(value)?),
            ResponseBody::Text(text) => Ok(serde_json::from_str(&text)?),
            ResponseBody::Bytes(bytes) => Ok(serde_json::from_slice(&bytes)?),
        }
    }

    pub fn request_id(&self) -> Option<&str> {
        self.headers
            .get("x-request-id")
            .or_else(|| self.headers.get("x-crawlora-request-id"))
            .and_then(|value| value.to_str().ok())
    }
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct ParameterDefinition {
    pub name: &'static str,
    pub collection_format: Option<&'static str>,
    pub kind: Option<&'static str>,
    pub required: bool,
    pub enum_values: &'static [&'static str],
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct OperationDefinition {
    pub id: &'static str,
    pub method: &'static str,
    pub path: &'static str,
    pub path_params: &'static [ParameterDefinition],
    pub query_params: &'static [ParameterDefinition],
    pub form_params: &'static [ParameterDefinition],
    pub body_param: Option<&'static str>,
    pub body_required: bool,
    pub consumes: &'static [&'static str],
    pub produces: &'static [&'static str],
    pub security: &'static [&'static str],
}