xmpp_parsers/
tune.rs

1// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use crate::ns;
8use crate::pubsub::PubSubPayload;
9use minidom::Element;
10use xso::error::{Error, FromElementError};
11
12generate_elem_id!(
13    /// The artist or performer of the song or piece.
14    Artist,
15    "artist",
16    TUNE
17);
18
19generate_elem_id!(
20    /// The duration of the song or piece in seconds.
21    Length,
22    "length",
23    TUNE,
24    u16
25);
26
27generate_elem_id!(
28    /// The user's rating of the song or piece, from 1 (lowest) to 10 (highest).
29    Rating,
30    "rating",
31    TUNE,
32    u8
33);
34
35generate_elem_id!(
36    /// The collection (e.g., album) or other source (e.g., a band website that hosts streams or
37    /// audio files).
38    Source,
39    "source",
40    TUNE
41);
42
43generate_elem_id!(
44    /// The title of the song or piece.
45    Title,
46    "title",
47    TUNE
48);
49
50generate_elem_id!(
51    /// A unique identifier for the tune; e.g., the track number within a collection or the
52    /// specific URI for the object (e.g., a stream or audio file).
53    Track,
54    "track",
55    TUNE
56);
57
58generate_elem_id!(
59    /// A URI or URL pointing to information about the song, collection, or artist.
60    Uri,
61    "uri",
62    TUNE
63);
64
65/// Container for formatted text.
66#[derive(Debug, Clone)]
67pub struct Tune {
68    /// The artist or performer of the song or piece.
69    artist: Option<Artist>,
70
71    /// The duration of the song or piece in seconds.
72    length: Option<Length>,
73
74    /// The user's rating of the song or piece, from 1 (lowest) to 10 (highest).
75    rating: Option<Rating>,
76
77    /// The collection (e.g., album) or other source (e.g., a band website that hosts streams or
78    /// audio files).
79    source: Option<Source>,
80
81    /// The title of the song or piece.
82    title: Option<Title>,
83
84    /// A unique identifier for the tune; e.g., the track number within a collection or the
85    /// specific URI for the object (e.g., a stream or audio file).
86    track: Option<Track>,
87
88    /// A URI or URL pointing to information about the song, collection, or artist.
89    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}