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            method,
49            path,
50            token_required,
51        } = route.into_parts();
52
53        Self {
54            inner: Builder::new()
55                .uri(path)
56                .method(method)
57                .extension(TokenRequired(token_required)),
58        }
59    }
60
61    pub fn header<K, V>(self, key: K, value: V) -> Self
62    where
63        K: TryInto<HeaderName>,
64        <K as TryInto<HeaderName>>::Error: Into<http::Error>,
65        V: TryInto<HeaderValue>,
66        <V as TryInto<HeaderValue>>::Error: Into<http::Error>,
67    {
68        Self {
69            inner: self.inner.header(key, value),
70        }
71    }
72
73    pub fn filter(self, filter: Filter) -> Self {
74        Self {
75            inner: self.inner.extension(filter),
76        }
77    }
78
79    pub fn empty(self) -> Result<Request, Error> {
80        build(self.inner, Body::empty())
81    }
82
83    pub fn stream<S>(self, stream: S) -> Result<Request, Error>
84    where
85        S: TryStream + Send + 'static,
86        S::Ok: Into<Bytes>,
87        S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
88    {
89        build(self.inner, Body::from_stream(stream))
90    }
91
92    pub fn form<T: Serialize>(self, form: &T) -> Result<Request, Error> {
93        let body = serde_urlencoded::to_string(form).map_err(error::builder)?;
94        let builder = self.inner.header(
95            CONTENT_TYPE,
96            HeaderValue::from_static("application/x-www-form-urlencoded"),
97        );
98        build(builder, Body::from(body))
99    }
100
101    pub fn multipart(self, form: Form) -> Result<Request, Error> {
102        let mut builder = match HeaderValue::from_maybe_shared(form.content_type()) {
103            Ok(value) => self.inner.header(CONTENT_TYPE, value),
104            Err(_) => self.inner,
105        };
106
107        builder = match form.compute_length() {
108            Some(length) => builder.header(CONTENT_LENGTH, length),
109            None => builder,
110        };
111        build(builder, Body::from_stream(form.stream()))
112    }
113}
114
115#[inline]
116fn build(builder: Builder, body: Body) -> Result<Request, Error> {
117    builder.body(body).map_err(error::builder)
118}