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}