speedrun_api/api/
utils.rs

1use bytes::Bytes;
2use http::{header, request::Builder as RequestBuilder};
3use serde::{de::DeserializeOwned, Serializer};
4use thiserror::Error;
5
6use super::{
7    endpoint::Endpoint, query::url_to_http_uri, ApiError, Pageable, RestClient, Root, SinglePage,
8};
9
10pub(crate) fn serialize_as_csv<S, T>(
11    iter: impl IntoIterator<Item = T>,
12    serializer: S,
13) -> Result<S::Ok, S::Error>
14where
15    T: Into<&'static str>,
16    S: Serializer,
17{
18    let out: Vec<_> = iter.into_iter().map(Into::into).collect();
19    serializer.serialize_str(&out.join(","))
20}
21
22pub(crate) fn build_request<E, C>(
23    endpoint: &E,
24    client: &C,
25) -> Result<(RequestBuilder, Vec<u8>), ApiError<C::Error>>
26where
27    E: Endpoint,
28    C: RestClient,
29{
30    let url = client.rest_endpoint(&endpoint.endpoint())?;
31    build_request_internal(url, endpoint, client)
32}
33
34pub(crate) fn build_paged_request<E, C>(
35    page: &SinglePage<'_, E>,
36    client: &C,
37) -> Result<(RequestBuilder, Vec<u8>), ApiError<C::Error>>
38where
39    E: Endpoint + Pageable,
40    C: RestClient,
41{
42    let url = page.page_url(client)?;
43    let endpoint = page.inner;
44    build_request_internal(url, endpoint, client)
45}
46
47pub(crate) fn build_request_internal<E, C>(
48    mut url: url::Url,
49    endpoint: &E,
50    client: &C,
51) -> Result<(RequestBuilder, Vec<u8>), ApiError<C::Error>>
52where
53    E: Endpoint,
54    C: RestClient,
55{
56    if endpoint.requires_authentication() && !client.has_api_key() {
57        return Err(ApiError::RequiresAuthentication);
58    }
59
60    endpoint.query_parameters()?.apply_to(&mut url);
61
62    let req = RequestBuilder::new()
63        .method(endpoint.method())
64        .uri(url_to_http_uri(url));
65    if let Some((mime, data)) = endpoint.body()? {
66        let req = req.header(header::CONTENT_TYPE, mime);
67        Ok((req, data))
68    } else {
69        Ok((req, Vec::new()))
70    }
71}
72
73#[derive(Debug, Error)]
74pub enum ResponseError {
75    #[error("Parsing JSON: {0}")]
76    Parse(#[from] serde_json::Error),
77    #[error("Deserializing value: {source}")]
78    DataType {
79        source: serde_json::Error,
80        value: serde_json::Value,
81        typ: &'static str,
82    },
83    #[error("HTTP error: {status}")]
84    HttpStatus {
85        value: serde_json::Value,
86        status: http::StatusCode,
87    },
88}
89
90pub(crate) fn deserialize_response<T>(
91    rsp: http::Response<Bytes>,
92) -> Result<Root<T>, ResponseError>
93where
94    T: DeserializeOwned,
95{
96    let status = rsp.status();
97    let value = serde_json::from_slice(rsp.body())?;
98    if !status.is_success() {
99        return Err(ResponseError::HttpStatus { value, status });
100    }
101
102    serde_json::from_value::<Root<T>>(value.clone()).map_err(|err| ResponseError::DataType {
103        source: err,
104        value,
105        typ: std::any::type_name::<T>(),
106    })
107}