ddnet_account_client/
login.rs

1use ddnet_accounts_shared::{
2    account_server::{errors::AccountServerRequestError, login::LoginError},
3    client::{
4        account_data::AccountDataForClient,
5        login::{self, LoginRequest},
6    },
7};
8use ddnet_accounts_types::account_id::AccountId;
9use thiserror::Error;
10
11use crate::{
12    errors::{FsLikeError, HttpLikeError},
13    interface::Io,
14    safe_interface::{IoSafe, SafeIo},
15};
16
17/// The result of a [`login`] request.
18#[derive(Error, Debug)]
19pub enum LoginResult {
20    /// A http like error occurred.
21    #[error("{0}")]
22    HttpLikeError(HttpLikeError),
23    /// A fs like error occurred.
24    #[error("{0}")]
25    FsLikeError(FsLikeError),
26    /// The account server responded with an error.
27    #[error("{0}")]
28    AccountServerRequstError(AccountServerRequestError<LoginError>),
29    /// Errors that are not handled explicitly.
30    #[error("Login failed: {0}")]
31    Other(anyhow::Error),
32}
33
34impl From<HttpLikeError> for LoginResult {
35    fn from(value: HttpLikeError) -> Self {
36        Self::HttpLikeError(value)
37    }
38}
39
40impl From<FsLikeError> for LoginResult {
41    fn from(value: FsLikeError) -> Self {
42        Self::FsLikeError(value)
43    }
44}
45
46/// Writes the session data to disk
47#[must_use = "This writes the login data and must be used by calling \"write\" of this object"]
48#[derive(Debug)]
49pub struct LoginWriter {
50    account_data: AccountDataForClient,
51}
52
53impl LoginWriter {
54    /// Writes the session data to disk
55    async fn write_impl(self, io: IoSafe<'_>) -> anyhow::Result<(), FsLikeError> {
56        io.write_serialized_session_key_pair(&self.account_data)
57            .await
58    }
59
60    /// Writes the session data to disk
61    pub async fn write(self, io: &dyn Io) -> anyhow::Result<(), FsLikeError> {
62        self.write_impl(io.into()).await
63    }
64}
65
66async fn login_inner_impl(
67    login_req: LoginRequest,
68    login_data: AccountDataForClient,
69    io: IoSafe<'_>,
70) -> anyhow::Result<(AccountId, LoginWriter), LoginResult> {
71    let account_id = io
72        .request_login(login_req)
73        .await?
74        .map_err(LoginResult::AccountServerRequstError)?;
75
76    Ok((
77        account_id,
78        LoginWriter {
79            account_data: login_data,
80        },
81    ))
82}
83
84/// Create a new session (or account if not existed) on the account server.
85pub async fn login_with_account_data(
86    credential_auth_token_hex: String,
87    account_data: &AccountDataForClient,
88    io: &dyn Io,
89) -> anyhow::Result<(AccountId, LoginWriter), LoginResult> {
90    login_with_account_data_impl(credential_auth_token_hex, account_data, io.into()).await
91}
92
93async fn login_with_account_data_impl(
94    credential_auth_token_hex: String,
95    account_data: &AccountDataForClient,
96    io: IoSafe<'_>,
97) -> anyhow::Result<(AccountId, LoginWriter), LoginResult> {
98    let (login_req, login_data) =
99        login::login_from_client_account_data(account_data, credential_auth_token_hex)
100            .map_err(LoginResult::Other)?;
101
102    login_inner_impl(login_req, login_data, io).await
103}
104
105/// Create a new session (or account if not existed) on the account server.
106pub async fn login(
107    credential_auth_token_hex: String,
108    io: &dyn Io,
109) -> anyhow::Result<(AccountId, LoginWriter), LoginResult> {
110    login_impl(credential_auth_token_hex, io.into()).await
111}
112
113async fn login_impl(
114    credential_auth_token_hex: String,
115    io: IoSafe<'_>,
116) -> anyhow::Result<(AccountId, LoginWriter), LoginResult> {
117    let (login_req, login_data) =
118        login::login(credential_auth_token_hex).map_err(LoginResult::Other)?;
119
120    login_inner_impl(login_req, login_data, io).await
121}