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
//! Apple music api
#![deny(missing_docs)]

use crate::error::Error;
pub use celes;
use reqwest::{header, RequestBuilder};

pub mod error;
pub mod primitive;
pub mod request;
pub mod resource;
pub mod time;

/// Cast a Resource to a more specific type
///
/// # Examples
///
/// ```no_run,ignore
/// use
/// let a: Resource = ...;
/// let b: &DoubleProperty = cast!(Property, DoubleProperty, &a).unwrap();
/// ```
#[macro_export]
macro_rules! cast {
    ($type:path, $field:expr) => {
        match $field {
            $type { data } => Some(data),
            _ => None,
        }
    };
}

/// Apple music api client
///
/// Api client can be cloned safely as reqwest uses Arc internally
#[derive(Clone)]
pub struct ApiClient {
    client: reqwest::Client,
    storefront_country: celes::Country,
    localization: String,
}

impl ApiClient {
    /// Create a new [`ApiClient`] instance
    pub fn new(
        developer_token: &str,
        media_user_token: &str,
        storefront_country: celes::Country,
    ) -> Result<ApiClient, Error> {
        let mut headers = header::HeaderMap::new();

        let mut authorization_header =
            header::HeaderValue::from_str(&format!("Bearer {}", developer_token))?;
        authorization_header.set_sensitive(true);
        headers.insert(header::AUTHORIZATION, authorization_header);

        let mut media_user_token_header = header::HeaderValue::from_str(media_user_token)?;
        media_user_token_header.set_sensitive(true);
        headers.insert("media-user-token", media_user_token_header);

        let client = reqwest::Client::builder()
            .default_headers(headers)
            .build()?;

        Ok(ApiClient {
            client,
            storefront_country,
            localization: String::from("en-US"),
        })
    }

    /// Get the default storefront country for this client
    pub fn get_storefront_country(&self) -> celes::Country {
        self.storefront_country
    }

    /// Get the default localization for this client
    pub fn get_localization(&self) -> &str {
        self.localization.as_str()
    }

    /// Set the default localization for this client
    pub fn set_localization(&mut self, localization: &str) {
        self.localization = localization.to_string();
    }

    /// Convenience method to make a GET request to an endpoint
    pub fn get(&self, endpoint: &str) -> RequestBuilder {
        self.client
            .get(format!("https://api.music.apple.com{}", endpoint))
            .query(&[("art[url]", "f")])
    }

    /// Convenience method to make a POST request to an endpoint
    pub fn post(&self, endpoint: &str) -> RequestBuilder {
        self.client
            .post(format!("https://api.music.apple.com{}", endpoint))
            .query(&[("art[url]", "f")])
    }

    /// Convenience method to make a PUT request to an endpoint
    pub fn put(&self, endpoint: &str) -> RequestBuilder {
        self.client
            .put(format!("https://api.music.apple.com{}", endpoint))
            .query(&[("art[url]", "f")])
    }

    /// Convenience method to make a DELETE request to an endpoint
    pub fn delete(&self, endpoint: &str) -> RequestBuilder {
        self.client
            .delete(format!("https://api.music.apple.com{}", endpoint))
            .query(&[("art[url]", "f")])
    }
}