use errors::*;
use std::io::Read;
use xml::reader::XmlEvent;
use parser::waypoint;
use parser::Context;
use TrackSegment;
enum TrackSegmentEvent {
StartTrkSeg,
StartTrkPt,
EndTrkSeg,
Ignore,
}
pub fn consume<R: Read>(context: &mut Context<R>) -> Result<TrackSegment> {
let mut segment: TrackSegment = Default::default();
loop {
let event: Result<TrackSegmentEvent> = {
if let Some(next) = context.reader.peek() {
match next {
&Ok(XmlEvent::StartElement { ref name, .. }) => {
match name.local_name.as_ref() {
"trkseg" => Ok(TrackSegmentEvent::StartTrkSeg),
"trkpt" => Ok(TrackSegmentEvent::StartTrkPt),
child => Err(Error::from(ErrorKind::InvalidChildElement(
String::from(child),
"tracksegment",
))),
}
}
&Ok(XmlEvent::EndElement { .. }) => Ok(TrackSegmentEvent::EndTrkSeg),
_ => Ok(TrackSegmentEvent::Ignore),
}
} else {
break;
}
};
match event.chain_err(|| Error::from("error while parsing track segment event"))? {
TrackSegmentEvent::StartTrkSeg => {
context.reader.next();
}
TrackSegmentEvent::StartTrkPt => {
segment.points.push(waypoint::consume(context)?);
}
TrackSegmentEvent::EndTrkSeg => {
context.reader.next();
return Ok(segment);
}
TrackSegmentEvent::Ignore => {
context.reader.next();
}
}
}
return Err("no end tag for track segment".into());
}
#[cfg(test)]
mod tests {
use std::io::BufReader;
use xml::reader::EventReader;
use geo::length::Length;
use GpxVersion;
use parser::Context;
use super::consume;
#[test]
fn consume_full_trkseg() {
let segment = consume!(
"
<trkseg>
<trkpt lon=\"-77.0365\" lat=\"38.8977\">
<name>The White House</name>
</trkpt>
<trkpt lon=\"-71.063611\" lat=\"42.358056\">
<name>Boston, Massachusetts</name>
</trkpt>
<trkpt lon=\"-69.7832\" lat=\"44.31055\">
<name>Augusta, Maine</name>
</trkpt>
</trkseg>",
GpxVersion::Gpx11
);
assert!(segment.is_ok());
let segment = segment.unwrap();
assert_eq!(segment.points.len(), 3);
let linestring = segment.linestring();
assert_approx_eq!(linestring.length(), 9.2377437);
}
#[test]
fn consume_empty() {
let segment = consume!("<trkseg></trkseg>", GpxVersion::Gpx11);
assert!(segment.is_ok());
let segment = segment.unwrap();
assert_eq!(segment.points.len(), 0);
}
}