am_api/resource/
relationship.rs

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