speedrun_api/api/
utils.rs1use 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}