diff --git a/src/reader.rs b/src/reader.rs index 9824f20..e16055d 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -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, @@ -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)?)) } @@ -795,6 +796,37 @@ where Ok(alias) } + fn read_data(&mut self, mut attrs: HashMap) -> Result { + 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) -> Result { let mut schema_data = SchemaData { attrs, @@ -1371,6 +1403,49 @@ mod tests { ); } + #[test] + fn test_read_kml_data() { + let kml_str = r##" + + holeNumberDisplay + 1234 + "##; + 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##" + + holeNumberDisplay + 1234 + "##; + 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##" diff --git a/src/types/data.rs b/src/types/data.rs index 7dd5420..eb47b04 100644 --- a/src/types/data.rs +++ b/src/types/data.rs @@ -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, + pub uom: Option, + pub display_name: Option, + pub value: String, + pub attrs: HashMap, +} + /// `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 { diff --git a/src/types/kml.rs b/src/types/kml.rs index c405ff8..082d8b0 100644 --- a/src/types/kml.rs +++ b/src/types/kml.rs @@ -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, @@ -79,6 +79,7 @@ pub enum Kml { Link(Link), ResourceMap(ResourceMap), Alias(Alias), + Data(Data), SchemaData(SchemaData), SimpleArrayData(SimpleArrayData), SimpleData(SimpleData), diff --git a/src/types/mod.rs b/src/types/mod.rs index 58d564b..dd9470e 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -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; diff --git a/src/writer.rs b/src/writer.rs index 0b079e8..2eac943 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -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, @@ -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)?, @@ -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 = 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") @@ -891,6 +914,89 @@ mod tests { assert_eq!(expected_string, kml.to_string()); } + #[test] + fn test_write_kml_data() { + let kml: Kml = 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 = + "\ + holeNumberDisplay\ + 1234\ + "; + let expected_string_uom_name = + "\ + holeNumberDisplay\ + 1234\ + "; + 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 = 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("holeNumberDisplay")); + assert!(result_kml.contains("1234")); + } + + #[test] + fn test_write_kml_data_without_attrs() { + let kml: Kml = Kml::Data(Data { + name: None, + uom: None, + display_name: Some("holeNumberDisplay".to_string()), + value: "1234".to_string(), + ..Default::default() + }); + let expected_string = "\ + holeNumberDisplay\ + 1234\ + "; + assert_eq!(expected_string, kml.to_string()); + } + #[test] fn test_write_schema_data() { let kml: Kml = Kml::SchemaData(SchemaData {