1use crate::ns;
8use crate::pubsub::PubSubPayload;
9use minidom::Element;
10use xso::error::{Error, FromElementError};
11
12generate_elem_id!(
13 Artist,
15 "artist",
16 TUNE
17);
18
19generate_elem_id!(
20 Length,
22 "length",
23 TUNE,
24 u16
25);
26
27generate_elem_id!(
28 Rating,
30 "rating",
31 TUNE,
32 u8
33);
34
35generate_elem_id!(
36 Source,
39 "source",
40 TUNE
41);
42
43generate_elem_id!(
44 Title,
46 "title",
47 TUNE
48);
49
50generate_elem_id!(
51 Track,
54 "track",
55 TUNE
56);
57
58generate_elem_id!(
59 Uri,
61 "uri",
62 TUNE
63);
64
65#[derive(Debug, Clone)]
67pub struct Tune {
68 artist: Option<Artist>,
70
71 length: Option<Length>,
73
74 rating: Option<Rating>,
76
77 source: Option<Source>,
80
81 title: Option<Title>,
83
84 track: Option<Track>,
87
88 uri: Option<Uri>,
90}
91
92impl PubSubPayload for Tune {}
93
94impl Tune {
95 fn new() -> Tune {
96 Tune {
97 artist: None,
98 length: None,
99 rating: None,
100 source: None,
101 title: None,
102 track: None,
103 uri: None,
104 }
105 }
106}
107
108impl TryFrom<Element> for Tune {
109 type Error = FromElementError;
110
111 fn try_from(elem: Element) -> Result<Tune, FromElementError> {
112 check_self!(elem, "tune", TUNE);
113 check_no_attributes!(elem, "tune");
114
115 let mut tune = Tune::new();
116 for child in elem.children() {
117 if child.is("artist", ns::TUNE) {
118 if tune.artist.is_some() {
119 return Err(Error::Other("Tune can’t have more than one artist.").into());
120 }
121 tune.artist = Some(Artist::try_from(child.clone())?);
122 } else if child.is("length", ns::TUNE) {
123 if tune.length.is_some() {
124 return Err(Error::Other("Tune can’t have more than one length.").into());
125 }
126 tune.length = Some(Length::try_from(child.clone())?);
127 } else if child.is("rating", ns::TUNE) {
128 if tune.rating.is_some() {
129 return Err(Error::Other("Tune can’t have more than one rating.").into());
130 }
131 tune.rating = Some(Rating::try_from(child.clone())?);
132 } else if child.is("source", ns::TUNE) {
133 if tune.source.is_some() {
134 return Err(Error::Other("Tune can’t have more than one source.").into());
135 }
136 tune.source = Some(Source::try_from(child.clone())?);
137 } else if child.is("title", ns::TUNE) {
138 if tune.title.is_some() {
139 return Err(Error::Other("Tune can’t have more than one title.").into());
140 }
141 tune.title = Some(Title::try_from(child.clone())?);
142 } else if child.is("track", ns::TUNE) {
143 if tune.track.is_some() {
144 return Err(Error::Other("Tune can’t have more than one track.").into());
145 }
146 tune.track = Some(Track::try_from(child.clone())?);
147 } else if child.is("uri", ns::TUNE) {
148 if tune.uri.is_some() {
149 return Err(Error::Other("Tune can’t have more than one uri.").into());
150 }
151 tune.uri = Some(Uri::try_from(child.clone())?);
152 } else {
153 return Err(Error::Other("Unknown element in User Tune.").into());
154 }
155 }
156
157 Ok(tune)
158 }
159}
160
161impl From<Tune> for Element {
162 fn from(tune: Tune) -> Element {
163 Element::builder("tune", ns::TUNE)
164 .append_all(tune.artist)
165 .append_all(tune.length)
166 .append_all(tune.rating)
167 .append_all(tune.source)
168 .append_all(tune.title)
169 .append_all(tune.track)
170 .append_all(tune.uri)
171 .build()
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use std::str::FromStr;
179
180 #[cfg(target_pointer_width = "32")]
181 #[test]
182 fn test_size() {
183 assert_size!(Tune, 68);
184 assert_size!(Artist, 12);
185 assert_size!(Length, 2);
186 assert_size!(Rating, 1);
187 assert_size!(Source, 12);
188 assert_size!(Title, 12);
189 assert_size!(Track, 12);
190 assert_size!(Uri, 12);
191 }
192
193 #[cfg(target_pointer_width = "64")]
194 #[test]
195 fn test_size() {
196 assert_size!(Tune, 128);
197 assert_size!(Artist, 24);
198 assert_size!(Length, 2);
199 assert_size!(Rating, 1);
200 assert_size!(Source, 24);
201 assert_size!(Title, 24);
202 assert_size!(Track, 24);
203 assert_size!(Uri, 24);
204 }
205
206 #[test]
207 fn empty() {
208 let elem: Element = "<tune xmlns='http://jabber.org/protocol/tune'/>"
209 .parse()
210 .unwrap();
211 let elem2 = elem.clone();
212 let tune = Tune::try_from(elem).unwrap();
213 assert!(tune.artist.is_none());
214 assert!(tune.length.is_none());
215 assert!(tune.rating.is_none());
216 assert!(tune.source.is_none());
217 assert!(tune.title.is_none());
218 assert!(tune.track.is_none());
219 assert!(tune.uri.is_none());
220
221 let elem3 = tune.into();
222 assert_eq!(elem2, elem3);
223 }
224
225 #[test]
226 fn full() {
227 let elem: Element = "<tune xmlns='http://jabber.org/protocol/tune'><artist>Yes</artist><length>686</length><rating>8</rating><source>Yessongs</source><title>Heart of the Sunrise</title><track>3</track><uri>http://www.yesworld.com/lyrics/Fragile.html#9</uri></tune>"
228 .parse()
229 .unwrap();
230 let tune = Tune::try_from(elem).unwrap();
231 assert_eq!(tune.artist, Some(Artist::from_str("Yes").unwrap()));
232 assert_eq!(tune.length, Some(Length(686)));
233 assert_eq!(tune.rating, Some(Rating(8)));
234 assert_eq!(tune.source, Some(Source::from_str("Yessongs").unwrap()));
235 assert_eq!(
236 tune.title,
237 Some(Title::from_str("Heart of the Sunrise").unwrap())
238 );
239 assert_eq!(tune.track, Some(Track::from_str("3").unwrap()));
240 assert_eq!(
241 tune.uri,
242 Some(Uri::from_str("http://www.yesworld.com/lyrics/Fragile.html#9").unwrap())
243 );
244 }
245}