am_api/resource/
view.rs

1//! Apple music view
2
3use crate::error::Error;
4use crate::request::context::{ContextContainer, RequestContext};
5use crate::resource::ErrorResponse;
6use crate::ApiClient;
7use async_stream::try_stream;
8use futures::Stream;
9use reqwest::{Response};
10use serde::de::DeserializeOwned;
11use serde::{Deserialize, Serialize};
12use std::fmt::{Debug, Formatter};
13use std::hash::{Hash, Hasher};
14use std::sync::Arc;
15
16/// Apple music view
17#[derive(Serialize, Deserialize, Clone)]
18#[serde(rename_all = "camelCase")]
19pub struct View<Attributes, T> {
20    /// The relative location to fetch the view directly
21    #[serde(default)]
22    pub href: Option<String>,
23    /// The relative location to request the next page of resources in the collection, if additional resources are available for fetching
24    #[serde(default)]
25    pub next: Option<String>,
26    /// Attributes
27    pub attributes: Attributes,
28    /// Data
29    #[serde(default = "Vec::default")]
30    pub data: Vec<T>,
31    /// Context
32    #[serde(skip, default)]
33    context: Option<Arc<RequestContext>>,
34}
35
36impl<Attributes, T> View<Attributes, T>
37where
38    Attributes: Clone + DeserializeOwned,
39    T: Clone + DeserializeOwned + ContextContainer,
40{
41    /// Iterate this view
42    pub fn iter(&self, client: &ApiClient) -> impl Stream<Item = Result<T, Error>> {
43        let view = self.clone();
44        let client = client.clone();
45        let context = view
46            .context
47            .clone()
48            .expect("context should always exist on views");
49
50        try_stream! {
51            let mut view = view;
52
53            loop {
54                for mut entry in view.data {
55                    entry.set_context(context.clone());
56                    yield entry;
57                }
58
59                let Some(next) = view.next.as_ref() else {
60                    return;
61                };
62
63                let response = client.get(next.as_str()).query(&context.query).send().await?;
64                view = Self::try_view_response(response).await?;
65            }
66        }
67    }
68
69    async fn try_view_response(response: Response) -> Result<Self, Error> {
70        if !response.status().is_success() {
71            let error_response: ErrorResponse = response.json().await?;
72            return Err(Error::MusicError(error_response));
73        }
74
75        let result = response.json().await?;
76        Ok(result)
77    }
78}
79
80impl<Attributes, T> ContextContainer for View<Attributes, T>
81where
82    T: ContextContainer,
83{
84    fn set_context(&mut self, context: Arc<RequestContext>) {
85        self.context = Some(context.clone());
86        self.data.set_context(context)
87    }
88}
89
90impl<Attributes, T> Debug for View<Attributes, T>
91where
92    Attributes: Debug,
93    T: Debug,
94{
95    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96        f.debug_struct("View")
97            .field("href", &self.href)
98            .field("next", &self.next)
99            .field("attributes", &self.attributes)
100            .field("data", &self.data)
101            .finish()
102    }
103}
104
105impl<Attributes, T> PartialEq for View<Attributes, T>
106where
107    Attributes: PartialEq,
108    T: PartialEq,
109{
110    fn eq(&self, other: &Self) -> bool {
111        self.href == other.href
112            && self.next == other.next
113            && self.attributes == other.attributes
114            && self.data == other.data
115    }
116}
117
118impl<Attributes, T> Eq for View<Attributes, T>
119where
120    Attributes: Eq,
121    T: Eq,
122{
123}
124
125impl<Attributes, T> Hash for View<Attributes, T>
126where
127    Attributes: Hash,
128    T: Hash,
129{
130    fn hash<H: Hasher>(&self, state: &mut H) {
131        self.href.hash(state);
132        self.next.hash(state);
133        self.attributes.hash(state);
134        self.data.hash(state);
135    }
136}