am_api/resource/library/
mod.rs

1//! Apple music library
2
3use crate::error::Error;
4use crate::request::context::ContextContainer;
5use crate::request::try_resource_response;
6use crate::resource::{Resource, ResourceInfo, ResourceType};
7use crate::ApiClient;
8
9use crate::request::builder::MusicRequestBuilder;
10use reqwest::header;
11use std::collections::{HashMap, HashSet};
12use std::sync::Arc;
13
14pub mod album;
15pub mod artist;
16pub mod music_video;
17pub mod playlist;
18pub mod song;
19
20/// Library builder
21pub struct LibraryBuilder;
22
23/// Library add resource builder
24pub type LibraryAddResourceBuilder<'a> =
25    MusicRequestBuilder<'a, LibraryBuilder, HashMap<&'static str, HashSet<String>>>;
26
27impl<'a> LibraryAddResourceBuilder<'a> {
28    /// Create a new [`LibraryAddResourceBuilder`] instance
29    pub fn new() -> LibraryAddResourceBuilder<'a> {
30        LibraryAddResourceBuilder::default()
31    }
32
33    /// Add a resource to the library
34    pub fn add_resource(mut self, resource: &Resource) -> Result<Self, Error> {
35        let supported = matches!(
36            resource,
37            Resource::Album { .. }
38                | Resource::Artist { .. }
39                | Resource::MusicVideo { .. }
40                | Resource::Playlist { .. }
41                | Resource::Song { .. }
42        );
43
44        if !supported {
45            return Err(Error::InvalidResourceType);
46        }
47        self.data
48            .entry(resource.get_type())
49            .or_default()
50            .insert(resource.get_header().id.clone());
51
52        Ok(self)
53    }
54
55    /// Add multiple resources to the library
56    pub fn add_resources(mut self, resources: &[Resource]) -> Result<Self, Error> {
57        for resource in resources {
58            let supported = matches!(
59                resource,
60                Resource::Album { .. }
61                    | Resource::Artist { .. }
62                    | Resource::MusicVideo { .. }
63                    | Resource::Playlist { .. }
64                    | Resource::Song { .. }
65            );
66
67            if !supported {
68                return Err(Error::InvalidResourceType);
69            }
70            self.data
71                .entry(resource.get_type())
72                .or_default()
73                .insert(resource.get_header().id.clone());
74        }
75        Ok(self)
76    }
77
78    /// Send the request
79    pub async fn send(mut self, client: &ApiClient) -> Result<Vec<Resource>, Error> {
80        let mut request_context = self.get_request_context_drain(client);
81        request_context
82            .query
83            .push((String::from("representation"), String::from("ids")));
84
85        for (resource_type, ids) in self.data {
86            request_context.query.push((
87                format!("ids[{}]", resource_type),
88                ids.into_iter().collect::<Vec<_>>().join(","),
89            ));
90        }
91
92        let request_context = Arc::new(request_context);
93
94        let response = client
95            .post("/v1/me/library")
96            .header(header::CONTENT_LENGTH, 0)
97            .query(&request_context.query)
98            .send()
99            .await?;
100
101        let mut response = try_resource_response(response).await?;
102        response.data.set_context(request_context);
103        Ok(response.data)
104    }
105}