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