spotify_cli/spotify/
artists.rs

1use anyhow::bail;
2use reqwest::blocking::Client as HttpClient;
3use serde::Deserialize;
4
5use crate::domain::artist::Artist;
6use crate::error::Result;
7use crate::spotify::auth::AuthService;
8use crate::spotify::base::api_base;
9use crate::spotify::error::format_api_error;
10
11
12/// Spotify artist API client.
13#[derive(Debug, Clone)]
14pub struct ArtistsClient {
15    http: HttpClient,
16    auth: AuthService,
17}
18
19impl ArtistsClient {
20    pub fn new(http: HttpClient, auth: AuthService) -> Self {
21        Self { http, auth }
22    }
23
24    pub fn get(&self, artist_id: &str) -> Result<Artist> {
25        let token = self.auth.token()?;
26        let url = format!("{}/artists/{artist_id}", api_base());
27
28        let response = self
29            .http
30            .get(url)
31            .bearer_auth(token.access_token)
32            .send()?;
33
34        if !response.status().is_success() {
35            let status = response.status();
36            let body = response.text().unwrap_or_else(|_| "<no body>".to_string());
37            bail!(format_api_error("spotify artist request failed", status, &body));
38        }
39
40        let payload: SpotifyArtist = response.json()?;
41        Ok(Artist {
42            id: payload.id,
43            name: payload.name,
44            uri: payload.uri,
45            genres: payload.genres,
46            followers: payload.followers.map(|followers| followers.total),
47        })
48    }
49}
50
51#[derive(Debug, Deserialize)]
52struct SpotifyArtist {
53    id: String,
54    name: String,
55    uri: String,
56    #[serde(default)]
57    genres: Vec<String>,
58    followers: Option<SpotifyFollowers>,
59}
60
61#[derive(Debug, Deserialize)]
62struct SpotifyFollowers {
63    total: u64,
64}