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
//! Interpreting login responses.
use std::marker::PhantomData;
use std::borrow::Cow;

use data;
use error::{ApiError, Result};

use {EndpointResult, TokenStorage};

/// Login details
#[derive(Serialize, Clone, Hash, Debug)]
pub struct Details<'a> {
    /// The email or username to log in with (either works)
    pub email: Cow<'a, str>,
    /// The password to log in with (steam auth is not supported)
    pub password: Cow<'a, str>,
}

impl<'a> Details<'a> {
    /// Create a new login details with the given username and password
    pub fn new<T, U>(email: T, password: U) -> Self
    where
        T: Into<Cow<'a, str>>,
        U: Into<Cow<'a, str>>,
    {
        Details {
            email: email.into(),
            password: password.into(),
        }
    }
}

/// Login raw result.
#[derive(Deserialize, Clone, Hash, Debug)]
#[doc(hidden)]
pub struct Response {
    ok: i32,
    token: Option<String>,
}

/// The result of a call to log in.
#[must_use = "LoggedIn does not do anything unless registered in a token store"]
#[derive(Clone, Hash, Debug)]
pub struct LoggedIn {
    /// The token which can be used to make future authenticated API calls.
    pub token: String,
    /// Phantom data in order to allow adding any additional fields in the future.
    _phantom: PhantomData<()>,
}

impl LoggedIn {
    /// Stores the token into the given token storage.
    pub fn return_to<T>(self, storage: &T)
    where
        T: TokenStorage,
    {
        storage.return_token(self.token);
    }
}

impl EndpointResult for LoggedIn {
    type RequestResult = Response;
    type ErrorResult = data::ApiError;

    fn from_raw(raw: Response) -> Result<LoggedIn> {
        let Response { ok, token } = raw;

        if ok != 1 {
            return Err(ApiError::NotOk(ok).into());
        }
        match token {
            Some(token) => Ok(LoggedIn {
                token: token,
                _phantom: PhantomData,
            }),
            None => Err(ApiError::MissingField("token").into()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::LoggedIn;
    use EndpointResult;
    use serde_json;

    fn test_parse(json: serde_json::Value) {
        let response = serde_json::from_value(json).unwrap();

        let _ = LoggedIn::from_raw(response).unwrap();
    }

    #[test]
    fn parse_sample_login_success() {
        test_parse(json! ({
            "ok": 1,
            "token": "c07924d3f556a355eba7cd59f4c21f670fda76c2",
        }));
    }
}