1
// This file is part of hnefatafl-copenhagen.
2
//
3
// hnefatafl-copenhagen is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU Affero General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// hnefatafl-copenhagen is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU Affero General Public License for more details.
12
//
13
// You should have received a copy of the GNU Affero General Public License
14
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
//
16
// SPDX-License-Identifier: AGPL-3.0-or-later
17
// SPDX-FileCopyrightText: 2025 David Campbell <david@hnefatafl.org>
18

            
19
use std::{
20
    collections::{HashMap, HashSet},
21
    fmt,
22
};
23

            
24
use crate::{email::Email, glicko::Rating};
25
use jiff::Timestamp;
26
use serde::{Deserialize, Serialize};
27

            
28
use crate::Id;
29

            
30
impl Accounts {
31
    /// # Errors
32
    ///
33
    /// If serialization fails.
34
    pub fn display_admin(&self) -> anyhow::Result<String> {
35
        let string = ron::ser::to_string(&self)?;
36

            
37
        Ok(string)
38
    }
39
}
40

            
41
impl fmt::Display for Accounts {
42
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43
        let mut accounts = Vec::new();
44
        for (name, account) in &self.0 {
45
            accounts.push(format!("{name} {account}"));
46
        }
47
        accounts.sort_unstable();
48
        let accounts = accounts.join(" ");
49

            
50
        write!(f, "{accounts}")
51
    }
52
}
53

            
54
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
55
pub struct Account {
56
    #[serde(default)]
57
    pub email: Option<Email>,
58
    /// A unix timestamp in seconds when the email was sent.
59
    #[serde(default)]
60
    pub email_sent: i64,
61
    #[serde(default)]
62
    pub password: String,
63
    /// If logged in, holds the index into clients.
64
    #[serde(default)]
65
    pub logged_in: Option<usize>,
66
    #[serde(default)]
67
    pub draws: u64,
68
    #[serde(default)]
69
    pub wins: u64,
70
    #[serde(default)]
71
    pub losses: u64,
72
    #[serde(default)]
73
    pub rating: Rating,
74
    #[serde(default)]
75
    pub send_emails: bool,
76
    #[serde(skip)]
77
    pub pending_games: HashSet<Id>,
78
    #[serde(default)]
79
    pub creation_date: DateTimeUtc,
80
    #[serde(default)]
81
    pub last_logged_in: DateTimeUtc,
82
}
83

            
84
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
85
pub struct DateTimeUtc(pub Timestamp);
86

            
87
impl Default for DateTimeUtc {
88
42
    fn default() -> Self {
89
42
        Self(Timestamp::now())
90
42
    }
91
}
92

            
93
impl fmt::Display for Account {
94
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95
        if self.logged_in.is_some() {
96
            write!(
97
                f,
98
                "{} {} {} {} logged_in",
99
                self.wins, self.losses, self.draws, self.rating
100
            )
101
        } else {
102
            write!(
103
                f,
104
                "{} {} {} {} logged_out",
105
                self.wins, self.losses, self.draws, self.rating
106
            )
107
        }
108
    }
109
}
110

            
111
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
112
pub struct Accounts(pub HashMap<String, Account>);