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
use crate::{
    backend::requests::{Request, EVENT_DAYS_TAG, MODE_TAG, TYPE_TAG, USER_ENDPOINT, USER_TAG},
    models::{GameMode, User},
    Osu, OsuResult,
};

use std::collections::HashMap;

#[derive(Clone, Eq, PartialEq)]
/// Request struct to retrieve users.
/// An instance __must__ contain either a user id or a username
pub struct UserRequest<'s> {
    args: HashMap<&'s str, String>,
}

impl<'s> UserRequest<'s> {
    /// Construct a `UserRequest` via user id
    pub fn with_user_id(id: u32) -> Self {
        let mut args = HashMap::new();
        args.insert(USER_TAG, id.to_string());
        args.insert(TYPE_TAG, "id".to_string());
        Self { args }
    }

    /// Construct a `UserRequest` via username
    pub fn with_username(name: &str) -> Self {
        let mut args = HashMap::new();
        args.insert(USER_TAG, name.replace(" ", "+"));
        args.insert(TYPE_TAG, "string".to_string());
        Self { args }
    }

    /// Specify a game mode for the request
    pub fn mode(mut self, mode: GameMode) -> Self {
        self.args.insert(MODE_TAG, (mode as u8).to_string());
        self
    }

    /// Specify event days for the request.
    ///
    /// From osu!api repo: Max number of days between now and last event date. Range of 1-31. Optional, default value is 1
    pub fn event_days(mut self, amount: u32) -> Self {
        self.args.insert(EVENT_DAYS_TAG, amount.to_string());
        self
    }

    /// Asynchronously send the user request and await the parsed `Vec<User>`.
    /// # Example
    /// ```no_run
    /// # use tokio::runtime::Runtime;
    /// # use rosu::OsuError;
    /// use rosu::{
    ///     backend::{Osu, requests::UserRequest},
    ///     models::User,
    /// };
    ///
    /// # let mut rt = Runtime::new().unwrap();
    /// # rt.block_on(async move {
    /// let osu = Osu::new("osu_api_key");
    /// let request: UserRequest = UserRequest::with_username("Badewanne3");
    /// let users: Vec<User> = request.queue(&osu).await?;
    /// // ...
    /// # Ok::<_, OsuError>(())
    /// # });
    /// ```
    pub async fn queue(self, osu: &Osu) -> OsuResult<Vec<User>> {
        let url = Request::create_url(USER_ENDPOINT, self.args);
        osu.send_request(url).await
    }

    /// Asynchronously send the user request and await the parsed [User][user].
    ///
    /// If the API's response contains more than one user, the method will
    /// return the last one.
    ///
    /// If the API response contains no users, the method will return `None`.
    ///
    /// [user]: ../models/struct.User.html
    /// # Example
    /// ```no_run
    /// # use tokio::runtime::Runtime;
    /// # use rosu::OsuError;
    /// use rosu::{
    ///     backend::{Osu, requests::UserRequest},
    ///     models::User,
    /// };
    ///
    /// # let mut rt = Runtime::new().unwrap();
    /// # rt.block_on(async move {
    /// let osu = Osu::new("osu_api_key");
    /// let request: UserRequest = UserRequest::with_username("Badewanne3");
    /// let user: Option<User> = request.queue_single(&osu).await?;
    /// // ...
    /// # Ok::<_, OsuError>(())
    /// # });
    /// ```
    pub async fn queue_single(self, osu: &Osu) -> OsuResult<Option<User>> {
        Ok(self.queue(osu).await?.pop())
    }
}