nadeo_api/client/
client_builder.rs

1use crate::auth::o_auth::OAuthInfo;
2use crate::auth::{AuthInfo, AuthType};
3use crate::request::metadata::MetaData;
4use crate::Result;
5use crate::{auth, Error, NadeoClient};
6use futures::future::join3;
7use reqwest::Client;
8use thiserror::Error;
9
10type EMail = String;
11type Username = String;
12type Password = String;
13type Identifier = String;
14type Secret = String;
15
16#[derive(Debug, Clone, Default)]
17pub struct NadeoClientBuilder {
18    normal_auth: Option<(EMail, Password)>,
19    server_auth: Option<(Username, Password)>,
20    o_auth: Option<(Identifier, Secret)>,
21    user_agent: Option<String>,
22}
23
24impl NadeoClientBuilder {
25    /// Adds credentials for using [`AuthType::NadeoServices`] and [`AuthType::NadeoLiveServices`].
26    pub fn with_normal_auth(mut self, email: &str, password: &str) -> Self {
27        self.normal_auth = Some((email.to_string(), password.to_string()));
28
29        self
30    }
31
32    /// Adds credentials for using [`AuthType::NadeoServices`] and [`AuthType::NadeoLiveServices`] using a server account.
33    /// [`NadeoClientBuilder`] will prefer [`NadeoClientBuilder::with_normal_auth`] if `with_normal_auth` and `with_server_auth` are added.
34    pub fn with_server_auth(mut self, username: &str, password: &str) -> Self {
35        self.server_auth = Some((username.to_string(), password.to_string()));
36
37        self
38    }
39
40    /// Adds credentials for using [`AuthType::OAuth`].
41    pub fn with_oauth(mut self, identifier: &str, secret: &str) -> Self {
42        self.o_auth = Some((identifier.to_string(), secret.to_string()));
43
44        self
45    }
46
47    /// Adds a UserAgent which is sent along with each [`NadeoRequest`].
48    /// This is required because Ubisoft blocks some default UserAgents.
49    /// An example of a *good* UserAgent is:
50    /// - `My amazing app / my.email.address@gmail.com`
51    ///
52    /// # Examples
53    ///
54    /// ```rust
55    /// # use nadeo_api::NadeoClient;
56    /// let client = NadeoClient::builder()
57    ///     .with_normal_auth("my_email", "my_password")
58    ///     .user_agent("API Testing / mustermann.max@gmail.com") // not a real email
59    ///     .build()
60    ///     .await?;
61    /// ```
62    ///
63    /// [`NadeoRequest`]: crate::NadeoRequest
64    pub fn user_agent(mut self, user_agent: &str) -> Self {
65        self.user_agent = Some(user_agent.to_string());
66
67        self
68    }
69
70    /// Trys to build a [`NadeoClient`].
71    pub async fn build(self) -> Result<NadeoClient> {
72        if self.o_auth.is_none() && self.normal_auth.is_none() && self.server_auth.is_none() {
73            return Err(Error::from(NadeoClientBuilderError::MissingCredentials));
74        }
75        if self.user_agent.is_none() {
76            return Err(Error::from(NadeoClientBuilderError::MissingUserAgent));
77        }
78
79        let meta_data = MetaData {
80            user_agent: self.user_agent.unwrap(),
81        };
82
83        let client = Client::new();
84
85        // Ubisoft auth ticket
86        let mut ticket = String::new();
87        if let Some(ref auth) = self.normal_auth {
88            ticket = auth::get_ubi_auth_ticket(&auth.0, &auth.1, &meta_data, &client).await?;
89        }
90
91        // NadeoServices
92        let normal_auth_future = async {
93            if self.normal_auth.is_some() {
94                Some(AuthInfo::new(AuthType::NadeoServices, &ticket, &meta_data, &client).await)
95            } else if let Some((ref username, ref password)) = self.server_auth {
96                Some(
97                    AuthInfo::new_server(
98                        AuthType::NadeoServices,
99                        &meta_data,
100                        username,
101                        password,
102                        &client,
103                    )
104                    .await,
105                )
106            } else {
107                None
108            }
109        };
110        // NadeoLiveServices
111        let live_auth_future = async {
112            if self.normal_auth.is_some() {
113                Some(AuthInfo::new(AuthType::NadeoLiveServices, &ticket, &meta_data, &client).await)
114            } else if let Some((ref username, ref password)) = self.server_auth {
115                Some(
116                    AuthInfo::new_server(
117                        AuthType::NadeoLiveServices,
118                        &meta_data,
119                        username,
120                        password,
121                        &client,
122                    )
123                    .await,
124                )
125            } else {
126                None
127            }
128        };
129        // OAuth
130        let oauth_future = async {
131            if let Some(auth) = self.o_auth {
132                Some(OAuthInfo::new(&auth.0, &auth.1, &client).await)
133            } else {
134                None
135            }
136        };
137        // execute requests
138        let (normal_auth_res, live_auth_res, oauth_res) =
139            join3(normal_auth_future, live_auth_future, oauth_future).await;
140
141        let mut normal_auth = None;
142        let mut live_auth = None;
143        let mut o_auth = None;
144
145        // extract results
146        if let Some(auth) = normal_auth_res {
147            normal_auth = Some(auth?);
148        }
149        if let Some(auth) = live_auth_res {
150            live_auth = Some(auth?);
151        }
152        if let Some(auth) = oauth_res {
153            o_auth = Some(auth?);
154        }
155
156        Ok(NadeoClient {
157            client,
158            normal_auth,
159            live_auth,
160            o_auth,
161            meta_data,
162        })
163    }
164}
165
166#[derive(Error, Debug)]
167pub enum NadeoClientBuilderError {
168    #[error("No credentials were provided. At least 1 auth method is required")]
169    MissingCredentials,
170    #[error("No UserAgent was provided")]
171    MissingUserAgent,
172}