Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 76 additions & 1 deletion src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use quick_xml::events::{BytesStart, Event};
use crate::errors::Error;
use crate::types::geom_props::GeomProps;
use crate::types::{
self, coords_from_str, Alias, BalloonStyle, ColorMode, Coord, CoordType, Element, Folder,
self, coords_from_str, Alias, BalloonStyle, ColorMode, Coord, CoordType, Data, Element, Folder,
Geometry, Icon, IconStyle, Kml, KmlDocument, KmlVersion, LabelStyle, LineString, LineStyle,
LinearRing, Link, LinkTypeIcon, ListStyle, Location, MultiGeometry, Orientation, Pair,
Placemark, Point, PolyStyle, Polygon, RefreshMode, ResourceMap, Scale, SchemaData,
Expand Down Expand Up @@ -164,6 +164,7 @@ where
elements.push(Kml::ResourceMap(self.read_resource_map(attrs)?))
}
b"Alias" => elements.push(Kml::Alias(self.read_alias(attrs)?)),
b"Data" => elements.push(Kml::Data(self.read_data(attrs)?)),
b"SchemaData" => {
elements.push(Kml::SchemaData(self.read_schema_data(attrs)?))
}
Expand Down Expand Up @@ -795,6 +796,37 @@ where
Ok(alias)
}

fn read_data(&mut self, mut attrs: HashMap<String, String>) -> Result<Data, Error> {
let mut kml_data = Data {
name: attrs.remove("name"),
uom: attrs.remove("uom"),
attrs,
..Default::default()
};
loop {
let e = self.reader.read_event_into(&mut self.buf)?;
match e {
Event::Start(e) => match e.local_name().as_ref() {
b"displayName" => {
kml_data.display_name = self.read_str().ok();
}
b"value" => {
kml_data.value = self.read_str()?;
}
_ => {}
},
Event::End(e) => {
if e.local_name().as_ref() == b"Data" {
break;
}
}
Event::Comment(_) => {}
_ => {}
}
}
Ok(kml_data)
}

