1use std::io::Read;
4
5use xml::reader::XmlEvent;
6
7use crate::errors::{GpxError, GpxResult};
8use crate::parser::{string, verify_starting_tag, Context};
9use crate::Link;
10
11pub fn consume<R: Read>(context: &mut Context<R>) -> GpxResult<Link> {
15 let mut link: Link = Default::default();
16 let attributes = verify_starting_tag(context, "link")?;
17 let attr = attributes
18 .into_iter()
19 .find(|attr| attr.name.local_name == "href");
20
21 let attr = attr.ok_or(GpxError::InvalidElementLacksAttribute("href", "link"))?;
22
23 link.href = attr.value;
24
25 loop {
26 let next_event = {
27 if let Some(next) = context.reader.peek() {
28 match next {
29 Ok(n) => n,
30 Err(_) => return Err(GpxError::EventParsingError("link event")),
31 }
32 } else {
33 break;
34 }
35 };
36
37 match next_event {
38 XmlEvent::StartElement { ref name, .. } => match name.local_name.as_ref() {
39 "text" => link.text = Some(string::consume(context, "text", true)?),
40 "type" => link.type_ = Some(string::consume(context, "type", true)?),
41 child => {
42 return Err(GpxError::InvalidChildElement(String::from(child), "link"));
43 }
44 },
45 XmlEvent::EndElement { ref name } => {
46 if name.local_name != "link" {
47 return Err(GpxError::InvalidClosingTag(name.local_name.clone(), "link"));
48 }
49 context.reader.next();
50 return Ok(link);
51 }
52 _ => {
53 context.reader.next(); }
55 }
56 }
57
58 Err(GpxError::MissingClosingTag("link"))
59}
60
61#[cfg(test)]
62mod tests {
63 use super::consume;
64 use crate::GpxVersion;
65
66 #[test]
67 fn consume_simple_link() {
68 let link = consume!(
69 "<link href='http://example.com'><text>hello</text><type>world</type></link>",
70 GpxVersion::Gpx11
71 );
72
73 assert!(link.is_ok());
74
75 let link = link.unwrap();
76
77 assert_eq!(link.href, "http://example.com");
78
79 assert!(link.text.is_some());
80 assert_eq!(link.text.unwrap(), "hello");
81
82 assert!(link.type_.is_some());
83 assert_eq!(link.type_.unwrap(), "world");
84 }
85
86 #[test]
87 fn consume_barebones() {
88 let link = consume!(
89 "<link href='http://topografix.com'></link>",
90 GpxVersion::Gpx11
91 );
92
93 assert!(link.is_ok());
94
95 let link = link.unwrap();
96
97 assert_eq!(link.href, "http://topografix.com");
98
99 assert!(link.text.is_none());
100 assert!(link.type_.is_none());
101 }
102
103 #[test]
104 fn consume_no_href() {
105 let link = consume!("<link></link>", GpxVersion::Gpx11);
106
107 assert!(link.is_err());
108 }
109
110 #[test]
111 fn consume_empty_href_text_type() {
112 let link = consume!(
113 r#"<link href=""><text></text><type></type></link>"#,
114 GpxVersion::Gpx11
115 );
116
117 assert!(link.is_ok());
118
119 let link = link.unwrap();
120
121 assert_eq!(link.href, "");
122 assert_eq!(link.text, Some(String::from("")));
123 assert_eq!(link.type_, Some(String::from("")));
124 }
125}