1#[cfg(feature = "unstable-v2")]
9use std::collections::BTreeMap;
10use std::num::NonZeroU32;
11
12use geo_types::{Geometry, LineString};
13
14use crate::{MltError, MltResult};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub struct Extent(NonZeroU32);
22
23impl Extent {
24 pub fn new(value: u32) -> MltResult<Self> {
25 NonZeroU32::new(value)
26 .map(Self)
27 .ok_or(MltError::InvalidExtent(value))
28 }
29
30 #[must_use]
31 pub fn get(self) -> u32 {
32 self.0.get()
33 }
34}
35
36impl From<Extent> for NonZeroU32 {
37 fn from(value: Extent) -> Self {
38 value.0
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display)]
44pub enum ColumnRole {
45 #[strum(serialize = "property")]
47 Property,
48 #[cfg(feature = "unstable-v2")]
50 #[strum(serialize = "m-value")]
51 MValue,
52 #[cfg(feature = "unstable-v2")]
54 #[strum(serialize = "nested")]
55 Nested,
56}
57
58#[derive(Debug, Clone, PartialEq)]
64pub struct TileLayer {
65 pub(crate) name: String,
66 pub(crate) extent: Extent,
67 pub(crate) property_names: Vec<String>,
69 pub(crate) property_kinds: Vec<PropKind>,
71 #[cfg(feature = "unstable-v2")]
73 pub(crate) m_value_names: Vec<String>,
74 #[cfg(feature = "unstable-v2")]
76 pub(crate) m_value_kinds: Vec<PropKind>,
77 #[cfg(feature = "unstable-v2")]
79 pub(crate) nested_names: Vec<String>,
80 #[cfg(feature = "unstable-v2")]
82 pub(crate) nested_kinds: Vec<NestedKind>,
83 pub(crate) features: Vec<TileFeature>,
84}
85
86#[derive(Debug, Clone, PartialEq)]
88pub struct TileFeature {
89 pub(crate) id: Option<u64>,
90 pub(crate) geometry: Geometry<i32>,
92 pub(crate) properties: Vec<PropValue>,
95 #[cfg(feature = "unstable-v2")]
99 pub(crate) m_values: Vec<MValue>,
100 #[cfg(feature = "unstable-v2")]
102 pub(crate) nested: Vec<NestedValue>,
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106pub struct PropertyKey(usize);
107
108impl PropertyKey {
109 #[must_use]
110 pub fn index(self) -> usize {
111 self.0
112 }
113}
114
115#[cfg(feature = "unstable-v2")]
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118pub struct MValueKey(usize);
119
120#[cfg(feature = "unstable-v2")]
121impl MValueKey {
122 #[must_use]
123 pub fn index(self) -> usize {
124 self.0
125 }
126}
127
128#[cfg(feature = "unstable-v2")]
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131pub struct NestedKey(usize);
132
133#[cfg(feature = "unstable-v2")]
134impl NestedKey {
135 #[must_use]
136 pub fn index(self) -> usize {
137 self.0
138 }
139}
140
141#[cfg(feature = "unstable-v2")]
143pub(crate) const MAX_NESTED_DEPTH: usize = 8;
144
145#[cfg(feature = "unstable-v2")]
147#[derive(Debug, Clone, PartialEq, Eq)]
148pub enum NestedKind {
149 Leaf(PropKind),
150 List(Box<Self>),
151 Map(BTreeMap<String, Self>),
153}
154
155#[cfg(feature = "unstable-v2")]
156impl NestedKind {
157 #[must_use]
159 pub fn map<K: Into<String>>(fields: impl IntoIterator<Item = (K, Self)>) -> Self {
160 Self::Map(fields.into_iter().map(|(k, v)| (k.into(), v)).collect())
161 }
162
163 #[must_use]
165 pub fn list(element: Self) -> Self {
166 Self::List(Box::new(element))
167 }
168
169 #[must_use]
171 pub fn depth(&self) -> usize {
172 1 + match self {
173 Self::Leaf(_) => 0,
174 Self::List(element) => element.depth(),
175 Self::Map(fields) => fields.values().map(Self::depth).max().unwrap_or(0),
176 }
177 }
178
179 #[must_use]
181 pub fn null_value(&self) -> NestedValue {
182 match self {
183 Self::Leaf(kind) => NestedValue::Leaf(PropValue::null(*kind)),
184 Self::List(_) => NestedValue::List(None),
185 Self::Map(_) => NestedValue::Map(None),
186 }
187 }
188
189 #[must_use]
193 pub fn accepts(&self, value: &NestedValue) -> bool {
194 match (self, value) {
195 (Self::Leaf(kind), NestedValue::Leaf(value)) => value.kind() == *kind,
196 (Self::List(_), NestedValue::List(None)) | (Self::Map(_), NestedValue::Map(None)) => {
197 true
198 }
199 (Self::List(element), NestedValue::List(Some(items))) => {
200 items.iter().all(|item| element.accepts(item))
201 }
202 (Self::Map(fields), NestedValue::Map(Some(entries))) => entries
203 .iter()
204 .all(|(key, value)| fields.get(key).is_some_and(|kind| kind.accepts(value))),
205 _ => false,
206 }
207 }
208
209 fn has_empty_map(&self) -> bool {
211 match self {
212 Self::Leaf(_) => false,
213 Self::List(element) => element.has_empty_map(),
214 Self::Map(fields) => fields.is_empty() || fields.values().any(Self::has_empty_map),
215 }
216 }
217}
218
219#[cfg(feature = "unstable-v2")]
223#[derive(Debug, Clone, PartialEq)]
224pub enum NestedValue {
225 Leaf(PropValue),
226 List(Option<Vec<Self>>),
227 Map(Option<BTreeMap<String, Self>>),
228}
229
230#[cfg(feature = "unstable-v2")]
231impl NestedValue {
232 #[must_use]
234 pub fn map<K: Into<String>>(entries: impl IntoIterator<Item = (K, Self)>) -> Self {
235 Self::Map(Some(
236 entries.into_iter().map(|(k, v)| (k.into(), v)).collect(),
237 ))
238 }
239
240 #[must_use]
242 pub fn list(items: impl IntoIterator<Item = Self>) -> Self {
243 Self::List(Some(items.into_iter().collect()))
244 }
245
246 #[must_use]
248 pub fn is_null(&self) -> bool {
249 match self {
250 Self::Leaf(value) => value.is_null(),
251 Self::List(items) => items.is_none(),
252 Self::Map(entries) => entries.is_none(),
253 }
254 }
255
256 #[must_use]
258 pub(crate) fn heap_bytes(&self) -> usize {
259 match self {
260 Self::Leaf(PropValue::Str(Some(value))) => value.len(),
261 Self::Leaf(_) => size_of::<PropValue>(),
262 Self::List(items) => items.iter().flatten().map(Self::heap_bytes).sum::<usize>(),
263 Self::Map(entries) => entries
264 .iter()
265 .flatten()
266 .map(|(key, value)| key.len() + value.heap_bytes())
267 .sum(),
268 }
269 }
270
271 fn same_shape(&self, other: &Self) -> bool {
274 matches!(
275 (self, other),
276 (Self::Leaf(_), Self::Leaf(_))
277 | (Self::List(_), Self::List(_))
278 | (Self::Map(_), Self::Map(_))
279 )
280 }
281}
282
283impl TileLayer {
284 pub fn new(name: impl Into<String>, extent: u32) -> MltResult<Self> {
285 Self::with_capacity(name, extent, 0)
286 }
287
288 pub fn with_capacity(name: impl Into<String>, extent: u32, features: usize) -> MltResult<Self> {
289 let name = name.into();
290 validate_layer_name(&name)?;
291 let extent = Extent::new(extent)?;
292 Ok(Self {
293 name,
294 extent,
295 property_names: Vec::new(),
296 property_kinds: Vec::new(),
297 #[cfg(feature = "unstable-v2")]
298 m_value_names: Vec::new(),
299 #[cfg(feature = "unstable-v2")]
300 m_value_kinds: Vec::new(),
301 #[cfg(feature = "unstable-v2")]
302 nested_names: Vec::new(),
303 #[cfg(feature = "unstable-v2")]
304 nested_kinds: Vec::new(),
305 features: Vec::with_capacity(features),
306 })
307 }
308
309 pub(crate) fn from_parts(
310 name: impl Into<String>,
311 extent: u32,
312 property_names: Vec<String>,
313 features: Vec<TileFeature>,
314 ) -> MltResult<Self> {
315 let name = name.into();
316 validate_layer_name(&name)?;
317 let extent = Extent::new(extent)?;
318 validate_unique_names(&property_names, ColumnRole::Property)?;
319 let property_kinds = infer_kinds::<PropValue>(&property_names, &features)?;
320 let layer = Self {
321 name,
322 extent,
323 property_names,
324 property_kinds,
325 #[cfg(feature = "unstable-v2")]
326 m_value_names: Vec::new(),
327 #[cfg(feature = "unstable-v2")]
328 m_value_kinds: Vec::new(),
329 #[cfg(feature = "unstable-v2")]
330 nested_names: Vec::new(),
331 #[cfg(feature = "unstable-v2")]
332 nested_kinds: Vec::new(),
333 features,
334 };
335 Ok(layer)
336 }
337
338 #[must_use]
339 pub fn name(&self) -> &str {
340 &self.name
341 }
342
343 #[must_use]
344 pub fn extent(&self) -> Extent {
345 self.extent
346 }
347
348 #[must_use]
349 pub fn property_names(&self) -> &[String] {
350 &self.property_names
351 }
352
353 #[must_use]
354 pub fn features(&self) -> &[TileFeature] {
355 &self.features
356 }
357
358 #[must_use]
359 pub(crate) fn features_mut(&mut self) -> &mut [TileFeature] {
360 &mut self.features
361 }
362
363 #[must_use]
364 pub fn feature_count(&self) -> usize {
365 self.features.len()
366 }
367
368 fn reject_name_in_use(&self, name: &str, role: ColumnRole) -> MltResult<()> {
370 let taken = self
371 .property_names
372 .iter()
373 .map(|n| (n.as_str(), ColumnRole::Property));
374 #[cfg(feature = "unstable-v2")]
375 let taken = taken
376 .chain(
377 self.m_value_names
378 .iter()
379 .map(|n| (n.as_str(), ColumnRole::MValue)),
380 )
381 .chain(
382 self.nested_names
383 .iter()
384 .map(|n| (n.as_str(), ColumnRole::Nested)),
385 );
386 reject_taken_name(name, role, taken)
387 }
388
389 pub fn add_property(
390 &mut self,
391 name: impl Into<String>,
392 kind: PropKind,
393 ) -> MltResult<PropertyKey> {
394 let name = name.into();
395 self.reject_name_in_use(&name, ColumnRole::Property)?;
396 for feature in &mut self.features {
397 feature.properties.push(PropValue::null(kind));
398 }
399 self.property_names.push(name);
400 self.property_kinds.push(kind);
401 Ok(PropertyKey(self.property_names.len() - 1))
402 }
403
404 #[cfg(feature = "unstable-v2")]
405 #[must_use]
406 pub fn m_value_names(&self) -> &[String] {
407 &self.m_value_names
408 }
409
410 #[cfg(feature = "unstable-v2")]
411 #[must_use]
412 pub fn m_value_kinds(&self) -> &[PropKind] {
413 &self.m_value_kinds
414 }
415
416 #[cfg(feature = "unstable-v2")]
418 pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
419 let name = name.into();
420 self.reject_name_in_use(&name, ColumnRole::MValue)?;
421 for feature in &mut self.features {
422 feature.m_values.push(MValue::null(kind));
423 }
424 self.m_value_names.push(name);
425 self.m_value_kinds.push(kind);
426 Ok(MValueKey(self.m_value_names.len() - 1))
427 }
428
429 #[cfg(feature = "unstable-v2")]
432 pub(crate) fn with_m_value_names(mut self, names: Vec<String>) -> MltResult<Self> {
433 validate_unique_names(&names, ColumnRole::MValue)?;
434 for name in &names {
435 self.reject_name_in_use(name, ColumnRole::MValue)?;
436 }
437 self.m_value_kinds = infer_kinds::<MValue>(&names, &self.features)?;
438 self.m_value_names = names;
439 for feature in &self.features {
440 self.validate_m_values(feature)?;
441 }
442 Ok(self)
443 }
444
445 #[cfg(feature = "unstable-v2")]
446 #[must_use]
447 pub fn nested_names(&self) -> &[String] {
448 &self.nested_names
449 }
450
451 #[cfg(feature = "unstable-v2")]
452 #[must_use]
453 pub fn nested_kinds(&self) -> &[NestedKind] {
454 &self.nested_kinds
455 }
456
457 #[cfg(feature = "unstable-v2")]
461 pub fn add_nested(
462 &mut self,
463 name: impl Into<String>,
464 kind: NestedKind,
465 ) -> MltResult<NestedKey> {
466 let name = name.into();
467 self.reject_name_in_use(&name, ColumnRole::Nested)?;
468 validate_nested_kind(&name, &kind)?;
469 for feature in &mut self.features {
470 feature.nested.push(kind.null_value());
471 }
472 self.nested_names.push(name);
473 self.nested_kinds.push(kind);
474 Ok(NestedKey(self.nested_names.len() - 1))
475 }
476
477 #[cfg(feature = "unstable-v2")]
480 pub(crate) fn with_nested(
481 mut self,
482 names: Vec<String>,
483 kinds: Vec<NestedKind>,
484 ) -> MltResult<Self> {
485 validate_unique_names(&names, ColumnRole::Nested)?;
486 for name in &names {
487 self.reject_name_in_use(name, ColumnRole::Nested)?;
488 }
489 for (name, kind) in names.iter().zip(&kinds) {
490 validate_nested_kind(name, kind)?;
491 }
492 self.nested_names = names;
493 self.nested_kinds = kinds;
494 for feature in &self.features {
495 self.validate_nested(feature)?;
496 }
497 Ok(self)
498 }
499
500 pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
501 self.validate_feature(&feature)?;
502 self.features.push(feature);
503 Ok(())
504 }
505
506 pub fn builder(name: impl Into<String>, extent: u32) -> MltResult<TileLayerBuilder> {
507 Ok(TileLayerBuilder {
508 layer: Self::new(name, extent)?,
509 })
510 }
511
512 fn validate_feature(&self, feature: &TileFeature) -> MltResult<()> {
513 validate_kinds::<PropValue>(&self.property_kinds, feature)?;
514 #[cfg(feature = "unstable-v2")]
515 self.validate_m_values(feature)?;
516 #[cfg(feature = "unstable-v2")]
517 self.validate_nested(feature)?;
518 Ok(())
519 }
520
521 #[cfg(feature = "unstable-v2")]
524 fn validate_nested(&self, feature: &TileFeature) -> MltResult<()> {
525 if feature.nested.len() != self.nested_kinds.len() {
526 return Err(MltError::NestedColumnCountMismatch {
527 expected: self.nested_kinds.len(),
528 actual: feature.nested.len(),
529 });
530 }
531 for (index, (value, kind)) in feature.nested.iter().zip(&self.nested_kinds).enumerate() {
532 if !kind.accepts(value) {
533 return Err(MltError::NestedValueMismatch {
534 index,
535 name: self.nested_names[index].clone(),
536 });
537 }
538 }
539 Ok(())
540 }
541
542 #[cfg(feature = "unstable-v2")]
545 fn validate_m_values(&self, feature: &TileFeature) -> MltResult<()> {
546 validate_kinds::<MValue>(&self.m_value_kinds, feature)?;
547 let vertices = feature.vertex_count();
548 for (index, m_value) in feature.m_values.iter().enumerate() {
549 if let Some(len) = m_value.count()
550 && len != vertices
551 {
552 return Err(MltError::MValueVertexCountMismatch {
553 index,
554 expected: vertices,
555 actual: len,
556 });
557 }
558 }
559 Ok(())
560 }
561}
562
563impl TileFeature {
564 #[must_use]
565 pub fn new(geometry: Geometry<i32>) -> Self {
566 Self {
567 id: None,
568 geometry,
569 properties: Vec::new(),
570 #[cfg(feature = "unstable-v2")]
571 m_values: Vec::new(),
572 #[cfg(feature = "unstable-v2")]
573 nested: Vec::new(),
574 }
575 }
576
577 #[must_use]
578 pub fn with_id(geometry: Geometry<i32>, id: u64) -> Self {
579 Self {
580 id: Some(id),
581 geometry,
582 properties: Vec::new(),
583 #[cfg(feature = "unstable-v2")]
584 m_values: Vec::new(),
585 #[cfg(feature = "unstable-v2")]
586 nested: Vec::new(),
587 }
588 }
589
590 #[must_use]
591 pub fn id(&self) -> Option<u64> {
592 self.id
593 }
594
595 #[must_use]
596 pub fn geometry(&self) -> &Geometry<i32> {
597 &self.geometry
598 }
599
600 #[must_use]
601 pub fn properties(&self) -> &[PropValue] {
602 &self.properties
603 }
604
605 #[must_use]
606 pub(crate) fn properties_mut(&mut self) -> &mut [PropValue] {
607 &mut self.properties
608 }
609
610 #[cfg(feature = "unstable-v2")]
615 #[must_use]
616 pub fn vertex_count(&self) -> usize {
617 stored_vertex_count(&self.geometry)
618 }
619
620 #[cfg(feature = "unstable-v2")]
621 #[must_use]
622 pub fn m_values(&self) -> &[MValue] {
623 &self.m_values
624 }
625
626 #[cfg(feature = "unstable-v2")]
629 pub fn set_m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<()> {
630 let vertices = self.vertex_count();
631 let Some(slot) = self.m_values.get_mut(key.index()) else {
632 return Err(MltError::UnknownMValueKey {
633 index: key.index(),
634 columns: self.m_values.len(),
635 });
636 };
637 let expected = slot.kind();
638 let actual = value.kind();
639 if actual != expected {
640 return Err(MltError::MValueKindMismatch {
641 index: key.index(),
642 expected,
643 actual,
644 });
645 }
646 if let Some(len) = value.count()
647 && len != vertices
648 {
649 return Err(MltError::MValueVertexCountMismatch {
650 index: key.index(),
651 expected: vertices,
652 actual: len,
653 });
654 }
655 *slot = value;
656 Ok(())
657 }
658
659 #[cfg(feature = "unstable-v2")]
660 #[must_use]
661 pub fn nested(&self) -> &[NestedValue] {
662 &self.nested
663 }
664
665 #[cfg(feature = "unstable-v2")]
671 pub fn set_nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<()> {
672 let columns = self.nested.len();
673 let Some(slot) = self.nested.get_mut(key.index()) else {
674 return Err(MltError::UnknownNestedKey {
675 index: key.index(),
676 columns,
677 });
678 };
679 if !slot.same_shape(&value) {
680 return Err(MltError::NestedShapeMismatch { index: key.index() });
681 }
682 *slot = value;
683 Ok(())
684 }
685
686 pub fn set_property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<()> {
687 let Some(prop) = self.properties.get_mut(key.index()) else {
688 return Err(MltError::PropertyLengthMismatch {
689 expected: key.index() + 1,
690 actual: self.properties.len(),
691 });
692 };
693 let expected = PropKind::from(&*prop);
694 let actual = PropKind::from(&value);
695 if actual != expected {
696 return Err(MltError::PropertyKindMismatch {
697 index: key.index(),
698 expected,
699 actual,
700 });
701 }
702 *prop = value;
703 Ok(())
704 }
705}
706
707pub struct TileLayerBuilder {
708 layer: TileLayer,
709}
710
711impl TileLayerBuilder {
712 pub fn add_property(
713 &mut self,
714 name: impl Into<String>,
715 kind: PropKind,
716 ) -> MltResult<PropertyKey> {
717 self.layer.add_property(name, kind)
718 }
719
720 #[cfg(feature = "unstable-v2")]
721 pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
722 self.layer.add_m_value(name, kind)
723 }
724
725 #[cfg(feature = "unstable-v2")]
726 pub fn add_nested(
727 &mut self,
728 name: impl Into<String>,
729 kind: NestedKind,
730 ) -> MltResult<NestedKey> {
731 self.layer.add_nested(name, kind)
732 }
733
734 pub fn feature(&mut self, geometry: Geometry<i32>) -> TileFeatureBuilder<'_> {
735 let properties = self
736 .layer
737 .property_kinds
738 .iter()
739 .copied()
740 .map(PropValue::null)
741 .collect();
742 #[cfg(feature = "unstable-v2")]
743 let m_values = self
744 .layer
745 .m_value_kinds
746 .iter()
747 .copied()
748 .map(MValue::null)
749 .collect();
750 #[cfg(feature = "unstable-v2")]
751 let nested = self
752 .layer
753 .nested_kinds
754 .iter()
755 .map(NestedKind::null_value)
756 .collect();
757 TileFeatureBuilder {
758 layer: self,
759 feature: TileFeature {
760 id: None,
761 geometry,
762 properties,
763 #[cfg(feature = "unstable-v2")]
764 m_values,
765 #[cfg(feature = "unstable-v2")]
766 nested,
767 },
768 }
769 }
770
771 pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
772 self.layer.push_feature(feature)
773 }
774
775 #[must_use]
776 pub fn finish(self) -> TileLayer {
777 self.layer
778 }
779}
780
781pub struct TileFeatureBuilder<'a> {
782 layer: &'a mut TileLayerBuilder,
783 feature: TileFeature,
784}
785
786impl TileFeatureBuilder<'_> {
787 pub fn id(&mut self, id: Option<u64>) -> &mut Self {
788 self.feature.id = id;
789 self
790 }
791
792 pub fn property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<&mut Self> {
793 self.feature.set_property(key, value)?;
794 Ok(self)
795 }
796
797 #[cfg(feature = "unstable-v2")]
798 pub fn m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<&mut Self> {
799 self.feature.set_m_value(key, value)?;
800 Ok(self)
801 }
802
803 #[cfg(feature = "unstable-v2")]
804 pub fn nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<&mut Self> {
805 self.feature.set_nested(key, value)?;
806 Ok(self)
807 }
808
809 pub fn finish(self) -> MltResult<()> {
810 self.layer.push_feature(self.feature)
811 }
812}
813
814#[derive(Debug, Clone, PartialEq)]
822pub enum PropValue {
823 Bool(Option<bool>),
824 I8(Option<i8>),
825 U8(Option<u8>),
826 I32(Option<i32>),
827 U32(Option<u32>),
828 I64(Option<i64>),
829 U64(Option<u64>),
830 F32(Option<f32>),
831 F64(Option<f64>),
832 Str(Option<String>),
833}
834
835impl PropValue {
836 #[must_use]
837 pub fn kind(&self) -> PropKind {
838 self.into()
839 }
840}
841
842#[cfg(feature = "unstable-v2")]
847#[derive(Debug, Clone, PartialEq)]
848pub enum MValue {
849 Bool(Option<Vec<bool>>),
850 I8(Option<Vec<i8>>),
851 U8(Option<Vec<u8>>),
852 I32(Option<Vec<i32>>),
853 U32(Option<Vec<u32>>),
854 I64(Option<Vec<i64>>),
855 U64(Option<Vec<u64>>),
856 F32(Option<Vec<f32>>),
857 F64(Option<Vec<f64>>),
858 Str(Option<Vec<String>>),
859}
860
861#[cfg(feature = "unstable-v2")]
862impl MValue {
863 #[must_use]
864 pub fn is_null(&self) -> bool {
865 self.count().is_none()
866 }
867}
868
869macro_rules! kind_mappings {
870 (
871 scalar { $($sv:ident),* $(,)? }
872 string { $($gv:ident),* $(,)? }
873 ) => {
874 #[derive(Debug, Clone, Copy, PartialEq, Eq, strum::IntoStaticStr)]
876 #[strum(serialize_all = "lowercase")]
877 pub enum PropKind {
878 $($sv,)*
879 $($gv,)*
880 }
881
882 impl From<&PropValue> for PropKind {
883 fn from(prop: &PropValue) -> Self {
884 match prop {
885 $(PropValue::$sv(_) => Self::$sv,)*
886 $(PropValue::$gv(_) => Self::$gv,)*
887 }
888 }
889 }
890
891 impl PropValue {
892 #[must_use]
893 pub fn is_null(&self) -> bool {
894 match self {
895 $(Self::$sv(v) => v.is_none(),)*
896 $(Self::$gv(v) => v.is_none(),)*
897 }
898 }
899
900 #[must_use]
902 pub fn null(kind: PropKind) -> Self {
903 match kind {
904 $(PropKind::$sv => Self::$sv(None),)*
905 $(PropKind::$gv => Self::$gv(None),)*
906 }
907 }
908 }
909
910 #[cfg(feature = "unstable-v2")]
911 impl MValue {
912 #[must_use]
913 pub fn kind(&self) -> PropKind {
914 match self {
915 $(Self::$sv(_) => PropKind::$sv,)*
916 $(Self::$gv(_) => PropKind::$gv,)*
917 }
918 }
919
920 #[must_use]
922 pub fn count(&self) -> Option<usize> {
923 match self {
924 $(Self::$sv(v) => v.as_ref().map(Vec::len),)*
925 $(Self::$gv(v) => v.as_ref().map(Vec::len),)*
926 }
927 }
928
929 #[must_use]
931 pub fn null(kind: PropKind) -> Self {
932 match kind {
933 $(PropKind::$sv => Self::$sv(None),)*
934 $(PropKind::$gv => Self::$gv(None),)*
935 }
936 }
937 }
938 };
939}
940
941with_kinds!(kind_mappings);
942
943pub(crate) fn stored_ring_len(ring: &LineString<i32>) -> usize {
945 let coords = &ring.0;
946 if coords.len() > 1 && coords.last() == coords.first() {
947 coords.len() - 1
948 } else {
949 coords.len()
950 }
951}
952
953#[cfg(feature = "unstable-v2")]
956pub(crate) fn stored_vertex_count(geom: &Geometry<i32>) -> usize {
957 let polygon = |poly: &geo_types::Polygon<i32>| {
958 std::iter::once(poly.exterior())
959 .chain(poly.interiors())
960 .map(stored_ring_len)
961 .sum::<usize>()
962 };
963 match geom {
964 Geometry::Point(_) => 1,
965 Geometry::Line(_) => 2,
966 Geometry::LineString(ls) => ls.0.len(),
967 Geometry::Polygon(p) => polygon(p),
968 Geometry::MultiPoint(mp) => mp.0.len(),
969 Geometry::MultiLineString(mls) => mls.iter().map(|ls| ls.0.len()).sum(),
970 Geometry::MultiPolygon(mp) => mp.iter().map(polygon).sum(),
971 Geometry::Triangle(t) => polygon(&t.to_polygon()),
972 Geometry::Rect(r) => polygon(&r.to_polygon()),
973 Geometry::GeometryCollection(gc) => gc.iter().map(stored_vertex_count).sum(),
974 }
975}
976
977#[cfg(feature = "unstable-v2")]
980fn validate_nested_kind(name: &str, kind: &NestedKind) -> MltResult<()> {
981 if matches!(kind, NestedKind::Leaf(_)) {
982 return Err(MltError::NestedRootIsLeaf(name.to_string()));
983 }
984 if kind.has_empty_map() {
985 return Err(MltError::EmptyStructNode);
986 }
987 let depth = kind.depth();
988 if depth > MAX_NESTED_DEPTH {
989 return Err(MltError::NestedTooDeep(depth));
990 }
991 Ok(())
992}
993
994fn validate_layer_name(name: &str) -> MltResult<()> {
995 if name.is_empty() {
996 Err(MltError::MissingLayerName)
997 } else {
998 Ok(())
999 }
1000}
1001
1002pub(crate) fn reject_taken_name<'a>(
1006 name: &str,
1007 role: ColumnRole,
1008 taken: impl IntoIterator<Item = (&'a str, ColumnRole)>,
1009) -> MltResult<()> {
1010 for (seen, taken_by) in taken {
1011 if seen == name {
1012 return Err(MltError::DuplicateColumnName {
1013 name: name.to_string(),
1014 role,
1015 taken_by,
1016 });
1017 }
1018 }
1019 Ok(())
1020}
1021
1022fn validate_unique_names(names: &[String], role: ColumnRole) -> MltResult<()> {
1024 for (i, name) in names.iter().enumerate() {
1027 reject_taken_name(name, role, names[..i].iter().map(|n| (n.as_str(), role)))?;
1028 }
1029 Ok(())
1030}
1031
1032trait FeatureColumn: Sized {
1034 fn of(feature: &TileFeature) -> &[Self];
1036 fn column_kind(&self) -> PropKind;
1037 fn count_mismatch(expected: usize, actual: usize) -> MltError;
1039 fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError;
1041}
1042
1043impl FeatureColumn for PropValue {
1044 fn of(feature: &TileFeature) -> &[Self] {
1045 &feature.properties
1046 }
1047
1048 fn column_kind(&self) -> PropKind {
1049 PropKind::from(self)
1050 }
1051
1052 fn count_mismatch(expected: usize, actual: usize) -> MltError {
1053 MltError::PropertyLengthMismatch { expected, actual }
1054 }
1055
1056 fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1057 MltError::PropertyKindMismatch {
1058 index,
1059 expected,
1060 actual,
1061 }
1062 }
1063}
1064
1065#[cfg(feature = "unstable-v2")]
1066impl FeatureColumn for MValue {
1067 fn of(feature: &TileFeature) -> &[Self] {
1068 &feature.m_values
1069 }
1070
1071 fn column_kind(&self) -> PropKind {
1072 self.kind()
1073 }
1074
1075 fn count_mismatch(expected: usize, actual: usize) -> MltError {
1076 MltError::MValueColumnCountMismatch { expected, actual }
1077 }
1078
1079 fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1080 MltError::MValueKindMismatch {
1081 index,
1082 expected,
1083 actual,
1084 }
1085 }
1086}
1087
1088fn infer_kinds<T: FeatureColumn>(
1092 names: &[String],
1093 features: &[TileFeature],
1094) -> MltResult<Vec<PropKind>> {
1095 let mut kinds = vec![None; names.len()];
1096 for feature in features {
1097 let values = T::of(feature);
1098 if values.len() != names.len() {
1099 return Err(T::count_mismatch(names.len(), values.len()));
1100 }
1101 for (index, value) in values.iter().enumerate() {
1102 let actual = value.column_kind();
1103 match kinds[index] {
1104 Some(expected) if expected != actual => {
1105 return Err(T::kind_mismatch(index, expected, actual));
1106 }
1107 None => kinds[index] = Some(actual),
1108 _ => {}
1109 }
1110 }
1111 }
1112 Ok(kinds
1113 .into_iter()
1114 .map(|kind| kind.unwrap_or(PropKind::Str))
1115 .collect())
1116}
1117
1118fn validate_kinds<T: FeatureColumn>(kinds: &[PropKind], feature: &TileFeature) -> MltResult<()> {
1120 let values = T::of(feature);
1121 if values.len() != kinds.len() {
1122 return Err(T::count_mismatch(kinds.len(), values.len()));
1123 }
1124 for (index, (value, expected)) in values.iter().zip(kinds).enumerate() {
1125 let actual = value.column_kind();
1126 if actual != *expected {
1127 return Err(T::kind_mismatch(index, *expected, actual));
1128 }
1129 }
1130 Ok(())
1131}
1132
1133#[cfg(test)]
1134mod tests {
1135 use geo_types::{Geometry, Point};
1136
1137 use super::*;
1138
1139 fn point_feature(properties: Vec<PropValue>) -> TileFeature {
1140 TileFeature {
1141 id: None,
1142 geometry: Geometry::Point(Point::new(0, 0)),
1143 properties,
1144 #[cfg(feature = "unstable-v2")]
1145 m_values: Vec::new(),
1146 #[cfg(feature = "unstable-v2")]
1147 nested: Vec::new(),
1148 }
1149 }
1150
1151 #[test]
1152 fn tile_layer_constructor_rejects_empty_name() {
1153 assert!(matches!(
1154 TileLayer::new("", 4096),
1155 Err(MltError::MissingLayerName)
1156 ));
1157 }
1158
1159 #[test]
1160 fn tile_layer_constructor_rejects_zero_extent() {
1161 assert!(matches!(
1162 TileLayer::new("layer", 0),
1163 Err(MltError::InvalidExtent(0))
1164 ));
1165 }
1166
1167 #[test]
1168 fn add_property_rejects_duplicate_names() {
1169 let mut layer = TileLayer::new("layer", 4096).unwrap();
1170 layer.add_property("name", PropKind::Str).unwrap();
1171 assert_eq!(
1172 layer
1173 .add_property("name", PropKind::Str)
1174 .unwrap_err()
1175 .to_string(),
1176 "duplicate column name name: the property column repeats the property column"
1177 );
1178 }
1179
1180 #[test]
1181 fn from_parts_allows_empty_property_name() {
1182 assert!(TileLayer::from_parts("layer", 4096, vec![String::new()], vec![]).is_ok());
1184 }
1185
1186 #[test]
1187 fn from_parts_rejects_duplicate_property_name() {
1188 assert_eq!(
1189 TileLayer::from_parts("layer", 4096, vec!["dup".into(), "dup".into()], vec![])
1190 .unwrap_err()
1191 .to_string(),
1192 "duplicate column name dup: the property column repeats the property column"
1193 );
1194 }
1195
1196 #[test]
1197 fn push_feature_validates_property_count() {
1198 let mut layer = TileLayer::new("layer", 4096).unwrap();
1199 layer.add_property("name", PropKind::Str).unwrap();
1200 assert!(matches!(
1201 layer.push_feature(point_feature(vec![])),
1202 Err(MltError::PropertyLengthMismatch {
1203 expected: 1,
1204 actual: 0
1205 })
1206 ));
1207 }
1208
1209 #[test]
1210 fn push_feature_validates_property_kind() {
1211 let mut layer = TileLayer::new("layer", 4096).unwrap();
1212 layer.add_property("flag", PropKind::Bool).unwrap();
1213 layer
1214 .push_feature(point_feature(vec![PropValue::Bool(Some(true))]))
1215 .unwrap();
1216 assert!(matches!(
1217 layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1218 Err(MltError::PropertyKindMismatch {
1219 index: 0,
1220 expected: PropKind::Bool,
1221 actual: PropKind::I32,
1222 })
1223 ));
1224 }
1225
1226 #[test]
1227 fn declared_property_kind_is_enforced_for_first_feature() {
1228 let mut layer = TileLayer::new("layer", 4096).unwrap();
1229 layer.add_property("flag", PropKind::Bool).unwrap();
1230 assert!(matches!(
1231 layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1232 Err(MltError::PropertyKindMismatch {
1233 index: 0,
1234 expected: PropKind::Bool,
1235 actual: PropKind::I32,
1236 })
1237 ));
1238 }
1239
1240 #[cfg(feature = "unstable-v2")]
1241 #[test]
1242 fn push_feature_validates_m_value_count() {
1243 let mut layer = TileLayer::new("layer", 4096).unwrap();
1244 layer.add_m_value("m", PropKind::I32).unwrap();
1245 assert!(matches!(
1246 layer.push_feature(point_feature(vec![])),
1247 Err(MltError::MValueColumnCountMismatch {
1248 expected: 1,
1249 actual: 0
1250 })
1251 ));
1252 }
1253
1254 #[cfg(feature = "unstable-v2")]
1255 #[test]
1256 fn push_feature_validates_one_value_per_vertex() {
1257 let mut layer = TileLayer::new("layer", 4096).unwrap();
1258 layer.add_m_value("m", PropKind::I32).unwrap();
1259 let mut feature = point_feature(vec![]);
1260 feature.m_values = vec![MValue::I32(Some(vec![1, 2]))];
1261 assert!(matches!(
1262 layer.push_feature(feature),
1263 Err(MltError::MValueVertexCountMismatch {
1264 index: 0,
1265 expected: 1,
1266 actual: 2,
1267 })
1268 ));
1269 }
1270
1271 #[cfg(feature = "unstable-v2")]
1272 #[test]
1273 fn a_polygon_does_not_count_its_closing_vertices() {
1274 let ring = |pts: &[(i32, i32)]| {
1275 let mut ls = LineString::new(
1276 pts.iter()
1277 .map(|&(x, y)| geo_types::Coord { x, y })
1278 .collect(),
1279 );
1280 ls.close();
1281 ls
1282 };
1283 let poly = geo_types::Polygon::new(
1284 ring(&[(0, 0), (0, 4), (4, 4), (4, 0)]),
1285 vec![ring(&[(1, 1), (1, 2), (2, 2)])],
1286 );
1287 let feature = TileFeature::new(Geometry::Polygon(poly));
1288 assert_eq!(feature.vertex_count(), 7);
1289 }
1290
1291 #[cfg(feature = "unstable-v2")]
1292 #[test]
1293 fn a_shape_counts_its_root_as_the_first_level() {
1294 let kind = NestedKind::list(NestedKind::map([("a", NestedKind::Leaf(PropKind::I32))]));
1295 assert_eq!(kind.depth(), 3);
1296 }
1297
1298 #[cfg(feature = "unstable-v2")]
1299 #[test]
1300 fn a_map_may_leave_out_a_field_but_not_add_one() {
1301 let kind = NestedKind::map([
1302 ("a", NestedKind::Leaf(PropKind::I32)),
1303 ("b", NestedKind::Leaf(PropKind::Str)),
1304 ]);
1305 assert!(kind.accepts(&NestedValue::map([(
1306 "a",
1307 NestedValue::Leaf(PropValue::I32(Some(1)))
1308 )])));
1309 assert!(!kind.accepts(&NestedValue::map([(
1310 "c",
1311 NestedValue::Leaf(PropValue::I32(Some(1)))
1312 )])));
1313 assert!(!kind.accepts(&NestedValue::map([(
1314 "a",
1315 NestedValue::Leaf(PropValue::Str(Some("x".into())))
1316 )])));
1317 }
1318
1319 #[cfg(feature = "unstable-v2")]
1320 fn list_kind() -> NestedKind {
1321 NestedKind::list(NestedKind::Leaf(PropKind::I32))
1322 }
1323
1324 #[cfg(feature = "unstable-v2")]
1325 #[test]
1326 fn add_m_value_rejects_a_name_a_property_has() {
1327 let mut layer = TileLayer::new("layer", 4096).unwrap();
1328 layer.add_property("n", PropKind::I32).unwrap();
1329 assert_eq!(
1330 layer
1331 .add_m_value("n", PropKind::I32)
1332 .unwrap_err()
1333 .to_string(),
1334 "duplicate column name n: the m-value column repeats the property column"
1335 );
1336 }
1337
1338 #[cfg(feature = "unstable-v2")]
1339 #[test]
1340 fn add_m_value_rejects_a_name_a_nested_column_has() {
1341 let mut layer = TileLayer::new("layer", 4096).unwrap();
1342 layer.add_nested("n", list_kind()).unwrap();
1343 assert_eq!(
1344 layer
1345 .add_m_value("n", PropKind::I32)
1346 .unwrap_err()
1347 .to_string(),
1348 "duplicate column name n: the m-value column repeats the nested column"
1349 );
1350 }
1351
1352 #[cfg(feature = "unstable-v2")]
1353 #[test]
1354 fn add_m_value_rejects_duplicate_names() {
1355 let mut layer = TileLayer::new("layer", 4096).unwrap();
1356 layer.add_m_value("n", PropKind::I32).unwrap();
1357 assert_eq!(
1358 layer
1359 .add_m_value("n", PropKind::I32)
1360 .unwrap_err()
1361 .to_string(),
1362 "duplicate column name n: the m-value column repeats the m-value column"
1363 );
1364 }
1365
1366 #[cfg(feature = "unstable-v2")]
1367 #[test]
1368 fn add_property_rejects_a_name_an_m_value_has() {
1369 let mut layer = TileLayer::new("layer", 4096).unwrap();
1370 layer.add_m_value("n", PropKind::I32).unwrap();
1371 assert_eq!(
1372 layer
1373 .add_property("n", PropKind::I32)
1374 .unwrap_err()
1375 .to_string(),
1376 "duplicate column name n: the property column repeats the m-value column"
1377 );
1378 }
1379
1380 #[cfg(feature = "unstable-v2")]
1381 #[test]
1382 fn add_property_rejects_a_name_a_nested_column_has() {
1383 let mut layer = TileLayer::new("layer", 4096).unwrap();
1384 layer.add_nested("n", list_kind()).unwrap();
1385 assert_eq!(
1386 layer
1387 .add_property("n", PropKind::I32)
1388 .unwrap_err()
1389 .to_string(),
1390 "duplicate column name n: the property column repeats the nested column"
1391 );
1392 }
1393
1394 #[cfg(feature = "unstable-v2")]
1395 #[test]
1396 fn add_nested_rejects_a_name_a_property_has() {
1397 let mut layer = TileLayer::new("layer", 4096).unwrap();
1398 layer.add_property("n", PropKind::I32).unwrap();
1399 assert_eq!(
1400 layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1401 "duplicate column name n: the nested column repeats the property column"
1402 );
1403 }
1404
1405 #[cfg(feature = "unstable-v2")]
1406 #[test]
1407 fn add_nested_rejects_a_name_an_m_value_already_has() {
1408 let mut layer = TileLayer::new("layer", 4096).unwrap();
1409 layer.add_m_value("n", PropKind::I32).unwrap();
1410 assert_eq!(
1411 layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1412 "duplicate column name n: the nested column repeats the m-value column"
1413 );
1414 }
1415
1416 #[cfg(feature = "unstable-v2")]
1417 #[test]
1418 fn add_nested_rejects_duplicate_names() {
1419 let mut layer = TileLayer::new("layer", 4096).unwrap();
1420 layer.add_nested("n", list_kind()).unwrap();
1421 assert_eq!(
1422 layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1423 "duplicate column name n: the nested column repeats the nested column"
1424 );
1425 }
1426
1427 #[cfg(feature = "unstable-v2")]
1428 #[test]
1429 fn set_nested_rejects_a_value_of_another_shape() {
1430 let mut layer = TileLayer::new("layer", 4096).unwrap();
1431 let key = layer
1432 .add_nested("n", NestedKind::list(NestedKind::Leaf(PropKind::I32)))
1433 .unwrap();
1434 let mut feature = point_feature(vec![]);
1435 feature.nested = vec![NestedValue::List(None)];
1436 assert_eq!(
1437 feature
1438 .set_nested(key, NestedValue::Map(None))
1439 .unwrap_err()
1440 .to_string(),
1441 "nested column 0 was given a value of another shape"
1442 );
1443 }
1444
1445 #[test]
1446 fn builder_uses_declared_property_kind_for_defaults() {
1447 let mut builder = TileLayer::builder("layer", 4096).unwrap();
1448 let flag = builder.add_property("flag", PropKind::Bool).unwrap();
1449 let mut feature = builder.feature(Geometry::Point(Point::new(0, 0)));
1450 feature.property(flag, PropValue::Bool(Some(true))).unwrap();
1451 feature.finish().unwrap();
1452 let layer = builder.finish();
1453
1454 assert_eq!(
1455 layer.features()[0].properties()[0],
1456 PropValue::Bool(Some(true))
1457 );
1458 }
1459}