1use crate::artist_credit::{get_credit_string, ArtistCredit, ArtistCreditParser};
2use crate::company::CompanyParser;
3use crate::parser::{Parser, ParserError};
4use crate::reader::XmlReader;
5use crate::shared::{Image, ReleaseLabel};
6use crate::track::{Track, TrackParser};
7use crate::util::get_attr;
8use crate::video::{Video, VideoParser};
9use log::debug;
10use quick_xml::events::Event;
11use std::fmt;
12use std::mem::take;
13
14#[derive(Clone, Debug, Default)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct Release {
17 pub id: i32,
18 pub status: String,
19 pub title: String,
20 pub artists: Vec<ArtistCredit>,
21 pub country: String,
22 pub labels: Vec<ReleaseLabel>,
23 pub released: String,
24 pub notes: Option<String>,
25 pub genres: Vec<String>,
26 pub styles: Vec<String>,
27 pub master_id: Option<i32>,
28 pub is_main_release: bool,
29 pub data_quality: String,
30 pub images: Vec<Image>,
31 pub videos: Vec<Video>,
32 pub extraartists: Vec<ArtistCredit>,
33 pub tracklist: Vec<Track>,
34 pub formats: Vec<ReleaseFormat>,
35 pub companies: Vec<ReleaseLabel>,
36 pub identifiers: Vec<ReleaseIdentifier>,
37}
38
39#[derive(Clone, Debug, Default)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41pub struct ReleaseFormat {
42 pub qty: String, pub name: String,
44 pub text: Option<String>,
45 pub descriptions: Vec<String>,
46}
47
48#[derive(Clone, Debug, Default)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct ReleaseIdentifier {
51 pub r#type: String,
52 pub description: String,
53 pub value: Option<String>,
54}
55
56impl fmt::Display for Release {
57 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58 let artist_credit = get_credit_string(&self.artists);
59 write!(f, "{} - {}", artist_credit, self.title)
60 }
61}
62
63pub struct ReleasesReader {
64 buf: Vec<u8>,
65 reader: XmlReader,
66 parser: ReleaseParser,
67}
68
69impl ReleasesReader {
70 pub fn new(reader: XmlReader, buf: Vec<u8>) -> Self {
71 Self {
72 buf,
73 reader,
74 parser: ReleaseParser::new(),
75 }
76 }
77}
78
79impl Iterator for ReleasesReader {
80 type Item = Release;
81 fn next(&mut self) -> Option<Self::Item> {
82 loop {
83 match self.reader.read_event_into(&mut self.buf).unwrap() {
84 Event::Eof => {
85 return None;
86 }
87 ev => self.parser.process(ev).unwrap(),
88 };
89 if self.parser.item_ready {
90 return Some(self.parser.take());
91 }
92 self.buf.clear();
93 }
94 }
95}
96
97#[derive(Debug, Default)]
98enum ParserState {
99 #[default]
100 Release,
101 Title,
102 Country,
103 Released,
104 Notes,
105 Genres,
106 Styles,
107 MasterId,
108 DataQuality,
109 Labels,
110 Videos,
111 Artists,
112 ExtraArtists,
113 TrackList,
114 Format,
115 Companies,
116 Identifiers,
117}
118
119#[derive(Debug, Default)]
120pub struct ReleaseParser {
121 state: ParserState,
122 current_item: Release,
123 artist_parser: ArtistCreditParser,
124 video_parser: VideoParser,
125 track_parser: TrackParser,
126 company_parser: CompanyParser,
127 item_ready: bool,
128}
129
130impl Parser for ReleaseParser {
131 type Item = Release;
132
133 fn new() -> Self {
134 Self::default()
135 }
136
137 fn take(&mut self) -> Release {
138 self.item_ready = false;
139 take(&mut self.current_item)
140 }
141
142 fn process(&mut self, ev: Event) -> Result<(), ParserError> {
143 self.state = match self.state {
144 ParserState::Release => match ev {
145 Event::End(e) if e.local_name().as_ref() == b"release" => {
146 self.item_ready = true;
147 ParserState::Release
148 }
149 Event::Start(e) if e.local_name().as_ref() == b"release" => {
150 let mut a = e.attributes();
151 self.current_item.id = get_attr(a.next()).parse()?;
152 debug!("Began parsing Release {}", self.current_item.id);
153 self.current_item.status = get_attr(a.next()).to_string();
154 ParserState::Release
155 }
156 Event::Start(e) if e.local_name().as_ref() == b"master_id" => {
157 let mut a = e.attributes();
158 self.current_item.is_main_release = get_attr(a.next()).parse()?;
159 ParserState::MasterId
160 }
161 Event::Start(e) => match e.local_name().as_ref() {
162 b"title" => ParserState::Title,
163 b"country" => ParserState::Country,
164 b"released" => ParserState::Released,
165 b"notes" => ParserState::Notes,
166 b"genres" => ParserState::Genres,
167 b"styles" => ParserState::Styles,
168 b"data_quality" => ParserState::DataQuality,
169 b"labels" => ParserState::Labels,
170 b"videos" => ParserState::Videos,
171 b"artists" => ParserState::Artists,
172 b"extraartists" => ParserState::ExtraArtists,
173 b"tracklist" => ParserState::TrackList,
174 b"formats" => ParserState::Format,
175 b"identifiers" => ParserState::Identifiers,
176 b"companies" => ParserState::Companies,
177 _ => ParserState::Release,
178 },
179 _ => ParserState::Release,
180 },
181
182 ParserState::Title => match ev {
183 Event::Text(e) => {
184 self.current_item.title = e.unescape()?.to_string();
185 ParserState::Title
186 }
187 _ => ParserState::Release,
188 },
189
190 ParserState::Country => match ev {
191 Event::Text(e) => {
192 self.current_item.country = e.unescape()?.to_string();
193 ParserState::Country
194 }
195 _ => ParserState::Release,
196 },
197
198 ParserState::Released => match ev {
199 Event::Text(e) => {
200 self.current_item.released = e.unescape()?.to_string();
201 ParserState::Released
202 }
203 _ => ParserState::Release,
204 },
205
206 ParserState::Notes => match ev {
207 Event::Text(e) => {
208 self.current_item.notes = Some(e.unescape()?.to_string());
209 ParserState::Notes
210 }
211 _ => ParserState::Release,
212 },
213
214 ParserState::Artists => match ev {
215 Event::End(e) if e.local_name().as_ref() == b"artists" => ParserState::Release,
216
217 ev => {
218 self.artist_parser.process(ev)?;
219 if self.artist_parser.item_ready {
220 self.current_item.artists.push(self.artist_parser.take());
221 }
222 ParserState::Artists
223 }
224 },
225
226 ParserState::ExtraArtists => match ev {
227 Event::End(e) if e.local_name().as_ref() == b"extraartists" => ParserState::Release,
228
229 ev => {
230 self.artist_parser.process(ev)?;
231 if self.artist_parser.item_ready {
232 let ea = self.artist_parser.take();
233 self.current_item.extraartists.push(ea);
234 }
235 ParserState::ExtraArtists
236 }
237 },
238
239 ParserState::Genres => match ev {
240 Event::End(e) if e.local_name().as_ref() == b"genres" => ParserState::Release,
241
242 Event::Text(e) => {
243 self.current_item.genres.push(e.unescape()?.to_string());
244 ParserState::Genres
245 }
246 _ => ParserState::Genres,
247 },
248
249 ParserState::Styles => match ev {
250 Event::End(e) if e.local_name().as_ref() == b"styles" => ParserState::Release,
251
252 Event::Text(e) => {
253 self.current_item.styles.push(e.unescape()?.to_string());
254 ParserState::Styles
255 }
256 _ => ParserState::Styles,
257 },
258
259 ParserState::Format => match ev {
260 Event::Start(e) if e.local_name().as_ref() == b"format" => {
261 let mut attrs = e.attributes();
262 let mut format = ReleaseFormat {
263 name: get_attr(attrs.next()).to_string(),
264 qty: get_attr(attrs.next()).to_string(),
265 ..Default::default()
266 };
267 let text = get_attr(attrs.next()).to_string();
268 if !text.is_empty() {
269 format.text = Some(text)
270 }
271 self.current_item.formats.push(format);
272 ParserState::Format
273 }
274 Event::Text(e) => {
275 let description = e.unescape()?.to_string();
276 let i = self.current_item.formats.len() - 1;
277 self.current_item.formats[i].descriptions.push(description);
278 ParserState::Format
279 }
280 Event::End(e) if e.local_name().as_ref() == b"formats" => ParserState::Release,
281
282 _ => ParserState::Format,
283 },
284
285 ParserState::Identifiers => match ev {
286 Event::Empty(e) => {
287 let mut attrs = e.attributes();
288 let identifier = ReleaseIdentifier {
289 r#type: get_attr(attrs.next()).to_string(),
290 description: get_attr(attrs.next()).to_string(),
291 value: if let Some(v) = attrs.next() {
292 Some(v.unwrap().unescape_value()?.to_string())
293 } else {
294 None
295 },
296 };
297 self.current_item.identifiers.push(identifier);
298 ParserState::Identifiers
299 }
300 _ => ParserState::Release,
301 },
302
303 ParserState::MasterId => match ev {
304 Event::Text(e) => {
305 self.current_item.master_id = Some(e.unescape()?.parse()?);
306 ParserState::MasterId
307 }
308 Event::End(_) => ParserState::Release,
309
310 _ => ParserState::MasterId,
311 },
312
313 ParserState::DataQuality => match ev {
314 Event::Text(e) => {
315 self.current_item.data_quality = e.unescape()?.to_string();
316 ParserState::DataQuality
317 }
318 _ => ParserState::Release,
319 },
320
321 ParserState::Labels => match ev {
322 Event::Empty(e) => {
323 let mut attrs = e.attributes();
324 let label = ReleaseLabel {
325 name: get_attr(attrs.next()).to_string(),
326 catno: Some(get_attr(attrs.next()).to_string()),
327 id: get_attr(attrs.next()).parse()?,
328 entity_type: 1,
329 entity_type_name: "Label".to_string(),
330 };
331 self.current_item.labels.push(label);
332 ParserState::Labels
333 }
334 _ => ParserState::Release,
335 },
336
337 ParserState::Videos => match ev {
338 Event::End(e) if e.local_name().as_ref() == b"videos" => ParserState::Release,
339
340 ev => {
341 self.video_parser.process(ev)?;
342 if self.video_parser.item_ready {
343 self.current_item.videos.push(self.video_parser.take());
344 }
345 ParserState::Videos
346 }
347 },
348
349 ParserState::TrackList => match ev {
350 Event::End(e) if e.local_name().as_ref() == b"tracklist" => ParserState::Release,
351
352 ev => {
353 self.track_parser.process(ev)?;
354 if self.track_parser.item_ready {
355 self.current_item.tracklist.push(self.track_parser.take());
356 }
357 ParserState::TrackList
358 }
359 },
360
361 ParserState::Companies => match ev {
362 Event::End(e) if e.local_name().as_ref() == b"companies" => ParserState::Release,
363
364 ev => {
365 self.company_parser.process(ev)?;
366 if self.company_parser.item_ready {
367 self.current_item.companies.push(self.company_parser.take());
368 }
369 ParserState::Companies
370 }
371 },
372 };
373
374 Ok(())
375 }
376}