disco_quick/
release.rs

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, // https://www.discogs.com/release/8262262
43    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}