modio/client/
builder.rs

1use http::header::{HeaderMap, HeaderValue, USER_AGENT};
2use http::uri::Authority;
3
4use crate::error::{self, Error};
5use crate::types::id::{GameId, UserId};
6use crate::types::{TargetPlatform, TargetPortal};
7
8use super::{Client, DEFAULT_HOST, HDR_X_MODIO_PLATFORM, HDR_X_MODIO_PORTAL, TEST_HOST};
9
10use super::service::Svc;
11
12/// A builder for [`Client`].
13pub struct Builder {
14    host: Option<Box<str>>,
15    api_key: Box<str>,
16    token: Option<Box<str>>,
17    headers: HeaderMap,
18    error: Option<Error>,
19}
20
21impl Builder {
22    /// Create a new builder with an API key.
23    pub fn new(api_key: String) -> Self {
24        Self {
25            host: None,
26            api_key: api_key.into_boxed_str(),
27            token: None,
28            headers: HeaderMap::new(),
29            error: None,
30        }
31    }
32
33    /// Build the [`Client`].
34    pub fn build(self) -> Result<Client, Error> {
35        if let Some(e) = self.error {
36            return Err(e);
37        }
38
39        let http = Svc::new();
40
41        let host = self.host.unwrap_or_else(|| Box::from(DEFAULT_HOST));
42
43        Ok(Client {
44            http,
45            host,
46            api_key: self.api_key,
47            token: self.token,
48            headers: self.headers,
49        })
50    }
51
52    /// Set the token to use for HTTP requests.
53    pub fn token(mut self, token: String) -> Self {
54        self.token = Some(create_token(token));
55        self
56    }
57
58    /// Use the default mod.io API host (`"api.mod.io"`).
59    pub fn use_default_env(mut self) -> Self {
60        self.host = Some(Box::from(DEFAULT_HOST));
61        self
62    }
63
64    /// Use the mod.io API test host (`"api.test.mod.io"`).
65    pub fn use_test_env(mut self) -> Self {
66        self.host = Some(Box::from(TEST_HOST));
67        self
68    }
69
70    /// Set the mod.io API host to "g-{id}.modapi.io".
71    pub fn game_host(mut self, game_id: GameId) -> Self {
72        self.host = Some(format!("g-{game_id}.modapi.io").into_boxed_str());
73        self
74    }
75
76    /// Set the mod.io API host to "u-{id}.modapi.io".
77    pub fn user_host(mut self, user_id: UserId) -> Self {
78        self.host = Some(format!("u-{user_id}.modapi.io").into_boxed_str());
79        self
80    }
81
82    /// Set the mod.io API host.
83    ///
84    /// Defaults to `"api.mod.io"` if not set.
85    pub fn host<V>(mut self, host: V) -> Self
86    where
87        V: TryInto<Authority>,
88        V::Error: Into<http::Error>,
89    {
90        match host.try_into() {
91            Ok(host) => {
92                self.host = Some(Box::from(host.as_str()));
93            }
94            Err(err) => {
95                self.error = Some(error::builder(err.into()));
96            }
97        }
98        self
99    }
100
101    /// Set the user agent used for every request.
102    pub fn user_agent<V>(mut self, value: V) -> Self
103    where
104        V: TryInto<HeaderValue>,
105        V::Error: Into<http::Error>,
106    {
107        match value.try_into() {
108            Ok(value) => {
109                self.headers.insert(USER_AGENT, value);
110            }
111            Err(err) => {
112                self.error = Some(error::builder(err.into()));
113            }
114        }
115        self
116    }
117
118    /// Set the target platform.
119    ///
120    /// See the [mod.io docs](https://docs.mod.io/restapiref/#targeting-a-platform) for more information.
121    pub fn target_platform(mut self, platform: TargetPlatform) -> Self {
122        match HeaderValue::from_str(platform.as_str()) {
123            Ok(value) => {
124                self.headers.insert(HDR_X_MODIO_PLATFORM, value);
125            }
126            Err(err) => {
127                self.error = Some(error::builder(err));
128            }
129        }
130        self
131    }
132
133    /// Set the target portal.
134    ///
135    /// See the [mod.io docs](https://docs.mod.io/restapiref/#targeting-a-portal) for more information.
136    pub fn target_portal(mut self, portal: TargetPortal) -> Self {
137        match HeaderValue::from_str(portal.as_str()) {
138            Ok(value) => {
139                self.headers.insert(HDR_X_MODIO_PORTAL, value);
140            }
141            Err(err) => {
142                self.error = Some(error::builder(err));
143            }
144        }
145        self
146    }
147}
148
149pub(super) fn create_token(mut token: String) -> Box<str> {
150    if !token.starts_with("Bearer ") {
151        token.insert_str(0, "Bearer ");
152    }
153    token.into_boxed_str()
154}
155
156#[cfg(test)]
157mod tests {
158    use super::create_token;
159
160    #[test]
161    fn test_create_token() {
162        assert_eq!("Bearer token", &*create_token("token".to_owned()));
163        assert_eq!("Bearer token", &*create_token("Bearer token".to_owned()));
164    }
165}