disco_quick/
track.rs

1use crate::artist_credit::{ArtistCredit, ArtistCreditParser};
2use crate::parser::{Parser, ParserError};
3use crate::util::maybe_text;
4use quick_xml::events::Event;
5use std::mem::take;
6
7#[derive(Clone, Debug, Default, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct Track {
10    pub position: String,
11    pub title: String,
12    pub duration: Option<String>,
13    pub artists: Vec<ArtistCredit>,
14    pub extraartists: Vec<ArtistCredit>,
15}
16
17#[derive(Debug, Default)]
18enum ParserState {
19    #[default]
20    Track,
21    Position,
22    Title,
23    Duration,
24    Artists,
25    ExtraArtists,
26}
27
28#[derive(Debug, Default)]
29pub struct TrackParser {
30    state: ParserState,
31    current_item: Track,
32    artist_parser: ArtistCreditParser,
33    pub item_ready: bool,
34}
35
36impl Parser for TrackParser {
37    type Item = Track;
38    fn new() -> Self {
39        TrackParser::default()
40    }
41
42    fn take(&mut self) -> Track {
43        self.item_ready = false;
44        take(&mut self.current_item)
45    }
46
47    fn process(&mut self, ev: &Event) -> Result<(), ParserError> {
48        self.state = match self.state {
49            ParserState::Track => match ev {
50                Event::Start(e) => match e.local_name().as_ref() {
51                    b"track" => ParserState::Track,
52                    b"position" => ParserState::Position,
53                    b"title" => ParserState::Title,
54                    b"duration" => ParserState::Duration,
55                    b"artists" => ParserState::Artists,
56                    b"extraartists" => ParserState::ExtraArtists,
57                    _ => ParserState::Track,
58                },
59                Event::End(e) if e.local_name().as_ref() == b"track" => {
60                    self.item_ready = true;
61                    ParserState::Track
62                }
63                _ => ParserState::Track,
64            },
65
66            ParserState::Position => match ev {
67                Event::Text(e) => {
68                    self.current_item.position = e.unescape()?.to_string();
69                    ParserState::Track
70                }
71                _ => ParserState::Track,
72            },
73
74            ParserState::Title => match ev {
75                Event::Text(e) => {
76                    self.current_item.title = e.unescape()?.to_string();
77                    ParserState::Track
78                }
79                _ => ParserState::Track,
80            },
81
82            ParserState::Duration => match ev {
83                Event::Text(e) => {
84                    self.current_item.duration = maybe_text(e)?;
85                    ParserState::Track
86                }
87                _ => ParserState::Track,
88            },
89
90            ParserState::Artists => match ev {
91                Event::End(e) if e.local_name().as_ref() == b"artists" => ParserState::Track,
92
93                ev => {
94                    self.artist_parser.process(ev)?;
95                    if self.artist_parser.item_ready {
96                        self.current_item.artists.push(self.artist_parser.take());
97                    }
98                    ParserState::Artists
99                }
100            },
101
102            ParserState::ExtraArtists => match ev {
103                Event::End(e) if e.local_name().as_ref() == b"extraartists" => ParserState::Track,
104
105                ev => {
106                    self.artist_parser.process(ev)?;
107                    if self.artist_parser.item_ready {
108                        self.current_item
109                            .extraartists
110                            .push(self.artist_parser.take());
111                    }
112                    ParserState::ExtraArtists
113                }
114            },
115        };
116        Ok(())
117    }
118}