use std::collections::BTreeMap;
use async_trait::async_trait;
use serde::Serialize;
use crate::error::SearchError;
#[derive(Debug, Clone)]
pub struct TransportRequest {
pub method: String,
pub url: String,
pub headers: BTreeMap<String, String>,
pub body: Option<Vec<u8>>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TransportResponse {
pub url: String,
pub status: u16,
pub headers: BTreeMap<String, String>,
pub body: Vec<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub receipt: Option<String>,
}
#[async_trait]
pub trait SearchTransport: Send + Sync {
async fn execute(&self, request: TransportRequest) -> Result<TransportResponse, SearchError>;
}
#[derive(Debug, Clone, Default)]
pub struct ReqwestTransport {
client: reqwest::Client,
}
impl ReqwestTransport {
pub fn new(client: reqwest::Client) -> Self {
Self { client }
}
}
#[async_trait]
impl SearchTransport for ReqwestTransport {
async fn execute(&self, request: TransportRequest) -> Result<TransportResponse, SearchError> {
let method = reqwest::Method::from_bytes(request.method.as_bytes())
.map_err(|error| SearchError::Transport(error.to_string()))?;
let mut builder = self.client.request(method, &request.url);
for (name, value) in request.headers {
builder = builder.header(name, value);
}
if let Some(body) = request.body {
builder = builder.body(body);
}
let response = builder.send().await?;
let status = response.status().as_u16();
let url = response.url().to_string();
let headers = response
.headers()
.iter()
.filter_map(|(name, value)| {
value
.to_str()
.ok()
.map(|value| (name.to_string(), value.to_string()))
})
.collect();
let body = response.bytes().await?.to_vec();
Ok(TransportResponse {
url,
status,
headers,
body,
receipt: None,
})
}
}