rustify/
http.rs

1//! Contains helper functions for working with HTTP requests and responses.
2
3use crate::{
4    enums::{RequestMethod, RequestType},
5    errors::ClientError,
6};
7use http::{Request, Uri};
8use serde::Serialize;
9use url::Url;
10
11/// Builds a request body by serializing an object using a serializer determined
12/// by the [RequestType].
13#[instrument(skip(object), err)]
14pub fn build_body(object: &impl Serialize, ty: RequestType) -> Result<Vec<u8>, ClientError> {
15    match ty {
16        RequestType::JSON => {
17            let parse_data = serde_json::to_string(object)
18                .map_err(|e| ClientError::DataParseError { source: e.into() })?;
19            Ok(match parse_data.as_str() {
20                "null" => "".as_bytes().to_vec(),
21                "{}" => "".as_bytes().to_vec(),
22                _ => parse_data.as_bytes().to_vec(),
23            })
24        }
25    }
26}
27
28/// Builds a query string by serializing an object.
29#[instrument(skip(object), err)]
30pub fn build_query(object: &impl Serialize) -> Result<String, ClientError> {
31    serde_urlencoded::to_string(object)
32        .map_err(|e| ClientError::UrlQueryParseError { source: e.into() })
33}
34
35/// Builds a [Request] using the given [Endpoint][crate::Endpoint] and base URL.
36#[instrument(skip(query, data), err)]
37pub fn build_request(
38    base: &str,
39    path: &str,
40    method: RequestMethod,
41    query: Option<String>,
42    data: Option<Vec<u8>>,
43) -> Result<Request<Vec<u8>>, ClientError> {
44    debug!("Building endpoint request");
45    let uri = build_url(base, path, query)?;
46
47    let method_err = method.clone();
48    let uri_err = uri.to_string();
49    Request::builder()
50        .uri(uri)
51        .method(method)
52        .body(data.unwrap_or_default())
53        .map_err(|e| ClientError::RequestBuildError {
54            source: e,
55            method: method_err,
56            url: uri_err,
57        })
58}
59
60/// Combines the given base URL, relative path, and optional query parameters
61/// into a single [Uri].
62#[instrument(skip(query), err)]
63pub fn build_url(base: &str, path: &str, query: Option<String>) -> Result<Uri, ClientError> {
64    let mut url = Url::parse(base).map_err(|e| ClientError::UrlParseError { source: e })?;
65    url.path_segments_mut().unwrap().extend(path.split('/'));
66    if let Some(q) = query {
67        url.set_query(Some(q.as_str()));
68    }
69
70    url.to_string()
71        .parse::<Uri>()
72        .map_err(|e| ClientError::UrlBuildError { source: e })
73}