disco_quick/
track.rs

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