// Copyright (C) 2020-2024 Daniel Mueller // SPDX-License-Identifier: GPL-3.0-or-later use std::fmt::Debug; use http_body_util::BodyExt; use http_body_util::Full; use hyper::body::Bytes as HyperBytes; use hyper::http::request::Builder as HttpRequestBuilder; use hyper::Error as HyperError; use hyper::Request; use hyper_tls::HttpsConnector; use hyper_util::client::legacy::Client as HttpClient; use hyper_util::client::legacy::Error as HyperUtilError; use hyper_util::rt::TokioExecutor; use http_endpoint::Bytes; use http_endpoint::Endpoint; use serde::Deserialize; use thiserror::Error; use url::Url; const HTTP_BIN_BASE_URL: &str = "https://httpbin.org/"; #[derive(Debug, Deserialize, Error, PartialEq)] #[error("an unspecified error was encountered")] pub struct NoError; #[allow(dead_code)] #[derive(Debug)] pub enum Error { Endpoint(E), Hyper(HyperError), HyperUtil(HyperUtilError), } fn request(input: &E::Input) -> Result>, E::Error> where E: Endpoint, { let mut url = Url::parse(HTTP_BIN_BASE_URL).unwrap(); url.set_path(&E::path(input)); url.set_query(E::query(input)?.as_ref().map(AsRef::as_ref)); let body = match E::body(input)? { None => HyperBytes::new(), Some(Bytes::Borrowed(slice)) => HyperBytes::from(slice), Some(Bytes::Owned(vec)) => HyperBytes::from(vec), }; let headers = E::headers(input)?; let mut request = HttpRequestBuilder::new() .method(E::method()) .uri(url.as_str()) .body(Full::new(body))?; if let Some(headers) = headers { request.headers_mut().extend(headers); } Ok(request) } pub async fn issue(input: &E::Input) -> Result> where E: Endpoint, { let client = HttpClient::builder(TokioExecutor::new()).build(HttpsConnector::new()); let request = request::(input).map_err(Error::Endpoint)?; let result = client.request(request).await.map_err(Error::HyperUtil)?; let status = result.status(); let bytes = BodyExt::collect(result) .await .map_err(Error::Hyper)? .to_bytes(); let body = bytes.as_ref(); let output = E::evaluate(status, body).map_err(Error::Endpoint)?; Ok(output) }