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}