modio/request/auth/
external_auth.rs1use std::future::IntoFuture;
2
3use serde::ser::Serialize;
4use serde_derive::Serialize;
5
6use crate::client::Client;
7use crate::request::{Output, RequestBuilder, Route};
8use crate::response::ResponseFuture;
9use crate::types::auth::AccessToken;
10
11pub struct ExternalAuth<'a, T> {
13 http: &'a Client,
14 fields: ExternalAuthFields<'a, T>,
15}
16
17#[derive(Serialize)]
18struct ExternalAuthFields<'a, T> {
19 #[serde(skip_serializing_if = "Option::is_none")]
20 email: Option<&'a str>,
21 #[serde(rename = "date_expires", skip_serializing_if = "Option::is_none")]
22 expired_at: Option<u64>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 terms_agreed: Option<bool>,
25 #[serde(flatten)]
26 others: T,
27}
28
29#[derive(Serialize)]
30pub struct Discord<'a> {
31 #[serde(rename = "discord_token")]
32 token: &'a str,
33}
34
35impl<'a> Discord<'a> {
36 pub const fn new(token: &'a str) -> Self {
37 Self { token }
38 }
39}
40
41#[derive(Serialize)]
42pub struct EpicGames<'a> {
43 #[serde(rename = "id_token")]
44 token: &'a str,
45}
46
47impl<'a> EpicGames<'a> {
48 pub const fn new(token: &'a str) -> Self {
49 Self { token }
50 }
51}
52
53#[derive(Serialize)]
54pub struct Google<'a> {
55 #[serde(rename = "id_token")]
56 token: &'a str,
57}
58
59impl<'a> Google<'a> {
60 pub const fn new(token: &'a str) -> Self {
61 Self { token }
62 }
63}
64
65#[derive(Serialize)]
66pub struct MetaQuest<'a> {
67 device: &'a str,
68 nonce: &'a str,
69 user_id: u64,
70 access_token: &'a str,
71}
72
73impl<'a> MetaQuest<'a> {
74 pub const fn new(device: &'a str, nonce: &'a str, user_id: u64, access_token: &'a str) -> Self {
75 Self {
76 device,
77 nonce,
78 user_id,
79 access_token,
80 }
81 }
82}
83
84#[derive(Serialize)]
85pub struct OpenID<'a> {
86 #[serde(rename = "id_token")]
87 token: &'a str,
88}
89
90impl<'a> OpenID<'a> {
91 pub fn new(token: &'a str) -> Self {
92 Self { token }
93 }
94}
95
96#[derive(Serialize)]
97pub struct PSN<'a> {
98 auth_code: &'a str,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 env: Option<u32>,
101}
102
103impl<'a> PSN<'a> {
104 pub fn new(auth_code: &'a str) -> Self {
105 Self {
106 auth_code,
107 env: None,
108 }
109 }
110}
111
112#[derive(Serialize)]
113pub struct Steam<'a> {
114 appdata: &'a str,
115}
116
117impl<'a> Steam<'a> {
118 pub const fn new(appdata: &'a str) -> Self {
119 Self { appdata }
120 }
121}
122
123#[derive(Serialize)]
124pub struct Switch<'a> {
125 #[serde(rename = "id_token")]
126 token: &'a str,
127}
128
129impl<'a> Switch<'a> {
130 pub const fn new(token: &'a str) -> Self {
131 Self { token }
132 }
133}
134
135#[derive(Serialize)]
136pub struct Xbox<'a> {
137 #[serde(rename = "xbox_token")]
138 token: &'a str,
139}
140
141impl<'a> Xbox<'a> {
142 pub const fn new(token: &'a str) -> Self {
143 Self { token }
144 }
145}
146
147impl<'a, T> ExternalAuth<'a, T> {
148 pub(crate) const fn new(http: &'a Client, others: T) -> Self {
149 Self {
150 http,
151 fields: ExternalAuthFields {
152 email: None,
153 expired_at: None,
154 terms_agreed: None,
155 others,
156 },
157 }
158 }
159
160 pub const fn email(mut self, email: &'a str) -> Self {
161 self.fields.email = Some(email);
162 self
163 }
164
165 pub const fn expired_at(mut self, expired_at: u64) -> Self {
166 self.fields.expired_at = Some(expired_at);
167 self
168 }
169
170 pub const fn terms_agreed(mut self, terms_agreed: bool) -> Self {
171 self.fields.terms_agreed = Some(terms_agreed);
172 self
173 }
174}
175
176impl<'a> ExternalAuth<'a, PSN<'a>> {
177 pub const fn env(mut self, env: u32) -> Self {
178 self.fields.others.env = Some(env);
179 self
180 }
181}
182
183pub trait Provider {
184 fn route() -> Route;
185}
186
187macro_rules! impl_provider {
188 ($($Type:ident: $route:expr)*) => {
189 $(impl<'a> Provider for $Type<'a> {
190 fn route() -> Route {
191 $route
192 }
193 })*
194 };
195}
196
197impl_provider! {
198 Discord: Route::ExternalAuthDiscord
199 EpicGames: Route::ExternalAuthEpic
200 Google: Route::ExternalAuthGoogle
201 MetaQuest: Route::ExternalAuthMeta
202 OpenID: Route::ExternalAuthOpenID
203 PSN: Route::ExternalAuthPSN
204 Steam: Route::ExternalAuthSteam
205 Switch: Route::ExternalAuthSwitch
206 Xbox: Route::ExternalAuthXbox
207}
208
209impl<T: Serialize + Provider> IntoFuture for ExternalAuth<'_, T> {
210 type Output = Output<AccessToken>;
211 type IntoFuture = ResponseFuture<AccessToken>;
212
213 fn into_future(self) -> Self::IntoFuture {
214 let route = T::route();
215 match RequestBuilder::from_route(&route).form(&self.fields) {
216 Ok(req) => self.http.request(req),
217 Err(err) => ResponseFuture::failed(err),
218 }
219 }
220}