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