1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! Authentication and user management related API objects.

use serde::{Deserialize, Serialize};

use crate::{
    ensure,
    validation::{self, Validatable},
};

pub const MIN_PASSWORD_LEN: usize = 6;
pub const MAX_PASSWORD_LEN: usize = 30;
pub const MAX_USERNAME_LEN: usize = 32;

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Token {
    token: String,
}

impl Token {
    pub fn new(token: String) -> Self {
        Self { token }
    }

    pub fn token(&self) -> &str {
        self.token.as_str()
    }
}

/// Username & password to be used while signing in.
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UsernameAndPassword {
    username: String,
    password: String,
}

impl UsernameAndPassword {
    pub fn new(username: String, password: String) -> Self {
        Self { username, password }
    }

    pub fn username(&self) -> &str {
        self.username.as_str()
    }

    pub fn password(&self) -> &str {
        self.password.as_str()
    }
}

/// User object combined with a password. To be used while signing up.
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserWithPassword {
    password: String,
    user: User,
}

impl UserWithPassword {
    pub fn new(password: String, user: User) -> Self {
        Self { password, user }
    }

    pub fn user(&self) -> &User {
        &self.user
    }

    pub fn password(&self) -> &str {
        self.password.as_str()
    }

    pub fn validate(&self) -> validation::Result {
        self.user.validate()?;

        ensure!(
            self.password.len() >= MIN_PASSWORD_LEN,
            "Password must have at least {} characters.",
            MIN_PASSWORD_LEN
        );

        ensure!(
            self.password.len() <= MAX_PASSWORD_LEN,
            "Password must have at most {} bytes.",
            MAX_PASSWORD_LEN
        );

        Ok(())
    }
}

/// A complete user info.
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct User {
    username: String,
}

impl User {
    pub fn new(username: String) -> Self {
        Self { username }
    }

    pub fn username(&self) -> &str {
        self.username.as_str()
    }
}

impl Validatable for User {
    fn validate(&self) -> validation::Result {
        ensure!(!self.username.is_empty(), "Empty username is not allowed.");
        ensure!(
            self.username.trim().len() == self.username.len(),
            "Username starting or ending with whitespace is not allowed."
        );
        ensure!(
            self.username.len() <= MAX_USERNAME_LEN,
            "Username has {} characters, which is more than the limit of {} characters.",
            self.username.len(),
            MAX_USERNAME_LEN
        );
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_validate_user() {
        let mut user = UserWithPassword {
            password: "short".to_owned(),
            user: User {
                username: "Indy".to_owned(),
            },
        };
        assert_eq!(
            user.validate().err().unwrap().to_string(),
            "Password must have at least 6 characters."
        );

        user.password = "Long-enough-pwd".to_string();
        assert!(user.validate().is_ok());

        user.user.username = "Indy ".to_string();
        assert_eq!(
            user.validate().err().unwrap().to_string(),
            "Username starting or ending with whitespace is not allowed."
        );
    }
}