1use std::borrow::Cow;
2
3use async_trait::async_trait;
4use bytes::Bytes;
5use http::{Request, Response};
6use std::error::Error;
7use thiserror::Error;
8
9pub trait Endpoint {
11 type Request: serde::Serialize + Send + Sync;
12 type Response: serde::de::DeserializeOwned + Send + Sync;
13
14 fn endpoint(&self) -> Cow<'static, str>;
16 fn body(&self) -> &Self::Request;
18 fn method(&self) -> http::Method {
20 http::Method::POST
21 }
22}
23
24#[async_trait]
26pub trait Query<C> {
27 type Result;
29 async fn execute(self, client: &C) -> Self::Result;
31}
32
33#[derive(Debug, Error)]
35pub enum QueryError<E>
36where
37 E: Error + Send + Sync + 'static,
38{
39 #[error("client error: {}", source)]
41 Client {
42 source: E,
44 },
45 #[error("could not parse JSON response: {}", source)]
47 Json {
48 #[from]
50 source: serde_json::Error,
51 },
52 #[error("failed to create form data: {}", source)]
54 Body {
55 #[from]
57 source: http::Error,
58 },
59}
60
61impl<E> QueryError<E>
62where
63 E: Error + Send + Sync + 'static,
64{
65 pub fn client(source: E) -> Self {
67 QueryError::Client { source }
68 }
69}
70
71#[async_trait]
73impl<T, C> Query<C> for T
74where
75 T: Endpoint + Send + Sync,
76 C: Client + Send + Sync,
77{
78 type Result = Result<T::Response, QueryError<C::Error>>;
80
81 async fn execute(self, client: &C) -> Self::Result {
82 let body = serde_json::to_vec(self.body())?;
83
84 let http_req = http::Request::builder()
85 .method(self.method())
86 .uri(String::from(self.endpoint()))
87 .header("Accept", "application/json")
88 .header("Content-Type", "application/json")
89 .body(body.into())?;
90
91 let response = client.execute(http_req).await.map_err(QueryError::client)?;
92
93 Ok(serde_json::from_slice(response.body())?)
94 }
95}
96
97#[async_trait]
99pub trait Client {
100 type Error: Error + Send + Sync + 'static;
102 async fn execute(&self, req: Request<Bytes>) -> Result<Response<Bytes>, Self::Error>;
104}