1use std::convert::TryFrom;
16use std::str::FromStr;
17
18use crate::errors::{Error, Result};
19use crate::{util, Feature, Geometry, Value};
20use crate::{JsonObject, JsonValue};
21use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
22
23impl From<Geometry> for Feature {
24 fn from(geom: Geometry) -> Feature {
25 Feature {
26 bbox: geom.bbox.clone(),
27 foreign_members: geom.foreign_members.clone(),
28 geometry: Some(geom),
29 id: None,
30 properties: None,
31 }
32 }
33}
34
35impl From<Value> for Feature {
36 fn from(val: Value) -> Feature {
37 Feature {
38 bbox: None,
39 foreign_members: None,
40 geometry: Some(Geometry::from(val)),
41 id: None,
42 properties: None,
43 }
44 }
45}
46
47impl FromStr for Feature {
48 type Err = Error;
49
50 fn from_str(s: &str) -> Result<Self> {
51 Self::try_from(crate::GeoJson::from_str(s)?)
52 }
53}
54
55impl<'a> From<&'a Feature> for JsonObject {
56 fn from(feature: &'a Feature) -> JsonObject {
57 match serde_json::to_value(feature).unwrap() {
59 serde_json::Value::Object(obj) => obj,
60 value => {
61 panic!(
64 "serializing Feature should result in an Object, but got something {:?}",
65 value
66 )
67 }
68 }
69 }
70}
71
72impl Feature {
73 pub fn from_json_object(object: JsonObject) -> Result<Self> {
74 Self::try_from(object)
75 }
76
77 pub fn from_json_value(value: JsonValue) -> Result<Self> {
78 Self::try_from(value)
79 }
80
81 pub fn property(&self, key: impl AsRef<str>) -> Option<&JsonValue> {
83 self.properties
84 .as_ref()
85 .and_then(|props| props.get(key.as_ref()))
86 }
87
88 pub fn contains_property(&self, key: impl AsRef<str>) -> bool {
90 match &self.properties {
91 None => false,
92 Some(props) => props.contains_key(key.as_ref()),
93 }
94 }
95
96 pub fn set_property(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) {
98 let key: String = key.into();
99 let value: JsonValue = value.into();
100 if self.properties.is_none() {
101 self.properties = Some(JsonObject::new());
102 }
103
104 self.properties.as_mut().unwrap().insert(key, value);
105 }
106
107 pub fn remove_property(&mut self, key: impl AsRef<str>) -> Option<JsonValue> {
110 self.properties
111 .as_mut()
112 .and_then(|props| props.remove(key.as_ref()))
113 }
114
115 pub fn len_properties(&self) -> usize {
117 match &self.properties {
118 None => 0,
119 Some(props) => props.len(),
120 }
121 }
122
123 pub fn properties_iter(&self) -> Box<dyn ExactSizeIterator<Item = (&String, &JsonValue)> + '_> {
125 match self.properties.as_ref() {
126 None => Box::new(std::iter::empty()),
127 Some(props) => Box::new(props.iter()),
128 }
129 }
130}
131
132impl TryFrom<JsonObject> for Feature {
133 type Error = Error;
134
135 fn try_from(mut object: JsonObject) -> Result<Self> {
136 let res = &*util::expect_type(&mut object)?;
137 match res {
138 "Feature" => Ok(Feature {
139 geometry: util::get_geometry(&mut object)?,
140 properties: util::get_properties(&mut object)?,
141 id: util::get_id(&mut object)?,
142 bbox: util::get_bbox(&mut object)?,
143 foreign_members: util::get_foreign_members(object)?,
144 }),
145 _ => Err(Error::NotAFeature(res.to_string())),
146 }
147 }
148}
149
150impl TryFrom<JsonValue> for Feature {
151 type Error = Error;
152
153 fn try_from(value: JsonValue) -> Result<Self> {
154 if let JsonValue::Object(obj) = value {
155 Self::try_from(obj)
156 } else {
157 Err(Error::GeoJsonExpectedObject(value))
158 }
159 }
160}
161
162impl Serialize for Feature {
163 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
164 where
165 S: Serializer,
166 {
167 let mut map = serializer.serialize_map(None)?;
168 map.serialize_entry("type", "Feature")?;
169 map.serialize_entry("geometry", &self.geometry)?;
170 map.serialize_entry("properties", &self.properties)?;
171 if let Some(ref bbox) = self.bbox {
172 map.serialize_entry("bbox", bbox)?;
173 }
174 if let Some(ref id) = self.id {
175 map.serialize_entry("id", id)?;
176 }
177 if let Some(ref foreign_members) = self.foreign_members {
178 for (key, value) in foreign_members {
179 map.serialize_entry(key, value)?;
180 }
181 }
182 map.end()
183 }
184}
185
186impl<'de> Deserialize<'de> for Feature {
187 fn deserialize<D>(deserializer: D) -> std::result::Result<Feature, D::Error>
188 where
189 D: Deserializer<'de>,
190 {
191 use serde::de::Error as SerdeError;
192
193 let val = JsonObject::deserialize(deserializer)?;
194
195 Feature::from_json_object(val).map_err(|e| D::Error::custom(e.to_string()))
196 }
197}
198
199#[derive(Clone, Debug, PartialEq)]
203pub enum Id {
204 String(String),
205 Number(serde_json::Number),
206}
207
208impl Serialize for Id {
209 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
210 where
211 S: Serializer,
212 {
213 match self {
214 Id::String(ref s) => s.serialize(serializer),
215 Id::Number(ref n) => n.serialize(serializer),
216 }
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use crate::JsonObject;
223 use crate::{feature, Error, Feature, GeoJson, Geometry, Value};
224 use serde_json::json;
225
226 use std::str::FromStr;
227
228 fn feature_json_str() -> &'static str {
229 "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.1,2.1]},\"properties\":{}}"
230 }
231
232 fn properties() -> Option<JsonObject> {
233 Some(JsonObject::new())
234 }
235
236 fn feature() -> Feature {
237 crate::Feature {
238 geometry: Some(Geometry {
239 value: value(),
240 bbox: None,
241 foreign_members: None,
242 }),
243 properties: properties(),
244 bbox: None,
245 id: None,
246 foreign_members: None,
247 }
248 }
249
250 fn value() -> Value {
251 Value::Point(vec![1.1, 2.1])
252 }
253
254 fn geometry() -> Geometry {
255 Geometry::new(value())
256 }
257
258 fn encode(feature: &Feature) -> String {
259 serde_json::to_string(&feature).unwrap()
260 }
261
262 fn decode(json_string: String) -> GeoJson {
263 json_string.parse().unwrap()
264 }
265
266 #[test]
267 fn encode_decode_feature() {
268 let feature = feature();
269
270 let json_string = encode(&feature);
272 assert_eq!(json_string, feature_json_str());
273
274 let decoded_feature = match decode(json_string) {
276 GeoJson::Feature(f) => f,
277 _ => unreachable!(),
278 };
279 assert_eq!(decoded_feature, feature);
280 }
281
282 #[test]
283 fn try_from_value() {
284 use serde_json::json;
285 use std::convert::TryInto;
286
287 let json_value = json!({
288 "type": "Feature",
289 "geometry": {
290 "type": "Point",
291 "coordinates": [1.1, 2.1]
292 },
293 "properties": null,
294 });
295 assert!(json_value.is_object());
296
297 let feature: Feature = json_value.try_into().unwrap();
298 assert_eq!(
299 feature,
300 Feature {
301 bbox: None,
302 geometry: Some(geometry()),
303 id: None,
304 properties: None,
305 foreign_members: None,
306 }
307 )
308 }
309
310 #[test]
311 fn null_bbox() {
312 let geojson_str = r#"{
313 "geometry": null,
314 "bbox": null,
315 "properties":{},
316 "type":"Feature"
317 }"#;
318 let geojson = geojson_str.parse::<GeoJson>().unwrap();
319 let feature = match geojson {
320 GeoJson::Feature(feature) => feature,
321 _ => unimplemented!(),
322 };
323 assert!(feature.bbox.is_none());
324 }
325
326 #[test]
327 fn test_display_feature() {
328 let f = feature().to_string();
329 assert_eq!(f, "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.1,2.1]},\"properties\":{}}");
330 }
331
332 #[test]
333 fn feature_json_null_geometry() {
334 let geojson_str = r#"{
335 "geometry": null,
336 "properties":{},
337 "type":"Feature"
338 }"#;
339 let geojson = geojson_str.parse::<GeoJson>().unwrap();
340 let feature = match geojson {
341 GeoJson::Feature(feature) => feature,
342 _ => unimplemented!(),
343 };
344 assert!(feature.geometry.is_none());
345 }
346
347 #[test]
348 fn feature_json_invalid_geometry() {
349 let geojson_str = r#"{"geometry":3.14,"properties":{},"type":"Feature"}"#;
350 match geojson_str.parse::<GeoJson>().unwrap_err() {
351 Error::FeatureInvalidGeometryValue(_) => (),
352 _ => unreachable!(),
353 }
354 }
355
356 #[test]
357 fn encode_decode_feature_with_id_number() {
358 let feature_json_str = "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.1,2.1]},\"properties\":{},\"id\":0}";
359 let feature = crate::Feature {
360 geometry: Some(Geometry {
361 value: Value::Point(vec![1.1, 2.1]),
362 bbox: None,
363 foreign_members: None,
364 }),
365 properties: properties(),
366 bbox: None,
367 id: Some(feature::Id::Number(0.into())),
368 foreign_members: None,
369 };
370 let json_string = encode(&feature);
372 assert_eq!(json_string, feature_json_str);
373
374 let decoded_feature = match decode(feature_json_str.into()) {
376 GeoJson::Feature(f) => f,
377 _ => unreachable!(),
378 };
379 assert_eq!(decoded_feature, feature);
380 }
381
382 #[test]
383 fn encode_decode_feature_with_id_string() {
384 let feature_json_str = "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.1,2.1]},\"properties\":{},\"id\":\"foo\"}";
385 let feature = crate::Feature {
386 geometry: Some(Geometry {
387 value: Value::Point(vec![1.1, 2.1]),
388 bbox: None,
389 foreign_members: None,
390 }),
391 properties: properties(),
392 bbox: None,
393 id: Some(feature::Id::String("foo".into())),
394 foreign_members: None,
395 };
396 let json_string = encode(&feature);
398 assert_eq!(json_string, feature_json_str);
399
400 let decoded_feature = match decode(feature_json_str.into()) {
402 GeoJson::Feature(f) => f,
403 _ => unreachable!(),
404 };
405 assert_eq!(decoded_feature, feature);
406 }
407
408 #[test]
409 fn decode_feature_with_invalid_id_type_object() {
410 let feature_json_str = "{\"geometry\":{\"coordinates\":[1.1,2.1],\"type\":\"Point\"},\"id\":{},\"properties\":{},\"type\":\"Feature\"}";
411 assert!(matches!(
412 feature_json_str.parse::<GeoJson>(),
413 Err(Error::FeatureInvalidIdentifierType(_))
414 ));
415 }
416
417 #[test]
418 fn decode_feature_with_invalid_id_type_null() {
419 let feature_json_str = "{\"geometry\":{\"coordinates\":[1.1,2.1],\"type\":\"Point\"},\"id\":null,\"properties\":{},\"type\":\"Feature\"}";
420 assert!(matches!(
421 feature_json_str.parse::<GeoJson>(),
422 Err(Error::FeatureInvalidIdentifierType(_))
423 ));
424 }
425
426 #[test]
427 fn encode_decode_feature_with_foreign_member() {
428 use crate::JsonObject;
429 use serde_json;
430 let feature_json_str = "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.1,2.1]},\"properties\":{},\"other_member\":\"some_value\"}";
431
432 let mut foreign_members = JsonObject::new();
433 foreign_members.insert(
434 String::from("other_member"),
435 serde_json::to_value("some_value").unwrap(),
436 );
437 let feature = crate::Feature {
438 geometry: Some(Geometry {
439 value: Value::Point(vec![1.1, 2.1]),
440 bbox: None,
441 foreign_members: None,
442 }),
443 properties: properties(),
444 bbox: None,
445 id: None,
446 foreign_members: Some(foreign_members),
447 };
448 let json_string = encode(&feature);
450 assert_eq!(json_string, feature_json_str);
451
452 let decoded_feature = match decode(feature_json_str.into()) {
454 GeoJson::Feature(f) => f,
455 _ => unreachable!(),
456 };
457 assert_eq!(decoded_feature, feature);
458 }
459
460 #[test]
461 fn encode_decode_feature_with_null_properties() {
462 let feature_json_str = r#"{"type":"Feature","geometry":{"type":"Point","coordinates":[1.1,2.1]},"properties":null}"#;
463
464 let feature = crate::Feature {
465 geometry: Some(Value::Point(vec![1.1, 2.1]).into()),
466 properties: None,
467 bbox: None,
468 id: None,
469 foreign_members: None,
470 };
471 let json_string = encode(&feature);
473 assert_eq!(json_string, feature_json_str);
474
475 let decoded_feature = match decode(feature_json_str.into()) {
477 GeoJson::Feature(f) => f,
478 _ => unreachable!(),
479 };
480 assert_eq!(decoded_feature, feature);
481 }
482
483 #[test]
484 fn feature_ergonomic_property_access() {
485 use serde_json::json;
486
487 let mut feature = feature();
488
489 assert_eq!(feature.len_properties(), 0);
490 assert_eq!(feature.property("foo"), None);
491 assert!(!feature.contains_property("foo"));
492 assert_eq!(feature.properties_iter().collect::<Vec<_>>(), vec![]);
493
494 feature.set_property("foo", 12);
495 assert_eq!(feature.property("foo"), Some(&json!(12)));
496 assert_eq!(feature.len_properties(), 1);
497 assert!(feature.contains_property("foo"));
498 assert_eq!(
499 feature.properties_iter().collect::<Vec<_>>(),
500 vec![(&"foo".to_string(), &json!(12))]
501 );
502
503 assert_eq!(Some(json!(12)), feature.remove_property("foo"));
504 assert_eq!(feature.property("foo"), None);
505 assert_eq!(feature.len_properties(), 0);
506 assert!(!feature.contains_property("foo"));
507 assert_eq!(feature.properties_iter().collect::<Vec<_>>(), vec![]);
508 }
509
510 #[test]
511 fn test_from_str_ok() {
512 let feature_json = json!({
513 "type": "Feature",
514 "geometry": {
515 "type": "Point",
516 "coordinates": [125.6, 10.1]
517 },
518 "properties": {
519 "name": "Dinagat Islands"
520 }
521 })
522 .to_string();
523
524 let feature = Feature::from_str(&feature_json).unwrap();
525 assert_eq!("Dinagat Islands", feature.property("name").unwrap());
526 }
527
528 #[test]
529 fn test_from_str_with_unexpected_type() {
530 let geometry_json = json!({
531 "type": "Point",
532 "coordinates": [125.6, 10.1]
533 })
534 .to_string();
535
536 let actual_failure = Feature::from_str(&geometry_json).unwrap_err();
537 match actual_failure {
538 Error::ExpectedType { actual, expected } => {
539 assert_eq!(actual, "Geometry");
540 assert_eq!(expected, "Feature");
541 }
542 e => panic!("unexpected error: {}", e),
543 };
544 }
545}