1use reqwest::{
2 Body, Client, Response,
3 header::{CONTENT_TYPE, HeaderValue},
4};
5use serde::{Serialize, de::DeserializeOwned};
6use serde_json::to_value;
7
8#[derive(Debug)]
9pub struct Session {
10 pub reqwest_client: Client,
11 pub base_url: String,
12}
13
14pub trait ResponseData<T: Sized> {
15 fn from_response(response: Response) -> impl Future<Output = Result<T, Error>>;
16}
17impl<Data: DeserializeOwned> ResponseData<Data> for Data {
18 async fn from_response(response: Response) -> Result<Self, Error> {
19 serde_json::from_slice(&response.bytes().await.map_err(Error::ReqwestError)?)
20 .map_err(Error::SerdeJsonError)
21 }
22}
23
24pub trait QueryData {
25 fn create_query(&self) -> Result<Vec<(String, String)>, Error>;
26}
27impl<Data: Serialize> QueryData for Data {
28 fn create_query(&self) -> Result<Vec<(String, String)>, Error> {
29 Ok(to_value(self)
30 .map_err(Error::SerdeJsonError)?
31 .as_object()
32 .map(|x| {
33 x.into_iter()
34 .filter_map(|(key, value)| match value {
35 serde_json::Value::String(s) => Some((key.clone(), s.clone())),
36 serde_json::Value::Number(n) => Some((key.clone(), n.to_string())),
37 _ => None,
38 })
39 .collect()
40 })
41 .unwrap_or(vec![]))
42 }
43}
44
45pub trait BodyData {
46 fn create_body(&self) -> Result<Body, Error>;
47}
48impl<Data: Serialize> BodyData for Data {
49 fn create_body(&self) -> Result<Body, Error> {
50 Ok(serde_json::to_vec(&self)
51 .map_err(Error::SerdeJsonError)?
52 .into())
53 }
54}
55
56#[derive(Default)]
57pub struct Empty {}
58impl ResponseData<Empty> for Empty {
59 async fn from_response(_response: Response) -> Result<Self, Error> {
60 Ok(Self {})
61 }
62}
63impl QueryData for Empty {
64 fn create_query(&self) -> Result<Vec<(String, String)>, Error> {
65 Ok(vec![])
66 }
67}
68impl BodyData for Empty {
69 fn create_body(&self) -> Result<Body, Error> {
70 Ok(Body::default())
71 }
72}
73
74#[derive(Debug)]
75pub struct SessionGetInput<'a, Path, Query: QueryData> {
76 pub session: &'a Session,
77 pub path: Path,
78 pub query: Query,
79}
80pub type SessionGetOutput<Output> = Result<Output, Error>;
81#[derive(Debug)]
82pub struct SessionPostInput<'a, Path, Data: BodyData> {
83 pub session: &'a Session,
84 pub path: Path,
85 pub data: Data,
86}
87pub type SessionPostOutput<Output> = Result<Output, Error>;
88#[derive(Debug)]
89pub enum Error {
90 XnodeManagerSDKError,
91 ReqwestError(reqwest::Error),
92 SerdeJsonError(serde_json::Error),
93}
94
95impl<'a> SessionGetInput<'a, Empty, Empty> {
96 pub fn new(session: &'a Session) -> Self {
97 Self {
98 session,
99 path: Empty::default(),
100 query: Empty::default(),
101 }
102 }
103}
104impl<'a, Path> SessionGetInput<'a, Path, Empty> {
105 pub fn new_with_path(session: &'a Session, path: Path) -> Self {
106 Self {
107 session,
108 path,
109 query: Empty::default(),
110 }
111 }
112}
113impl<'a, Query: Serialize> SessionGetInput<'a, Empty, Query> {
114 pub fn new_with_query(session: &'a Session, query: Query) -> Self {
115 Self {
116 session,
117 path: Empty::default(),
118 query,
119 }
120 }
121}
122
123impl<'a> SessionPostInput<'a, Empty, Empty> {
124 pub fn new(session: &'a Session) -> Self {
125 Self {
126 session,
127 path: Empty::default(),
128 data: Empty::default(),
129 }
130 }
131}
132impl<'a, Path> SessionPostInput<'a, Path, Empty> {
133 pub fn new_with_path(session: &'a Session, path: Path) -> Self {
134 Self {
135 session,
136 path,
137 data: Empty::default(),
138 }
139 }
140}
141impl<'a, Data: Serialize> SessionPostInput<'a, Empty, Data> {
142 pub fn new_with_data(session: &'a Session, data: Data) -> Self {
143 Self {
144 session,
145 path: Empty::default(),
146 data,
147 }
148 }
149}
150
151pub async fn session_get<
152 Output: ResponseData<Output>,
153 Path,
154 Query: QueryData,
155 PathOutput: Into<String>,
156>(
157 input: SessionGetInput<'_, Path, Query>,
158 scope: String,
159 path: fn(Path) -> PathOutput,
160) -> SessionGetOutput<Output> {
161 let session = input.session;
162 Output::from_response(
163 session
164 .reqwest_client
165 .get(format!(
166 "{}{}{}",
167 session.base_url,
168 scope,
169 path(input.path).into()
170 ))
171 .query(&input.query.create_query()?)
172 .send()
173 .await
174 .map_err(Error::ReqwestError)?,
175 )
176 .await
177}
178
179pub async fn session_post<
180 Output: ResponseData<Output>,
181 Path,
182 Data: BodyData,
183 PathOutput: Into<String>,
184>(
185 input: SessionPostInput<'_, Path, Data>,
186 scope: String,
187 path: fn(Path) -> PathOutput,
188) -> SessionPostOutput<Output> {
189 let session = input.session;
190 Output::from_response(
191 session
192 .reqwest_client
193 .post(format!(
194 "{}{}{}",
195 session.base_url,
196 scope,
197 path(input.path).into()
198 ))
199 .header(CONTENT_TYPE, HeaderValue::from_static("application/json"))
200 .body(input.data.create_body()?)
201 .send()
202 .await
203 .map_err(Error::ReqwestError)?,
204 )
205 .await
206}