Skip to content

Commit

Permalink
Support GPX 1.0
Browse files Browse the repository at this point in the history
This commit enables support for GPX 1.0 format. It introduces a
"Context" wrapper for version-specific parsing of data.

Fixes: #5
PR: #6
  • Loading branch information
hfiguiere authored and brendanashworth committed Mar 10, 2018
1 parent 9680234 commit 385ca1c
Show file tree
Hide file tree
Showing 16 changed files with 354 additions and 158 deletions.
18 changes: 12 additions & 6 deletions src/parser/bounds.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use errors::*;

use std::iter::Peekable;
use std::io::Read;
use xml::reader::Events;
use xml::reader::XmlEvent;
use geo::Bbox;

use parser::Context;

/// consume consumes an element as a nothing.
pub fn consume<R: Read>(reader: &mut Peekable<Events<R>>) -> Result<Bbox<f64>> {
pub fn consume<R: Read>(context: &mut Context<R>) -> Result<Bbox<f64>> {
let mut element: Option<String> = None;
let mut bounds: Bbox<f64> = Bbox {
xmin: 0.,
xmax: 0.,
ymin: 0.,
ymax: 0.,
};
for event in reader {
for event in context.reader() {
match event.chain_err(|| "error while parsing XML")? {
XmlEvent::StartElement {
name, attributes, ..
Expand Down Expand Up @@ -86,14 +86,17 @@ mod tests {
use std::io::BufReader;
use xml::reader::EventReader;

use GpxVersion;
use parser::Context;
use super::consume;

#[test]
fn consume_bounds() {
let bounds = consume!(
"
<bounds minlat=\"45.487064362\" minlon=\"-74.031837463\" maxlat=\"45.701225281\" maxlon=\"-73.586273193\"/>
"
",
GpxVersion::Gpx11
);

assert!(bounds.is_ok());
Expand All @@ -107,7 +110,10 @@ mod tests {

#[test]
fn consume_bad_bounds() {
let bounds = consume!("<bounds minlat=\"32.4\" minlon=\"notanumber\"></wpt>");
let bounds = consume!(
"<bounds minlat=\"32.4\" minlon=\"notanumber\"></wpt>",
GpxVersion::Gpx11
);

assert!(bounds.is_err());
}
Expand Down
31 changes: 21 additions & 10 deletions src/parser/email.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

use errors::*;
use std::io::Read;
use std::iter::Peekable;
use xml::reader::Events;
use xml::reader::XmlEvent;

use parser::Context;

/// consume consumes a GPX email from the `reader` until it ends.
/// When it returns, the reader will be at the element after the end GPX email
/// tag.
pub fn consume<R: Read>(reader: &mut Peekable<Events<R>>) -> Result<String> {
pub fn consume<R: Read>(context: &mut Context<R>) -> Result<String> {
let mut email: Option<String> = None;

while let Some(event) = reader.next() {
while let Some(event) = context.reader.next() {
match event.chain_err(|| "error while parsing XML")? {
XmlEvent::StartElement {
name, attributes, ..
Expand Down Expand Up @@ -63,11 +63,16 @@ mod tests {
use std::io::BufReader;
use xml::reader::EventReader;

use GpxVersion;
use parser::Context;
use super::consume;

#[test]
fn consume_simple_email() {
let email = consume!("<email id=\"me\" domain=\"example.com\" />");
let email = consume!(
"<email id=\"me\" domain=\"example.com\" />",
GpxVersion::Gpx11
);

assert!(email.is_ok());

Expand All @@ -78,7 +83,10 @@ mod tests {

#[test]
fn consume_attrs_reversed() {
let email = consume!("<email domain=\"example.com\" id=\"me\" />");
let email = consume!(
"<email domain=\"example.com\" id=\"me\" />",
GpxVersion::Gpx11
);

assert!(email.is_ok());

Expand All @@ -89,7 +97,7 @@ mod tests {

#[test]
fn consume_err_no_id() {
let err = consume!("<email domain='example.com'/>").unwrap_err();
let err = consume!("<email domain='example.com'/>", GpxVersion::Gpx11).unwrap_err();

assert_eq!(
err.description(),
Expand All @@ -103,7 +111,7 @@ mod tests {

#[test]
fn consume_err_no_domain() {
let err = consume!("<email id=\"gpx\" />").unwrap_err();
let err = consume!("<email id=\"gpx\" />", GpxVersion::Gpx11).unwrap_err();

assert_eq!(
err.description(),
Expand All @@ -117,15 +125,18 @@ mod tests {

#[test]
fn consume_err_invalid_child_element() {
let err = consume!("<email id=\"id\" domain=\"domain\"><child /></email>").unwrap_err();
let err = consume!(
"<email id=\"id\" domain=\"domain\"><child /></email>",
GpxVersion::Gpx11
).unwrap_err();

assert_eq!(err.description(), "invalid child element");
assert_eq!(err.to_string(), "invalid child element 'child' in email");
}

#[test]
fn consume_err_no_ending_tag() {
let err = consume!("<email id=\"id\" domain=\"domain\">").unwrap_err();
let err = consume!("<email id=\"id\" domain=\"domain\">", GpxVersion::Gpx11).unwrap_err();

assert_eq!(err.description(), "error while parsing XML");
}
Expand Down
13 changes: 8 additions & 5 deletions src/parser/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
// TODO: extensions are not implemented

use errors::*;
use std::iter::Peekable;
use std::io::Read;
use xml::reader::Events;
use xml::reader::XmlEvent;

use parser::Context;

/// consume consumes a single string as tag content.
pub fn consume<R: Read>(reader: &mut Peekable<Events<R>>) -> Result<()> {
pub fn consume<R: Read>(context: &mut Context<R>) -> Result<()> {
let mut started = false;

for event in reader {
for event in context.reader() {
match event.chain_err(|| "error while parsing XML")? {
XmlEvent::StartElement { name, .. } => {
// flip started depending on conditions
Expand Down Expand Up @@ -41,6 +41,8 @@ mod tests {
use std::io::BufReader;
use xml::reader::EventReader;

use GpxVersion;
use parser::Context;
use super::consume;

#[test]
Expand All @@ -50,7 +52,8 @@ mod tests {
hello world
<a><b cond=\"no\"><c>derp</c></b></a>
<tag>yadda yadda we dont care</tag>
</extensions>"
</extensions>",
GpxVersion::Gpx11
);

assert!(result.is_ok());
Expand Down
25 changes: 14 additions & 11 deletions src/parser/fix.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
//! fix handles parsing of xsd:simpleType "fixType".

use errors::*;
use std::iter::Peekable;
use std::io::Read;
use xml::reader::Events;

use parser::string;
use parser::Context;

use types::Fix;

/// consume consumes an element as a fix.
pub fn consume<R: Read>(reader: &mut Peekable<Events<R>>) -> Result<Fix> {
let fix_string = string::consume(reader)?;
pub fn consume<R: Read>(context: &mut Context<R>) -> Result<Fix> {
let fix_string = string::consume(context)?;

let fix = match fix_string.as_ref() {
"none" => Fix::None,
Expand All @@ -30,30 +30,33 @@ mod tests {
use xml::reader::EventReader;

use super::consume;

use GpxVersion;
use Fix;
use parser::Context;

#[test]
fn consume_fix() {
let result = consume!("<fix>dgps</fix>");
let result = consume!("<fix>dgps</fix>", GpxVersion::Gpx11);
assert!(result.is_ok());

let result = consume!("<fix>none</fix>");
let result = consume!("<fix>none</fix>", GpxVersion::Gpx11);
assert_eq!(result.unwrap(), Fix::None);

let result = consume!("<fix>2d</fix>");
let result = consume!("<fix>2d</fix>", GpxVersion::Gpx11);
assert_eq!(result.unwrap(), Fix::TwoDimensional);

let result = consume!("<fix>3d</fix>");
let result = consume!("<fix>3d</fix>", GpxVersion::Gpx11);
assert_eq!(result.unwrap(), Fix::ThreeDimensional);

let result = consume!("<fix>dgps</fix>");
let result = consume!("<fix>dgps</fix>", GpxVersion::Gpx11);
assert_eq!(result.unwrap(), Fix::DGPS);

let result = consume!("<fix>pps</fix>");
let result = consume!("<fix>pps</fix>", GpxVersion::Gpx11);
assert_eq!(result.unwrap(), Fix::PPS);

// Not in the specification
let result = consume!("<fix>KF_4SV_OR_MORE</fix>");
let result = consume!("<fix>KF_4SV_OR_MORE</fix>", GpxVersion::Gpx11);
assert_eq!(result.unwrap(), Fix::Other("KF_4SV_OR_MORE".to_owned()));
}
}
Loading

0 comments on commit 385ca1c

Please sign in to comment.