spotify_cli/spotify/
track.rs

1use anyhow::{Context, bail};
2use reqwest::Method;
3use reqwest::blocking::Client as HttpClient;
4
5use crate::error::Result;
6use crate::spotify::auth::AuthService;
7use crate::spotify::base::api_base;
8use crate::spotify::error::format_api_error;
9
10/// Spotify saved tracks (library) API client.
11#[derive(Debug, Clone)]
12pub struct TrackClient {
13    http: HttpClient,
14    auth: AuthService,
15}
16
17impl TrackClient {
18    pub fn new(http: HttpClient, auth: AuthService) -> Self {
19        Self { http, auth }
20    }
21
22    pub fn like(&self, track_id: &str) -> Result<()> {
23        let path = format!("/me/tracks?ids={}", track_id);
24        self.send(Method::PUT, &path)
25    }
26
27    pub fn unlike(&self, track_id: &str) -> Result<()> {
28        let path = format!("/me/tracks?ids={}", track_id);
29        self.send(Method::DELETE, &path)
30    }
31
32    fn send(&self, method: Method, path: &str) -> Result<()> {
33        let token = self.auth.token()?;
34        let url = format!("{}{}", api_base(), path);
35
36        let response = self
37            .http
38            .request(method, url)
39            .bearer_auth(token.access_token)
40            .body(Vec::new())
41            .send()
42            .context("spotify request failed")?;
43
44        if response.status().is_success() {
45            return Ok(());
46        }
47
48        let status = response.status();
49        let body = response.text().unwrap_or_else(|_| "<no body>".to_string());
50        bail!(format_api_error("spotify request failed", status, &body))
51    }
52}