1use 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}