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
//! Functionality dealing with an account's friends

use crate::client::SteamClient;
use crate::errors::SteamError;
use serde::Deserialize;
use std::fmt::Formatter;

// The "GetFriendList (v0001)" endpoint
const ENDPOINT_GET_FRIENDLIST: &str = "https://api.steampowered.com/ISteamUser/GetFriendList/v0001";

/// Helper struct used during deserializing the API response.
#[derive(Debug, Deserialize)]
struct FriendsResponse {
    /// A list of [`Friend`]s
    #[serde(rename(deserialize = "friendslist"))]
    response: Option<FriendsList>,
}

/// This is the response that comes from the GetFriendList API.
#[derive(Debug, Default, Deserialize)]
pub struct FriendsList {
    /// A list of [`Friend`]s
    pub friends: Vec<Friend>,
}

impl From<FriendsResponse> for FriendsList {
    fn from(value: FriendsResponse) -> Self {
        let v = value.response.unwrap_or_default();
        Self { friends: v.friends }
    }
}

/// Represents a Steam friend and its metadata
#[derive(Debug, Deserialize, PartialEq)]
pub struct Friend {
    /// The friend's Steam ID
    #[serde(rename(deserialize = "steamid"))]
    pub steam_id: String,
    /// The relationship you have with the Steam user
    pub relationship: SteamRelationship,
    /// Unix timestamp of the time when the relationship was created.
    pub friend_since: i64,
}

impl std::fmt::Display for Friend {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Friend ID: {}, friends since {}",
            self.steam_id, self.friend_since
        )
    }
}

/// Enumeration of possible relationship qualifiers.
#[derive(Debug, Deserialize, PartialEq)]
#[non_exhaustive]
pub enum SteamRelationship {
    /// You are friends with that person
    #[serde(rename(deserialize = "friend"))]
    Friend,
}

impl SteamClient {
    /// Gets all friends from the user with the provided Steam ID.
    ///
    /// Example:
    ///
    /// ```no_run
    /// # use steamr::client::SteamClient;
    /// # use steamr::errors::SteamError;
    ///
    /// fn main() -> Result<(), SteamError> {
    ///     let steam_client = SteamClient::from("an-api-key".to_string());
    ///     let steam_friends = steam_client.get_friends("some-steam-ID")?;
    ///
    ///     // Print friends
    ///     steam_friends.iter().for_each(|f| println!("{}", f));
    ///     
    ///     Ok(())
    ///  }
    /// ```
    ///
    ///  The standard format of "friends since" is the UNIX timestamp, you might want to get a
    ///  more intuitive time format. You could use the `chrono` crate for this:
    /// ```ignore
    ///  let steam_friends = steam_client.get_friends("some-steam-ID")?;
    ///  steam_friends.iter().for_each(|f| {
    ///      println!(
    ///          "me and {} are friends since {}",
    ///          f.steam_id,
    ///          chrono::NaiveDateTime::from_timestamp(f.friend_since, 0)
    ///      )
    ///  });
    /// ```
    pub fn get_friends(&self, steam_id: &str) -> Result<Vec<Friend>, SteamError> {
        let response = self.get_request(
            ENDPOINT_GET_FRIENDLIST,
            vec![("steamid", steam_id), ("relationship", "friend")],
        )?;

        let friends_list = self
            .parse_response::<FriendsResponse, FriendsList>(response)
            .unwrap();
        Ok(friends_list.friends)
    }
}