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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

use thiserror::Error;
use crate::types::*;

/// Helper to request users with filters like gender, nationalities, etc.
pub struct UserGeneratorBuilder {
    req: reqwest::RequestBuilder
}

impl UserGeneratorBuilder {
    pub(crate) fn new(req: reqwest::RequestBuilder) -> Self {
        Self { req }
    }
    /// Request a specific gender
    pub fn gender(self, gender: Gender) -> Self {
        Self::new(self.req.query(&[("gender", serde_json::to_value(gender).unwrap().as_str())]))
    }

    /// Request a specific nationality
    pub fn nationality(self, nationality: Nationality) -> Self {
        Self::new(self.req.query(&[("nat", serde_json::to_value(nationality).unwrap().as_str())]))
    }

    /// Request specific nationalities, picked at random between each user
    pub fn nationalities(self, nationalities: &[Nationality]) -> Self {
        let mut nats = String::new();
        for nat in nationalities {
            nats += serde_json::to_value(nat).unwrap().as_str().unwrap();
            nats.push(',');
        }
        nats.pop();
        Self::new(self.req.query(&[("nat", nats)]))
    }

    /// Request with a specified seed, allow to always generate the same users
    ///
    /// ### Warning:
    /// May discard other filters
    pub fn seed(self, seed: &str) -> Self {
        Self::new(self.req.query(&[("seed", seed)]))
    }

    /// Request a user with specific password rules
    ///
    /// Format, without spaces:
    /// `CHARSETS,MIN_LENGTH-MAX_LENGTH`
    /// or
    /// `CHARSETS,MAX_LENGTH`
    ///
    /// You can mix and match the charsets below for the CHARSETS option above:
    /// ```md
    /// special    !"#$%&'()*+,- ./:;<=>?@[\]^_`{|}~
    /// upper      ABCDEFGHIJKLMNOPQRSTUVWXYZ
    /// lower      abcdefghijklmnopqrstuvwxyz
    /// number     0123456789
    /// ```
    /// `MIN_LENGTH` and `MAX_LENGTH` are the min/max length of the passwords that you want to generate.
    /// By default, passwords will be between 8 - 64 characters long.
    ///
    /// ## Example:
    /// ```
    /// // Get a user with a password composed with 8 upper and/or lower characters
    /// let user = generator.get().password("upper,lower,8").fetch_one().await?
    /// ```
    pub fn password(self, charset: &str) -> Self {
        Self::new(self.req.query(&[("password", charset)]))
    }

    /// Generate users with the api informations
    pub async fn fetch_with_info(self, count: usize) -> Result<RandomUserResult> {
        self.count(count).request().await
    }

    /// Generate users
    pub async fn fetch(self, count: usize) -> Result<Vec<RandomUser>> {
        Ok(self.count(count).request().await?.results)
    }

    /// Generate 1 user
    pub async fn fetch_one(self) -> Result<RandomUser> {
        Ok(self.fetch(1).await?.remove(0))
    }

    fn count(self, count: usize) -> Self {
        Self::new(self.req.query(&[("results", count)]))
    }

    async fn request(self) -> Result<RandomUserResult> {
        let api_rsp = self.req.send().await?;
        let rsp = Self::parse_response(api_rsp).await?;
        match rsp {
            RandomUserResponse::Error(e) => Err(RandomUserError::Api(e)),
            RandomUserResponse::Result(res) => Ok(res),
        }
    }

    async fn parse_response(response: reqwest::Response) -> Result<RandomUserResponse> {
        let content_type = response
            .headers()
            .get("content-type")
            .ok_or(RandomUserError::BadFormat)?
            .to_str()
            .map_err(|_| RandomUserError::BadFormat)?
            .to_owned();
        let text = response.text().await?;
        match content_type {
            ct if ct.contains("text/plain") => Ok(RandomUserResponse::Error(text)),
            ct if ct.contains("application/json") => serde_json::from_str::<RandomUserResponse>(&text).map_err(|_| RandomUserError::BadFormat),
            _ => Err(RandomUserError::BadFormat),
        }
    }
}

/// Random user generator
///
/// ## Example:
/// ```
/// let generator = UserGenerator::new();
///
/// let user = generator.fetch_one().await?
///
/// println!("{:#?}", user);
/// ```
pub struct UserGenerator {
    client: reqwest::Client,
}

impl UserGenerator {
    const API_URL: &str = "https://randomuser.me/api/1.4/";

    pub fn new() -> UserGenerator {
        UserGenerator { client: reqwest::Client::new() }
    }

    /// Start the request to easily apply filters
    pub fn get(&self) -> UserGeneratorBuilder {
        UserGeneratorBuilder::new(
            self.client.get(Self::API_URL)
        )
    }

    /// Generate users with the api informations
    ///
    /// ## Example:
    /// ```
    /// // Fetch 5 random users with api info
    /// let users = generator.fetch_with_info(5).await?
    ///
    /// println!("{:?}", users.info);
    /// for user in users.results {
    ///     prinln!("{user:?}");
    /// }
    ///
    /// ```
    pub async fn fetch_with_info(&self, count: usize) -> Result<RandomUserResult> {
        self.get().fetch_with_info(count).await
    }

    /// Generate users
    ///
    /// ## Example:
    /// ```
    /// // Fetch 5 random users
    /// let users = generator.fetch(5).await?
    ///
    /// for user in users {
    ///     prinln!("{user:?}");
    /// }
    ///
    /// ```
    pub async fn fetch(&self, count: usize) -> Result<Vec<RandomUser>> {
        self.get().fetch(count).await
    }

    /// Generate a user
    ///
    /// ## Example:
    /// ```
    /// let user = generator.fetch_one().await?
    /// println("{user:?}");
    /// ```
    pub async fn fetch_one(&self) -> Result<RandomUser> {
        self.get().fetch_one().await
    }
}

impl Default for UserGenerator {
    fn default() -> Self {
        Self::new()
    }
}

type Result<T> = std::result::Result<T, RandomUserError>;

#[derive(Debug, Error)]
pub enum RandomUserError {
    #[error("Reqwest error: {0}")]
    Reqwest(#[from] reqwest::Error),
    #[error("Api error: {0}")]
    Api(String),
    #[error("Bad format")]
    BadFormat,
}