use crate::{HttpRequest, HttpResponse, HttpTransport, Method, StatusCode};
use anyhow::Result;
use async_trait::async_trait;
use axum::Router;
use http_body_util::BodyExt;
use hyper::body::Bytes;
use tower::ServiceExt;
#[derive(Clone)]
pub struct AxumTransport {
router: Router,
}
impl std::fmt::Debug for AxumTransport {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AxumTransport")
.field("router", &"<axum::Router>")
.finish()
}
}
impl AxumTransport {
pub fn new(router: Router) -> Self {
Self { router }
}
}
#[async_trait]
impl HttpTransport for AxumTransport {
async fn send(&self, req: HttpRequest) -> Result<HttpResponse> {
let method = match req.method {
Method::Get => hyper::Method::GET,
Method::Post => hyper::Method::POST,
};
let mut request_builder = hyper::Request::builder().method(method).uri(&req.url);
for (key, value) in &req.headers {
request_builder = request_builder.header(key, value);
}
let body = req.body.unwrap_or_default();
let request = request_builder
.body(body)
.map_err(|e| anyhow::anyhow!("Failed to build request: {}", e))?;
let response = self
.router
.clone()
.oneshot(request)
.await
.map_err(|e| anyhow::anyhow!("Request failed: {}", e))?;
let status = StatusCode(response.status().as_u16());
let headers = response
.headers()
.iter()
.map(|(k, v)| (k.to_string(), v.to_str().unwrap_or_default().to_string()))
.collect();
let body_bytes: Bytes = response
.into_body()
.collect()
.await
.map_err(|e| anyhow::anyhow!("Failed to read response body: {}", e))?
.to_bytes();
let body = String::from_utf8_lossy(&body_bytes).to_string();
Ok(HttpResponse {
status,
headers,
body,
})
}
}