fn read_schema_data(&mut self, attrs: HashMap<String, String>) -> Result<SchemaData, Error> {
let mut schema_data = SchemaData {
attrs,
Expand Down Expand Up @@ -1371,6 +1403,49 @@ mod tests {
);
}

#[test]
fn test_read_kml_data() {
let kml_str = r##"<Data name="holeNumber", uom="yd_us", anyAttribute="anySimpleType">
<!-- comment -->
<displayName>holeNumberDisplay</displayName>
<value>1234</value>
</Data>"##;
let a: Kml = kml_str.parse().unwrap();
assert_eq!(
a,
Kml::Data(Data {
name: Some("holeNumber".to_string()),
uom: Some("yd_us".to_string()),
display_name: Some("holeNumberDisplay".to_string()),
value: "1234".to_string(),
attrs: [("anyAttribute".to_string(), "anySimpleType".to_string()),]
.iter()
.cloned()
.collect(),
})
);
}

#[test]
fn test_read_kml_data_without_attrs() {
let kml_str = r##"<Data>
<!-- comment -->
<displayName>holeNumberDisplay</displayName>
<value>1234</value>
</Data>"##;
let a: Kml = kml_str.parse().unwrap();
assert_eq!(
a,
Kml::Data(Data {
name: None,
uom: None,
display_name: Some("holeNumberDisplay".to_string()),
value: "1234".to_string(),
..Default::default()
})
);
}

#[test]
fn test_read_schema_data() {
let kml_str = r##"<SchemaData schemaUrl="#TrailHeadTypeId">
Expand Down
10 changes: 10 additions & 0 deletions src/types/data.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
use std::collections::HashMap;

/// `kml:Data` [9.4](https://docs.ogc.org/is/12-007r2/12-007r2.html#136) in the KML
#[derive(Clone, Default, Debug, PartialEq)]
pub struct Data {
pub name: Option<String>,
pub uom: Option<String>,
pub display_name: Option<String>,
pub value: String,
pub attrs: HashMap<String, String>,
}

/// `kml:SchemaData`, [9.5](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html#155) in the KML specification.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SchemaData {
Expand Down
3 changes: 2 additions & 1 deletion src/types/kml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::str::FromStr;

use crate::errors::Error;
use crate::types::{
Alias, BalloonStyle, CoordType, Element, Folder, Icon, IconStyle, LabelStyle, LineString,
Alias, BalloonStyle, CoordType, Data, Element, Folder, Icon, IconStyle, LabelStyle, LineString,
LineStyle, LinearRing, Link, LinkTypeIcon, ListStyle, Location, MultiGeometry, Orientation,
Pair, Placemark, Point, PolyStyle, Polygon, ResourceMap, Scale, SchemaData, SimpleArrayData,
SimpleData, Style, StyleMap,
Expand Down Expand Up @@ -79,6 +79,7 @@ pub enum Kml<T: CoordType = f64> {
Link(Link),
ResourceMap(ResourceMap),
Alias(Alias),
Data(Data),
SchemaData(SchemaData),
SimpleArrayData(SimpleArrayData),
SimpleData(SimpleData),
Expand Down
2 changes: 1 addition & 1 deletion src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub use alias::Alias;

mod data;

pub use data::{SchemaData, SimpleArrayData, SimpleData};
pub use data::{Data, SchemaData, SimpleArrayData, SimpleData};

mod kml;

Expand Down
108 changes: 107 additions & 1 deletion src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use crate::errors::Error;
use crate::types::geom_props::GeomProps;
use crate::types::{
Alias, BalloonStyle, Coord, CoordType, Element, Folder, Geometry, Icon, IconStyle, Kml,
Alias, BalloonStyle, Coord, CoordType, Data, Element, Folder, Geometry, Icon, IconStyle, Kml,
LabelStyle, LineString, LineStyle, LinearRing, Link, LinkTypeIcon, ListStyle, Location,
MultiGeometry, Orientation, Pair, Placemark, Point, PolyStyle, Polygon, ResourceMap, Scale,
SchemaData, SimpleArrayData, SimpleData, Style, StyleMap,
Expand Down Expand Up @@ -94,6 +94,7 @@ where
Kml::Link(l) => self.write_link(l)?,
Kml::ResourceMap(r) => self.write_resource_map(r)?,
Kml::Alias(a) => self.write_alias(a)?,
Kml::Data(d) => self.write_kml_data(d)?,
Kml::SchemaData(s) => self.write_schema_data(s)?,
Kml::SimpleArrayData(s) => self.write_simple_array_data(s)?,
Kml::SimpleData(s) => self.write_simple_data(s)?,
Expand Down Expand Up @@ -592,6 +593,28 @@ where
.write_event(Event::End(BytesEnd::new("Alias")))?)
}

fn write_kml_data(&mut self, data: &Data) -> Result<(), Error> {
let mut filter_attrs: HashMap<String, String> = HashMap::new();
if let Some(name) = data.name.clone() {
filter_attrs.insert("name".to_string(), name);
}
if let Some(uom) = data.uom.clone() {
filter_attrs.insert("uom".to_string(), uom);
}

self.writer
.write_event(Event::Start(BytesStart::new("Data").with_attributes(
self.hash_map_as_attrs_filtered(&data.attrs, &filter_attrs),
)))?;

if let Some(dn) = &data.display_name {
self.write_text_element("displayName", dn)?;
}

self.write_text_element("value", &data.value.to_string())?;
Ok(self.writer.write_event(Event::End(BytesEnd::new("Data")))?)
}

fn write_schema_data(&mut self, schema_data: &SchemaData) -> Result<(), Error> {
self.writer.write_event(Event::Start(
BytesStart::new("SchemaData")
Expand Down Expand Up @@ -891,6 +914,89 @@ mod tests {
assert_eq!(expected_string, kml.to_string());
}

#[test]
fn test_write_kml_data() {
let kml: Kml<f64> = Kml::Data(Data {
name: Some("holeNumber".to_string()),
uom: Some("yd_us".to_string()),
display_name: Some("holeNumberDisplay".to_string()),
value: "1234".to_string(),
attrs: [
("name".to_string(), "shouldBeRemoved".to_string()),
("uom".to_string(), "shouldBeRemoved".to_string()),
("anyAttribute".to_string(), "anySimpleType".to_string()),
]
.iter()
.cloned()
.collect(),
});
let expected_string_name_uom =
"<Data name=\"holeNumber\" uom=\"yd_us\" anyAttribute=\"anySimpleType\">\
<displayName>holeNumberDisplay</displayName>\
<value>1234</value>\
</Data>";
let expected_string_uom_name =
"<Data uom=\"yd_us\" name=\"holeNumber\" anyAttribute=\"anySimpleType\">\
<displayName>holeNumberDisplay</displayName>\
<value>1234</value>\
</Data>";
let result_kml = kml.to_string();
assert!(
expected_string_name_uom == result_kml || expected_string_uom_name == result_kml,
"{}, {}, {}",
expected_string_name_uom,
expected_string_uom_name,
result_kml
);
}

#[test]
fn test_write_kml_data_without_valid_data() {
let kml: Kml<f64> = Kml::Data(Data {
name: None,
uom: None,
display_name: Some("holeNumberDisplay".to_string()),
value: "1234".to_string(),
attrs: [
("name".to_string(), "holeNumber".to_string()),
("uom".to_string(), "yd_us".to_string()),
("anyAttribute".to_string(), "anySimpleType".to_string()),
]
.iter()
.cloned()
.collect(),
});

let result_kml = kml.to_string();

let attr_pattern = r#"name="holeNumber""#;
let uom_pattern = r#"uom="yd_us""#;
let any_attr_pattern = r#"anyAttribute="anySimpleType""#;

assert!(result_kml.contains(attr_pattern));
assert!(result_kml.contains(uom_pattern));
assert!(result_kml.contains(any_attr_pattern));

assert!(result_kml.contains("<displayName>holeNumberDisplay</displayName>"));
assert!(result_kml.contains("<value>1234</value>"));
}

#[test]
fn test_write_kml_data_without_attrs() {
let kml: Kml<f64> = Kml::Data(Data {
name: None,
uom: None,
display_name: Some("holeNumberDisplay".to_string()),
value: "1234".to_string(),
..Default::default()
});
let expected_string = "<Data>\
<displayName>holeNumberDisplay</displayName>\
<value>1234</value>\
</Data>";
assert_eq!(expected_string, kml.to_string());
}

#[test]
fn test_write_schema_data() {
let kml: Kml<f64> = Kml::SchemaData(SchemaData {
Expand Down
Loading