pcloud/request.rs
1//! The client implementing the [HTTP Json protocol](https://docs.pcloud.com/protocols/http_json_protocol/)
2
3use crate::{Credentials, Error};
4
5/// Reads the HTTP response and attempts to deserialize it into a type `T`.
6/// If the response is successful, it returns the payload, otherwise, it returns an error.
7async fn read_response<T: serde::de::DeserializeOwned>(res: reqwest::Response) -> Result<T, Error> {
8 let status = res.status();
9 tracing::debug!("responded with status {status:?}");
10 res.json::<Response<T>>()
11 .await
12 .map_err(Error::from)
13 .and_then(Response::payload)
14}
15
16impl crate::Client {
17 /// Constructs a URL for a specific method.
18 ///
19 /// # Arguments
20 ///
21 /// * `method` - The method or endpoint to be appended to the base URL.
22 ///
23 /// # Returns
24 ///
25 /// A formatted string containing the full URL.
26 fn build_url(&self, method: &str) -> String {
27 format!("{}/{}", self.base_url, method)
28 }
29
30 /// Sends a GET request with query parameters and deserializes the response into type `T`.
31 ///
32 /// # Arguments
33 ///
34 /// * `method` - The method or endpoint to be used in the request.
35 /// * `params` - The parameters to be sent with the GET request.
36 ///
37 /// # Returns
38 ///
39 /// A result containing the deserialized response payload or an error.
40 #[tracing::instrument(name = "get", skip(self, params))]
41 pub(crate) async fn get_request<T: serde::de::DeserializeOwned, P: serde::Serialize>(
42 &self,
43 method: &str,
44 params: P,
45 ) -> Result<T, Error> {
46 let uri = self.build_url(method);
47 let res = self
48 .inner
49 .get(uri)
50 .query(&WithCredentials {
51 credentials: &self.credentials,
52 inner: params,
53 })
54 .send()
55 .await?;
56 read_response(res).await
57 }
58
59 /// Sends a PUT request with query parameters and a binary payload, and deserializes the response into type `T`.
60 ///
61 /// # Arguments
62 ///
63 /// * `method` - The method or endpoint to be used in the request.
64 /// * `params` - The parameters to be sent with the PUT request.
65 /// * `payload` - The binary payload to be included in the body of the PUT request.
66 ///
67 /// # Returns
68 ///
69 /// A result containing the deserialized response payload or an error.
70 #[tracing::instrument(name = "put", skip(self, params))]
71 pub(crate) async fn put_request_data<T: serde::de::DeserializeOwned, P: serde::Serialize>(
72 &self,
73 method: &str,
74 params: P,
75 payload: Vec<u8>,
76 ) -> Result<T, Error> {
77 let uri = self.build_url(method);
78 let res = self
79 .inner
80 .put(uri)
81 .query(&WithCredentials {
82 credentials: &self.credentials,
83 inner: params,
84 })
85 .body(payload)
86 .send()
87 .await?;
88 read_response(res).await
89 }
90
91 /// Sends a POST request with multipart form data and query parameters, and deserializes the response into type `T`.
92 ///
93 /// # Arguments
94 ///
95 /// * `method` - The method or endpoint to be used in the request.
96 /// * `params` - The parameters to be sent with the POST request.
97 /// * `form` - The multipart form data to be included in the body of the POST request.
98 ///
99 /// # Returns
100 ///
101 /// A result containing the deserialized response payload or an error.
102 #[tracing::instrument(name = "post", skip(self, params))]
103 pub(crate) async fn post_request_multipart<
104 T: serde::de::DeserializeOwned,
105 P: serde::Serialize,
106 >(
107 &self,
108 method: &str,
109 params: P,
110 form: reqwest::multipart::Form,
111 ) -> Result<T, Error> {
112 let uri = self.build_url(method);
113 let res = self
114 .inner
115 .post(uri)
116 .query(&WithCredentials {
117 credentials: &self.credentials,
118 inner: params,
119 })
120 .multipart(form)
121 .send()
122 .await?;
123 read_response(res).await
124 }
125}
126
127/// Struct for serializing credentials along with the request parameters.
128///
129/// # Type Parameters
130/// * `I` - The inner type that holds the parameters of the request.
131#[derive(serde::Serialize)]
132struct WithCredentials<'a, I> {
133 /// The credentials for the request.
134 #[serde(flatten)]
135 credentials: &'a Credentials,
136
137 /// The parameters to be sent with the request.
138 #[serde(flatten)]
139 inner: I,
140}
141
142/// A utility function that returns `true` if the value is `false`.
143/// Used for serializing boolean values as `0` (false) or `1` (true).
144///
145/// # Arguments
146/// * `value` - A boolean value to check.
147///
148/// # Returns
149/// A boolean value indicating if the provided value is false.
150pub(crate) fn is_false(value: &bool) -> bool {
151 !*value
152}
153
154/// A custom serializer for boolean values that serializes them as strings `"0"` or `"1"`.
155///
156/// # Arguments
157/// * `value` - The boolean value to serialize.
158/// * `serializer` - The serializer to use for serialization.
159///
160/// # Returns
161/// A result indicating the success or failure of serialization.
162pub(crate) fn serialize_bool<S>(value: &bool, serializer: S) -> Result<S::Ok, S::Error>
163where
164 S: serde::Serializer,
165{
166 serializer.serialize_str(if *value { "1" } else { "0" })
167}
168
169/// Enum representing the HTTP response from the server, which can either be an error or a success.
170///
171/// # Type Parameters
172/// * `T` - The type of the response payload in case of success.
173#[derive(Debug, serde::Deserialize)]
174#[serde(untagged)]
175enum Response<T> {
176 /// Represents an error response from the server.
177 Error { result: u16, error: String },
178
179 /// Represents a successful response from the server.
180 Success {
181 #[allow(unused)]
182 result: u16,
183 #[serde(flatten)]
184 payload: T,
185 },
186}
187
188impl<T> Response<T> {
189 /// Extracts the payload from the response.
190 ///
191 /// If the response is a success, it returns the payload, otherwise, it returns an error.
192 ///
193 /// # Returns
194 /// A result containing the payload if the response is a success, or an error if the response is an error.
195 fn payload(self) -> Result<T, Error> {
196 match self {
197 Self::Error { result, error } => Err(Error::Protocol(result, error)),
198 Self::Success { payload, .. } => Ok(payload),
199 }
200 }
201}