1use chrono::NaiveDate;
2use serde::Deserialize;
3use serde_with::{serde_as, DisplayFromStr};
4
5use crate::{Epoch, Genre, OpenOpusError, OpenOpusResult, Status, Work, ID};
6
7#[derive(Debug, Deserialize)]
8struct Composers {
9 status: Status,
10 composers: Option<Vec<Composer>>,
11}
12
13#[serde_as]
14#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
15pub struct Composer {
16 #[serde_as(as = "DisplayFromStr")]
17 pub id: ID,
18 pub name: String,
19 pub complete_name: String,
20 pub birth: Option<NaiveDate>,
21 pub death: Option<NaiveDate>,
22 pub epoch: Epoch,
23 pub portrait: String,
24}
25
26impl Composer {
27 async fn list_common(url: &str) -> OpenOpusResult<Vec<Self>> {
28 let result = reqwest::get(url).await?.json::<Composers>().await?;
29
30 dbg!(&result);
31
32 match result.status {
33 Status::Ok(_) => Ok(result.composers.unwrap()),
34 Status::Err(err) => Err(OpenOpusError::OpenOpusAPIError(err.error)),
35 }
36 }
37
38 pub async fn list_popular() -> OpenOpusResult<Vec<Self>> {
41 Self::list_common("https://api.openopus.org/composer/list/pop.json").await
42 }
43
44 pub async fn list_essential() -> OpenOpusResult<Vec<Self>> {
47 Self::list_common("https://api.openopus.org/composer/list/rec.json").await
48 }
49
50 pub async fn list_by_first_letter(first_letter: char) -> OpenOpusResult<Vec<Self>> {
53 Self::list_common(&format!(
54 "https://api.openopus.org/composer/list/name/{}.json",
55 first_letter
56 ))
57 .await
58 }
59
60 pub async fn list_by_period(epoch: Epoch) -> OpenOpusResult<Vec<Self>> {
63 Self::list_common(
64 &format!(
65 "https://api.openopus.org/composer/list/epoch/{}.json",
66 epoch.into_url_str()
67 )
68 .to_string(),
69 )
70 .await
71 }
72
73 pub async fn search(word: &str) -> OpenOpusResult<Vec<Self>> {
76 Self::list_common(
77 &format!(
78 "https://api.openopus.org/composer/list/search/{}.json",
79 word
80 )
81 .to_string(),
82 )
83 .await
84 }
85
86 pub async fn get_by_id(id: ID) -> OpenOpusResult<Self> {
89 Self::list_common(
90 &format!("https://api.openopus.org/composer/list/ids/{}.json", id).to_string(),
91 )
92 .await
93 .map(|v| v.into_iter().next().unwrap())
94 }
95
96 pub async fn genres(&self) -> OpenOpusResult<Vec<Genre>> {
99 Genre::list_by_composer_id(self.id).await
100 }
101
102 pub async fn works(&self) -> OpenOpusResult<Vec<Work>> {
105 Work::list_by_composer_id_and_genre(self.id, Genre::All).await
106 }
107
108 pub async fn popular_works(&self) -> OpenOpusResult<Vec<Work>> {
111 Work::list_by_composer_id_and_genre(self.id, Genre::Popular).await
112 }
113
114 pub async fn recommended_works(&self) -> OpenOpusResult<Vec<Work>> {
117 Work::list_by_composer_id_and_genre(self.id, Genre::Recommended).await
118 }
119
120 pub async fn works_by_genre(&self, genre: Genre) -> OpenOpusResult<Vec<Work>> {
123 Work::list_by_composer_id_and_genre(self.id, genre).await
124 }
125
126 pub async fn search_works(&self, search_word: &str) -> OpenOpusResult<Vec<Work>> {
129 Work::saerch_with_composer_id_and_genre(self.id, Genre::All, search_word).await
130 }
131
132 pub async fn search_works_with_genre(
135 &self,
136 search_word: &str,
137 genre: Genre,
138 ) -> OpenOpusResult<Vec<Work>> {
139 Work::saerch_with_composer_id_and_genre(self.id, genre, search_word).await
140 }
141}
142
143#[cfg(test)]
144mod test {
145 use std::{thread, time::Duration};
146
147 use super::*;
148
149 #[tokio::test]
150 async fn test_list_popular() -> anyhow::Result<()> {
151 let _ = Composer::list_popular().await?;
152 Ok(())
153 }
154
155 #[tokio::test]
156 async fn test_list_essential() -> anyhow::Result<()> {
157 let _ = Composer::list_essential().await?;
158 Ok(())
159 }
160
161 #[tokio::test]
162 async fn test_filter_by_first_letter() -> anyhow::Result<()> {
163 let _ = Composer::list_by_first_letter('A').await?;
164 Ok(())
165 }
166
167 #[tokio::test]
168 async fn test_filter_by_period() -> anyhow::Result<()> {
169 use strum::IntoEnumIterator;
170
171 for epoch in Epoch::iter() {
172 let _ = Composer::list_by_period(epoch).await?;
173
174 thread::sleep(Duration::from_millis(100));
175 }
176
177 Ok(())
178 }
179
180 #[tokio::test]
181 async fn test_search() -> anyhow::Result<()> {
182 let _ = Composer::search("bruc").await?;
183 Ok(())
184 }
185
186 #[tokio::test]
187 async fn test_get_by_id() -> anyhow::Result<()> {
188 let _ = Composer::get_by_id(186).await?;
189 Ok(())
190 }
191
192 #[tokio::test]
193 async fn test_get_by_id_not_exists() -> anyhow::Result<()> {
194 assert!(matches!(
195 Composer::get_by_id(999999999).await,
196 Err(OpenOpusError::OpenOpusAPIError(_))
197 ));
198 Ok(())
199 }
200}