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}