spotify_cli/spotify/
track.rs

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