modio/request/
mod.rs

1//! Typed request builders for the different endpoints.
2
3use bytes::Bytes;
4use futures_util::TryStream;
5use http::header::{HeaderName, HeaderValue, CONTENT_LENGTH, CONTENT_TYPE};
6use http::request::Builder;
7use serde::ser::Serialize;
8
9use crate::error::{self, Error};
10use crate::response::Response;
11
12use self::body::Body;
13use self::multipart::Form;
14use self::routing::Parts;
15pub(crate) use self::routing::Route;
16use self::util::ArrayParams;
17
18pub use self::filter::Filter;
19pub use self::submit_report::SubmitReport;
20
21pub(crate) mod body;
22mod multipart;
23mod routing;
24mod submit_report;
25mod util;
26
27pub mod auth;
28#[macro_use]
29pub mod filter;
30pub mod files;
31pub mod games;
32pub mod mods;
33pub mod user;
34
35pub(crate) type Request = http::Request<Body>;
36pub(crate) type Output<T> = Result<Response<T>, Error>;
37
38#[derive(Clone, Copy, Debug)]
39pub(crate) struct TokenRequired(pub bool);
40
41pub(crate) struct RequestBuilder {
42    inner: Builder,
43}
44
45impl RequestBuilder {
46    pub fn from_route(route: &Route) -> Self {
47        let Parts {
48            game_id,
49            method,
50            path,
51            token_required,
52        } = route.into_parts();
53
54        let mut builder = Builder::new()
55            .uri(path)
56            .method(method)
57            .extension(TokenRequired(token_required));
58
59        if let Some(game_id) = game_id {
60            builder = builder.extension(game_id);
61        }
62
63        Self { inner: builder }
64    }
65
66    pub fn header<K, V>(self, key: K, value: V) -> Self
67    where
68        K: TryInto<HeaderName>,
69        <K as TryInto<HeaderName>>::Error: Into<http::Error>,
70        V: TryInto<HeaderValue>,
71        <V as TryInto<HeaderValue>>::Error: Into<http::Error>,
72    {
73        Self {
74            inner: self.inner.header(key, value),
75        }
76    }
77
78    pub fn filter(self, filter: Filter) -> Self {
79        Self {
80            inner: self.inner.extension(filter),
81        }
82    }
83
84    pub fn empty(self) -> Result<Request, Error> {
85        build(self.inner, Body::empty())
86    }
87
88    pub fn stream<S>(self, stream: S) -> Result<Request, Error>
89    where
90        S: TryStream + Send + 'static,
91        S::Ok: Into<Bytes>,
92        S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
93    {
94        build(self.inner, Body::from_stream(stream))
95    }
96
97    pub fn form<T: Serialize>(self, form: &T) -> Result<Request, Error> {
98        let body = serde_urlencoded::to_string(form).map_err(error::builder)?;
99        let builder = self.inner.header(
100            CONTENT_TYPE,
101            HeaderValue::from_static("application/x-www-form-urlencoded"),
102        );
103        build(builder, Body::from(body))
104    }
105
106    pub fn multipart(self, form: Form) -> Result<Request, Error> {
107        let mut builder = match HeaderValue::from_maybe_shared(form.content_type()) {
108            Ok(value) => self.inner.header(CONTENT_TYPE, value),
109            Err(_) => self.inner,
110        };
111
112        builder = match form.compute_length() {
113            Some(length) => builder.header(CONTENT_LENGTH, length),
114            None => builder,
115        };
116        build(builder, Body::from_stream(form.stream()))
117    }
118}
119
120#[inline]
121fn build(builder: Builder, body: Body) -> Result<Request, Error> {
122    builder.body(body).map_err(error::builder)
123}