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
//! Endpoint functions relating to albums.

use crate::*;
use serde::Deserialize;

/// Get information about an album.
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/albums/get-album/).
pub async fn get_album(
    token: &AccessToken,
    id: &str,
    market: Option<Market>,
) -> Result<Album, EndpointError<Error>> {
    Ok(request!(
        token,
        GET "/v1/albums/{}",
        path_params = [id],
        optional_query_params = {"market": market.map(|m| m.as_str())},
        ret = Album
    ))
}

/// Get information about several albums.
///
/// Maximum number of albums is 20.
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/albums/get-several-albums/).
pub async fn get_albums(
    token: &AccessToken,
    ids: &[&str],
    market: Option<Market>,
) -> Result<Vec<Album>, EndpointError<Error>> {
    if ids.is_empty() {
        return Ok(Vec::new());
    }

    #[derive(Deserialize)]
    struct Albums {
        albums: Vec<Album>,
    }

    Ok(request!(
        token,
        GET "/v1/albums",
        query_params = {"ids": ids.join(",")},
        optional_query_params = {"market": market.map(|m| m.as_str())},
        ret = Albums
    )
    .albums)
}

/// Get an album's tracks.
///
/// It does not return all the tracks, but a page of tracks. Limit and offset determine attributes
/// of the page. Limit has a maximum of 50.
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/albums/get-albums-tracks/).
pub async fn get_album_tracks(
    token: &AccessToken,
    id: &str,
    limit: usize,
    offset: usize,
    market: Option<Market>,
) -> Result<Page<TrackSimplified>, EndpointError<Error>> {
    Ok(request!(
        token,
        GET "/v1/albums/{}/tracks",
        path_params = [id],
        query_params = {"limit": limit, "offset": offset},
        optional_query_params = {"market": market.map(|m| m.as_str())},
        ret = Page<TrackSimplified>
    ))
}

#[cfg(test)]
mod tests {
    use crate::endpoints::token;
    use crate::*;

    #[tokio::test]
    async fn test_get_album() {
        let album = get_album(&token().await, "03JPFQvZRnHHysSZrSFmKY", None)
            .await
            .unwrap();
        assert_eq!(album.name, "Inside In / Inside Out");
        assert_eq!(album.artists.len(), 1);
        assert_eq!(album.artists[0].name, "The Kooks");
        assert_eq!(album.tracks.total, 14);
        assert_eq!(album.tracks.items[0].name, "Seaside");
    }

    #[tokio::test]
    async fn test_get_albums() {
        let albums = get_albums(
            &token().await,
            &["29Xikj6r9kQDSbnZWCCW2s", "0axbvqBOAejn8DgTUcJAp1"],
            None,
        )
        .await
        .unwrap();
        assert_eq!(albums.len(), 2);
        assert_eq!(albums[0].name, "Neotheater");
        assert_eq!(albums[1].name, "Absentee");
    }

    #[tokio::test]
    async fn test_get_album_tracks() {
        let tracks = get_album_tracks(&token().await, "62U7xIHcID94o20Of5ea4D", 3, 1, None)
            .await
            .unwrap();
        assert_eq!(tracks.limit, 3);
        assert_eq!(tracks.total, 10);
        assert_eq!(tracks.offset, 1);
        assert_eq!(tracks.items.len(), 3);
        assert_eq!(tracks.items[0].name, "Make Believe");
        assert_eq!(tracks.items[1].name, "I Won't Hold You Back");
        assert_eq!(tracks.items[2].name, "Good for You");
    }
}