disco_quick/
master.rs

1use crate::artist_credit::{get_credit_string, ArtistCredit, ArtistCreditParser};
2use crate::parser::{Parser, ParserError};
3use crate::reader::XmlReader;
4use crate::shared::Image;
5use crate::util::get_attr_id;
6use crate::video::{Video, VideoParser};
7use log::debug;
8use quick_xml::events::Event;
9use std::fmt;
10use std::mem::take;
11
12#[derive(Clone, Debug, Default)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Master {
15    pub id: u32,
16    pub title: String,
17    pub main_release: i32,
18    pub year: i32,
19    pub notes: Option<String>,
20    pub genres: Vec<String>,
21    pub styles: Vec<String>,
22    pub data_quality: String,
23    pub artists: Vec<ArtistCredit>,
24    pub images: Vec<Image>,
25    pub videos: Vec<Video>,
26}
27
28impl fmt::Display for Master {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        let artist_credit = get_credit_string(&self.artists);
31        write!(f, "{} - {}", artist_credit, self.title)
32    }
33}
34
35pub struct MastersReader {
36    buf: Vec<u8>,
37    reader: XmlReader,
38    parser: MasterParser,
39}
40
41impl MastersReader {
42    pub fn new(reader: XmlReader, buf: Vec<u8>) -> Self {
43        Self {
44            buf,
45            reader,
46            parser: MasterParser::new(),
47        }
48    }
49}
50
51impl Iterator for MastersReader {
52    type Item = Master;
53    fn next(&mut self) -> Option<Self::Item> {
54        loop {
55            match self.reader.read_event_into(&mut self.buf).unwrap() {
56                Event::Eof => {
57                    return None;
58                }
59                ev => self.parser.process(ev).unwrap(),
60            };
61            if self.parser.item_ready {
62                return Some(self.parser.take());
63            }
64            self.buf.clear();
65        }
66    }
67}
68
69#[derive(Debug, Default)]
70enum ParserState {
71    #[default]
72    Master,
73    MainRelease,
74    Artists,
75    Title,
76    DataQuality,
77    Notes,
78    Images,
79    Styles,
80    Genres,
81    Year,
82    Videos,
83}
84
85#[derive(Debug, Default)]
86pub struct MasterParser {
87    state: ParserState,
88    current_item: Master,
89    artist_parser: ArtistCreditParser,
90    videos_parser: VideoParser,
91    item_ready: bool,
92}
93
94impl Parser for MasterParser {
95    type Item = Master;
96    fn new() -> Self {
97        Self::default()
98    }
99
100    fn take(&mut self) -> Master {
101        self.item_ready = false;
102        take(&mut self.current_item)
103    }
104
105    fn process(&mut self, ev: Event) -> Result<(), ParserError> {
106        self.state = match self.state {
107            ParserState::Master => match ev {
108                Event::Start(e) if e.local_name().as_ref() == b"master" => {
109                    self.current_item.id = get_attr_id(e);
110                    debug!("Began parsing Master {}", self.current_item.id);
111                    ParserState::Master
112                }
113
114                Event::Start(e) => match e.local_name().as_ref() {
115                    b"main_release" => ParserState::MainRelease,
116                    b"title" => ParserState::Title,
117                    b"artists" => ParserState::Artists,
118                    b"data_quality" => ParserState::DataQuality,
119                    b"images" => ParserState::Images,
120                    b"styles" => ParserState::Styles,
121                    b"genres" => ParserState::Genres,
122                    b"notes" => ParserState::Notes,
123                    b"year" => ParserState::Year,
124                    b"videos" => ParserState::Videos,
125                    _ => ParserState::Master,
126                },
127
128                Event::End(e) if e.local_name().as_ref() == b"master" => {
129                    self.item_ready = true;
130                    ParserState::Master
131                }
132
133                _ => ParserState::Master,
134            },
135
136            ParserState::MainRelease => match ev {
137                Event::Text(e) => {
138                    self.current_item.main_release = e.unescape()?.parse()?;
139                    ParserState::MainRelease
140                }
141                _ => ParserState::Master,
142            },
143
144            ParserState::Artists => match ev {
145                Event::End(e) if e.local_name().as_ref() == b"artists" => ParserState::Master,
146
147                ev => {
148                    self.artist_parser.process(ev)?;
149                    if self.artist_parser.item_ready {
150                        self.current_item.artists.push(self.artist_parser.take());
151                    }
152                    ParserState::Artists
153                }
154            },
155
156            ParserState::Title => match ev {
157                Event::Text(e) => {
158                    self.current_item.title = e.unescape()?.to_string();
159                    ParserState::Title
160                }
161                _ => ParserState::Master,
162            },
163
164            ParserState::DataQuality => match ev {
165                Event::Text(e) => {
166                    self.current_item.data_quality = e.unescape()?.to_string();
167                    ParserState::DataQuality
168                }
169                _ => ParserState::Master,
170            },
171
172            ParserState::Images => match ev {
173                Event::Empty(e) if e.local_name().as_ref() == b"image" => {
174                    let image = Image::from_event(e);
175                    self.current_item.images.push(image);
176                    ParserState::Images
177                }
178                Event::End(e) if e.local_name().as_ref() == b"images" => ParserState::Master,
179
180                _ => ParserState::Images,
181            },
182
183            ParserState::Genres => match ev {
184                Event::End(e) if e.local_name().as_ref() == b"genres" => ParserState::Master,
185
186                Event::Text(e) => {
187                    self.current_item.genres.push(e.unescape()?.to_string());
188                    ParserState::Genres
189                }
190                _ => ParserState::Genres,
191            },
192
193            ParserState::Styles => match ev {
194                Event::End(e) if e.local_name().as_ref() == b"styles" => ParserState::Master,
195
196                Event::Text(e) => {
197                    self.current_item.styles.push(e.unescape()?.to_string());
198                    ParserState::Styles
199                }
200                _ => ParserState::Styles,
201            },
202
203            ParserState::Notes => match ev {
204                Event::Text(e) => {
205                    self.current_item.notes = Some(e.unescape()?.to_string());
206                    ParserState::Notes
207                }
208                _ => ParserState::Master,
209            },
210
211            ParserState::Year => match ev {
212                Event::Text(e) => {
213                    self.current_item.year = e.unescape()?.parse()?;
214                    ParserState::Year
215                }
216                _ => ParserState::Master,
217            },
218
219            ParserState::Videos => match ev {
220                Event::End(e) if e.local_name().as_ref() == b"videos" => ParserState::Master,
221
222                ev => {
223                    self.videos_parser.process(ev)?;
224                    if self.videos_parser.item_ready {
225                        self.current_item.videos.push(self.videos_parser.take());
226                    }
227                    ParserState::Videos
228                }
229            },
230        };
231
232        Ok(())
233    }
234}