1#[cfg(feature = "unstable-v2")]
9use std::collections::BTreeMap;
10use std::num::NonZeroU32;
11
12use geo_types::{Geometry, LineString};
13#[cfg(feature = "unstable-v2")]
14use num_traits::ToPrimitive as _;
15
16use crate::{MltError, MltResult};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub struct Extent(NonZeroU32);
24
25impl Extent {
26 pub fn new(value: u32) -> MltResult<Self> {
27 NonZeroU32::new(value)
28 .map(Self)
29 .ok_or(MltError::InvalidExtent(value))
30 }
31
32 #[must_use]
33 pub fn get(self) -> u32 {
34 self.0.get()
35 }
36}
37
38impl From<Extent> for NonZeroU32 {
39 fn from(value: Extent) -> Self {
40 value.0
41 }
42}
43
44#[cfg(feature = "unstable-v2")]
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub struct ZStep(i8);
50
51#[cfg(feature = "unstable-v2")]
52impl ZStep {
53 pub const MIN_EXPONENT: i8 = -3;
55 pub const MAX_EXPONENT: i8 = 4;
57 pub const BASE_METRES: f64 = -10_000.0;
59
60 pub fn new(exponent: i8) -> MltResult<Self> {
62 if (Self::MIN_EXPONENT..=Self::MAX_EXPONENT).contains(&exponent) {
63 Ok(Self(exponent))
64 } else {
65 Err(MltError::InvalidZStep(exponent))
66 }
67 }
68
69 #[must_use]
70 pub fn exponent(self) -> i8 {
71 self.0
72 }
73
74 pub(crate) fn code(self) -> u8 {
76 (self.0 - Self::MIN_EXPONENT).cast_unsigned()
77 }
78
79 pub(crate) fn from_code(code: u8) -> MltResult<Self> {
81 i8::try_from(code)
82 .ok()
83 .and_then(|c| Self::new(c + Self::MIN_EXPONENT).ok())
84 .ok_or(MltError::InvalidZStepCode(code))
85 }
86
87 #[must_use]
89 pub fn metres(self) -> f64 {
90 if self.0 < 0 {
91 1.0 / self.power()
92 } else {
93 self.power()
94 }
95 }
96
97 #[must_use]
101 pub fn elevation(self, z: i32) -> f64 {
102 let power = self.power();
103 if self.0 < 0 {
104 (f64::from(z) + Self::BASE_METRES * power) / power
105 } else {
106 Self::BASE_METRES + f64::from(z) * power
107 }
108 }
109
110 #[must_use]
112 pub fn z(self, elevation: f64) -> Option<i32> {
113 let power = self.power();
114 let z = if self.0 < 0 {
115 elevation * power - Self::BASE_METRES * power
116 } else {
117 (elevation - Self::BASE_METRES) / power
118 };
119 z.round().to_i32()
120 }
121
122 fn power(self) -> f64 {
124 10f64.powi(self.0.unsigned_abs().into())
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display)]
130pub enum ColumnRole {
131 #[strum(serialize = "property")]
133 Property,
134 #[cfg(feature = "unstable-v2")]
136 #[strum(serialize = "m-value")]
137 MValue,
138 #[cfg(feature = "unstable-v2")]
140 #[strum(serialize = "nested")]
141 Nested,
142}
143
144#[derive(Debug, Clone, PartialEq)]
150pub struct TileLayer {
151 pub(crate) name: String,
152 pub(crate) extent: Extent,
153 pub(crate) property_names: Vec<String>,
155 pub(crate) property_kinds: Vec<PropKind>,
157 #[cfg(feature = "unstable-v2")]
159 pub(crate) m_value_names: Vec<String>,
160 #[cfg(feature = "unstable-v2")]
162 pub(crate) m_value_kinds: Vec<PropKind>,
163 #[cfg(feature = "unstable-v2")]
165 pub(crate) nested_names: Vec<String>,
166 #[cfg(feature = "unstable-v2")]
168 pub(crate) nested_kinds: Vec<NestedKind>,
169 #[cfg(feature = "unstable-v2")]
171 pub(crate) z_step: Option<ZStep>,
172 pub(crate) features: Vec<TileFeature>,
173}
174
175#[derive(Debug, Clone, PartialEq)]
177pub struct TileFeature {
178 pub(crate) id: Option<u64>,
179 pub(crate) geometry: Geometry<i32>,
181 pub(crate) properties: Vec<PropValue>,
184 #[cfg(feature = "unstable-v2")]
188 pub(crate) m_values: Vec<MValue>,
189 #[cfg(feature = "unstable-v2")]
191 pub(crate) nested: Vec<NestedValue>,
192 #[cfg(feature = "unstable-v2")]
194 pub(crate) z: Vec<i32>,
195}
196
197#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
198pub struct PropertyKey(usize);
199
200impl PropertyKey {
201 #[must_use]
202 pub fn index(self) -> usize {
203 self.0
204 }
205}
206
207#[cfg(feature = "unstable-v2")]
209#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
210pub struct MValueKey(usize);
211
212#[cfg(feature = "unstable-v2")]
213impl MValueKey {
214 #[must_use]
215 pub fn index(self) -> usize {
216 self.0
217 }
218}
219
220#[cfg(feature = "unstable-v2")]
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
223pub struct NestedKey(usize);
224
225#[cfg(feature = "unstable-v2")]
226impl NestedKey {
227 #[must_use]
228 pub fn index(self) -> usize {
229 self.0
230 }
231}
232
233#[cfg(feature = "unstable-v2")]
235pub(crate) const MAX_NESTED_DEPTH: usize = 8;
236
237#[cfg(feature = "unstable-v2")]
239#[derive(Debug, Clone, PartialEq, Eq)]
240pub enum NestedKind {
241 Leaf(PropKind),
242 List(Box<Self>),
243 Map(BTreeMap<String, Self>),
245}
246
247#[cfg(feature = "unstable-v2")]
248impl NestedKind {
249 #[must_use]
251 pub fn map<K: Into<String>>(fields: impl IntoIterator<Item = (K, Self)>) -> Self {
252 Self::Map(fields.into_iter().map(|(k, v)| (k.into(), v)).collect())
253 }
254
255 #[must_use]
257 pub fn list(element: Self) -> Self {
258 Self::List(Box::new(element))
259 }
260
261 #[must_use]
263 pub fn depth(&self) -> usize {
264 1 + match self {
265 Self::Leaf(_) => 0,
266 Self::List(element) => element.depth(),
267 Self::Map(fields) => fields.values().map(Self::depth).max().unwrap_or(0),
268 }
269 }
270
271 #[must_use]
273 pub fn null_value(&self) -> NestedValue {
274 match self {
275 Self::Leaf(kind) => NestedValue::Leaf(PropValue::null(*kind)),
276 Self::List(_) => NestedValue::List(None),
277 Self::Map(_) => NestedValue::Map(None),
278 }
279 }
280
281 #[must_use]
285 pub fn accepts(&self, value: &NestedValue) -> bool {
286 match (self, value) {
287 (Self::Leaf(kind), NestedValue::Leaf(value)) => value.kind() == *kind,
288 (Self::List(_), NestedValue::List(None)) | (Self::Map(_), NestedValue::Map(None)) => {
289 true
290 }
291 (Self::List(element), NestedValue::List(Some(items))) => {
292 items.iter().all(|item| element.accepts(item))
293 }
294 (Self::Map(fields), NestedValue::Map(Some(entries))) => entries
295 .iter()
296 .all(|(key, value)| fields.get(key).is_some_and(|kind| kind.accepts(value))),
297 _ => false,
298 }
299 }
300
301 fn has_empty_map(&self) -> bool {
303 match self {
304 Self::Leaf(_) => false,
305 Self::List(element) => element.has_empty_map(),
306 Self::Map(fields) => fields.is_empty() || fields.values().any(Self::has_empty_map),
307 }
308 }
309}
310
311#[cfg(feature = "unstable-v2")]
315#[derive(Debug, Clone, PartialEq)]
316pub enum NestedValue {
317 Leaf(PropValue),
318 List(Option<Vec<Self>>),
319 Map(Option<BTreeMap<String, Self>>),
320}
321
322#[cfg(feature = "unstable-v2")]
323impl NestedValue {
324 #[must_use]
326 pub fn map<K: Into<String>>(entries: impl IntoIterator<Item = (K, Self)>) -> Self {
327 Self::Map(Some(
328 entries.into_iter().map(|(k, v)| (k.into(), v)).collect(),
329 ))
330 }
331
332 #[must_use]
334 pub fn list(items: impl IntoIterator<Item = Self>) -> Self {
335 Self::List(Some(items.into_iter().collect()))
336 }
337
338 #[must_use]
340 pub fn is_null(&self) -> bool {
341 match self {
342 Self::Leaf(value) => value.is_null(),
343 Self::List(items) => items.is_none(),
344 Self::Map(entries) => entries.is_none(),
345 }
346 }
347
348 #[must_use]
350 pub(crate) fn heap_bytes(&self) -> usize {
351 match self {
352 Self::Leaf(PropValue::Str(Some(value))) => value.len(),
353 Self::Leaf(_) => size_of::<PropValue>(),
354 Self::List(items) => items.iter().flatten().map(Self::heap_bytes).sum::<usize>(),
355 Self::Map(entries) => entries
356 .iter()
357 .flatten()
358 .map(|(key, value)| key.len() + value.heap_bytes())
359 .sum(),
360 }
361 }
362
363 fn same_shape(&self, other: &Self) -> bool {
366 matches!(
367 (self, other),
368 (Self::Leaf(_), Self::Leaf(_))
369 | (Self::List(_), Self::List(_))
370 | (Self::Map(_), Self::Map(_))
371 )
372 }
373}
374
375impl TileLayer {
376 pub fn new(name: impl Into<String>, extent: u32) -> MltResult<Self> {
377 Self::with_capacity(name, extent, 0)
378 }
379
380 pub fn with_capacity(name: impl Into<String>, extent: u32, features: usize) -> MltResult<Self> {
381 let name = name.into();
382 validate_layer_name(&name)?;
383 let extent = Extent::new(extent)?;
384 Ok(Self {
385 name,
386 extent,
387 property_names: Vec::new(),
388 property_kinds: Vec::new(),
389 #[cfg(feature = "unstable-v2")]
390 m_value_names: Vec::new(),
391 #[cfg(feature = "unstable-v2")]
392 m_value_kinds: Vec::new(),
393 #[cfg(feature = "unstable-v2")]
394 nested_names: Vec::new(),
395 #[cfg(feature = "unstable-v2")]
396 nested_kinds: Vec::new(),
397 #[cfg(feature = "unstable-v2")]
398 z_step: None,
399 features: Vec::with_capacity(features),
400 })
401 }
402
403 pub(crate) fn from_parts(
404 name: impl Into<String>,
405 extent: u32,
406 property_names: Vec<String>,
407 features: Vec<TileFeature>,
408 ) -> MltResult<Self> {
409 let name = name.into();
410 validate_layer_name(&name)?;
411 let extent = Extent::new(extent)?;
412 validate_unique_names(&property_names, ColumnRole::Property)?;
413 let property_kinds = infer_kinds::<PropValue>(&property_names, &features)?;
414 let layer = Self {
415 name,
416 extent,
417 property_names,
418 property_kinds,
419 #[cfg(feature = "unstable-v2")]
420 m_value_names: Vec::new(),
421 #[cfg(feature = "unstable-v2")]
422 m_value_kinds: Vec::new(),
423 #[cfg(feature = "unstable-v2")]
424 nested_names: Vec::new(),
425 #[cfg(feature = "unstable-v2")]
426 nested_kinds: Vec::new(),
427 #[cfg(feature = "unstable-v2")]
428 z_step: None,
429 features,
430 };
431 Ok(layer)
432 }
433
434 #[must_use]
435 pub fn name(&self) -> &str {
436 &self.name
437 }
438
439 #[must_use]
440 pub fn extent(&self) -> Extent {
441 self.extent
442 }
443
444 #[must_use]
445 pub fn property_names(&self) -> &[String] {
446 &self.property_names
447 }
448
449 #[must_use]
450 pub fn features(&self) -> &[TileFeature] {
451 &self.features
452 }
453
454 #[must_use]
455 pub(crate) fn features_mut(&mut self) -> &mut [TileFeature] {
456 &mut self.features
457 }
458
459 pub(crate) fn widen_8bit_ints(&mut self) {
461 for kind in &mut self.property_kinds {
462 if *kind == PropKind::I8 {
463 *kind = PropKind::I32;
464 } else if *kind == PropKind::U8 {
465 *kind = PropKind::U32;
466 }
467 }
468 for prop in self.features.iter_mut().flat_map(|f| &mut f.properties) {
469 if let PropValue::I8(v) = *prop {
470 *prop = PropValue::I32(v.map(i32::from));
471 } else if let PropValue::U8(v) = *prop {
472 *prop = PropValue::U32(v.map(u32::from));
473 }
474 }
475 }
476
477 #[must_use]
478 pub fn feature_count(&self) -> usize {
479 self.features.len()
480 }
481
482 fn reject_name_in_use(&self, name: &str, role: ColumnRole) -> MltResult<()> {
484 let taken = self
485 .property_names
486 .iter()
487 .map(|n| (n.as_str(), ColumnRole::Property));
488 #[cfg(feature = "unstable-v2")]
489 let taken = taken
490 .chain(
491 self.m_value_names
492 .iter()
493 .map(|n| (n.as_str(), ColumnRole::MValue)),
494 )
495 .chain(
496 self.nested_names
497 .iter()
498 .map(|n| (n.as_str(), ColumnRole::Nested)),
499 );
500 reject_taken_name(name, role, taken)
501 }
502
503 pub fn add_property(
504 &mut self,
505 name: impl Into<String>,
506 kind: PropKind,
507 ) -> MltResult<PropertyKey> {
508 let name = name.into();
509 self.reject_name_in_use(&name, ColumnRole::Property)?;
510 for feature in &mut self.features {
511 feature.properties.push(PropValue::null(kind));
512 }
513 self.property_names.push(name);
514 self.property_kinds.push(kind);
515 Ok(PropertyKey(self.property_names.len() - 1))
516 }
517
518 #[cfg(feature = "unstable-v2")]
519 #[must_use]
520 pub fn m_value_names(&self) -> &[String] {
521 &self.m_value_names
522 }
523
524 #[cfg(feature = "unstable-v2")]
525 #[must_use]
526 pub fn m_value_kinds(&self) -> &[PropKind] {
527 &self.m_value_kinds
528 }
529
530 #[cfg(feature = "unstable-v2")]
532 pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
533 let name = name.into();
534 self.reject_name_in_use(&name, ColumnRole::MValue)?;
535 for feature in &mut self.features {
536 feature.m_values.push(MValue::null(kind));
537 }
538 self.m_value_names.push(name);
539 self.m_value_kinds.push(kind);
540 Ok(MValueKey(self.m_value_names.len() - 1))
541 }
542
543 #[cfg(feature = "unstable-v2")]
546 pub(crate) fn with_m_value_names(mut self, names: Vec<String>) -> MltResult<Self> {
547 validate_unique_names(&names, ColumnRole::MValue)?;
548 for name in &names {
549 self.reject_name_in_use(name, ColumnRole::MValue)?;
550 }
551 self.m_value_kinds = infer_kinds::<MValue>(&names, &self.features)?;
552 self.m_value_names = names;
553 for feature in &self.features {
554 self.validate_m_values(feature)?;
555 }
556 Ok(self)
557 }
558
559 #[cfg(feature = "unstable-v2")]
560 #[must_use]
561 pub fn nested_names(&self) -> &[String] {
562 &self.nested_names
563 }
564
565 #[cfg(feature = "unstable-v2")]
566 #[must_use]
567 pub fn nested_kinds(&self) -> &[NestedKind] {
568 &self.nested_kinds
569 }
570
571 #[cfg(feature = "unstable-v2")]
573 #[must_use]
574 pub fn z_step(&self) -> Option<ZStep> {
575 self.z_step
576 }
577
578 #[cfg(feature = "unstable-v2")]
582 pub fn set_z_step(&mut self, step: ZStep) -> MltResult<()> {
583 if !self.features.is_empty() {
584 return Err(MltError::ZStepOnNonEmptyLayer(self.name.clone()));
585 }
586 self.z_step = Some(step);
587 Ok(())
588 }
589
590 #[cfg(feature = "unstable-v2")]
592 pub(crate) fn with_z_step(mut self, step: Option<ZStep>) -> MltResult<Self> {
593 self.z_step = step;
594 for feature in &self.features {
595 self.validate_z(feature)?;
596 }
597 Ok(self)
598 }
599
600 #[cfg(feature = "unstable-v2")]
604 pub fn add_nested(
605 &mut self,
606 name: impl Into<String>,
607 kind: NestedKind,
608 ) -> MltResult<NestedKey> {
609 let name = name.into();
610 self.reject_name_in_use(&name, ColumnRole::Nested)?;
611 validate_nested_kind(&name, &kind)?;
612 for feature in &mut self.features {
613 feature.nested.push(kind.null_value());
614 }
615 self.nested_names.push(name);
616 self.nested_kinds.push(kind);
617 Ok(NestedKey(self.nested_names.len() - 1))
618 }
619
620 #[cfg(feature = "unstable-v2")]
623 pub(crate) fn with_nested(
624 mut self,
625 names: Vec<String>,
626 kinds: Vec<NestedKind>,
627 ) -> MltResult<Self> {
628 validate_unique_names(&names, ColumnRole::Nested)?;
629 for name in &names {
630 self.reject_name_in_use(name, ColumnRole::Nested)?;
631 }
632 for (name, kind) in names.iter().zip(&kinds) {
633 validate_nested_kind(name, kind)?;
634 }
635 self.nested_names = names;
636 self.nested_kinds = kinds;
637 for feature in &self.features {
638 self.validate_nested(feature)?;
639 }
640 Ok(self)
641 }
642
643 pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
644 self.validate_feature(&feature)?;
645 self.features.push(feature);
646 Ok(())
647 }
648
649 pub fn builder(name: impl Into<String>, extent: u32) -> MltResult<TileLayerBuilder> {
650 Ok(TileLayerBuilder {
651 layer: Self::new(name, extent)?,
652 })
653 }
654
655 fn validate_feature(&self, feature: &TileFeature) -> MltResult<()> {
656 validate_kinds::<PropValue>(&self.property_kinds, feature)?;
657 #[cfg(feature = "unstable-v2")]
658 self.validate_m_values(feature)?;
659 #[cfg(feature = "unstable-v2")]
660 self.validate_nested(feature)?;
661 #[cfg(feature = "unstable-v2")]
662 self.validate_z(feature)?;
663 Ok(())
664 }
665
666 #[cfg(feature = "unstable-v2")]
668 fn validate_z(&self, feature: &TileFeature) -> MltResult<()> {
669 let expected = if self.z_step.is_some() {
670 feature.vertex_count()
671 } else {
672 0
673 };
674 if feature.z.len() == expected {
675 Ok(())
676 } else {
677 Err(MltError::ZVertexCountMismatch {
678 expected,
679 actual: feature.z.len(),
680 })
681 }
682 }
683
684 #[cfg(feature = "unstable-v2")]
687 fn validate_nested(&self, feature: &TileFeature) -> MltResult<()> {
688 if feature.nested.len() != self.nested_kinds.len() {
689 return Err(MltError::NestedColumnCountMismatch {
690 expected: self.nested_kinds.len(),
691 actual: feature.nested.len(),
692 });
693 }
694 for (index, (value, kind)) in feature.nested.iter().zip(&self.nested_kinds).enumerate() {
695 if !kind.accepts(value) {
696 return Err(MltError::NestedValueMismatch {
697 index,
698 name: self.nested_names[index].clone(),
699 });
700 }
701 }
702 Ok(())
703 }
704
705 #[cfg(feature = "unstable-v2")]
708 fn validate_m_values(&self, feature: &TileFeature) -> MltResult<()> {
709 validate_kinds::<MValue>(&self.m_value_kinds, feature)?;
710 let vertices = feature.vertex_count();
711 for (index, m_value) in feature.m_values.iter().enumerate() {
712 if let Some(len) = m_value.count()
713 && len != vertices
714 {
715 return Err(MltError::MValueVertexCountMismatch {
716 index,
717 expected: vertices,
718 actual: len,
719 });
720 }
721 }
722 Ok(())
723 }
724}
725
726impl TileFeature {
727 #[must_use]
728 pub fn new(geometry: Geometry<i32>) -> Self {
729 Self {
730 id: None,
731 geometry,
732 properties: Vec::new(),
733 #[cfg(feature = "unstable-v2")]
734 m_values: Vec::new(),
735 #[cfg(feature = "unstable-v2")]
736 nested: Vec::new(),
737 #[cfg(feature = "unstable-v2")]
738 z: Vec::new(),
739 }
740 }
741
742 #[must_use]
743 pub fn with_id(geometry: Geometry<i32>, id: u64) -> Self {
744 Self {
745 id: Some(id),
746 geometry,
747 properties: Vec::new(),
748 #[cfg(feature = "unstable-v2")]
749 m_values: Vec::new(),
750 #[cfg(feature = "unstable-v2")]
751 nested: Vec::new(),
752 #[cfg(feature = "unstable-v2")]
753 z: Vec::new(),
754 }
755 }
756
757 #[must_use]
758 pub fn id(&self) -> Option<u64> {
759 self.id
760 }
761
762 #[must_use]
763 pub fn geometry(&self) -> &Geometry<i32> {
764 &self.geometry
765 }
766
767 #[must_use]
768 pub fn properties(&self) -> &[PropValue] {
769 &self.properties
770 }
771
772 #[must_use]
773 pub(crate) fn properties_mut(&mut self) -> &mut [PropValue] {
774 &mut self.properties
775 }
776
777 #[cfg(feature = "unstable-v2")]
782 #[must_use]
783 pub fn vertex_count(&self) -> usize {
784 stored_vertex_count(&self.geometry)
785 }
786
787 #[cfg(feature = "unstable-v2")]
788 #[must_use]
789 pub fn m_values(&self) -> &[MValue] {
790 &self.m_values
791 }
792
793 #[cfg(feature = "unstable-v2")]
795 #[must_use]
796 pub fn z(&self) -> &[i32] {
797 &self.z
798 }
799
800 #[cfg(feature = "unstable-v2")]
802 pub fn set_z(&mut self, z: Vec<i32>) -> MltResult<()> {
803 let expected = self.vertex_count();
804 if z.len() != expected {
805 return Err(MltError::ZVertexCountMismatch {
806 expected,
807 actual: z.len(),
808 });
809 }
810 self.z = z;
811 Ok(())
812 }
813
814 #[cfg(feature = "unstable-v2")]
817 pub fn set_m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<()> {
818 let vertices = self.vertex_count();
819 let Some(slot) = self.m_values.get_mut(key.index()) else {
820 return Err(MltError::UnknownMValueKey {
821 index: key.index(),
822 columns: self.m_values.len(),
823 });
824 };
825 let expected = slot.kind();
826 let actual = value.kind();
827 if actual != expected {
828 return Err(MltError::MValueKindMismatch {
829 index: key.index(),
830 expected,
831 actual,
832 });
833 }
834 if let Some(len) = value.count()
835 && len != vertices
836 {
837 return Err(MltError::MValueVertexCountMismatch {
838 index: key.index(),
839 expected: vertices,
840 actual: len,
841 });
842 }
843 *slot = value;
844 Ok(())
845 }
846
847 #[cfg(feature = "unstable-v2")]
848 #[must_use]
849 pub fn nested(&self) -> &[NestedValue] {
850 &self.nested
851 }
852
853 #[cfg(feature = "unstable-v2")]
859 pub fn set_nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<()> {
860 let columns = self.nested.len();
861 let Some(slot) = self.nested.get_mut(key.index()) else {
862 return Err(MltError::UnknownNestedKey {
863 index: key.index(),
864 columns,
865 });
866 };
867 if !slot.same_shape(&value) {
868 return Err(MltError::NestedShapeMismatch { index: key.index() });
869 }
870 *slot = value;
871 Ok(())
872 }
873
874 pub fn set_property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<()> {
875 let Some(prop) = self.properties.get_mut(key.index()) else {
876 return Err(MltError::PropertyLengthMismatch {
877 expected: key.index() + 1,
878 actual: self.properties.len(),
879 });
880 };
881 let expected = PropKind::from(&*prop);
882 let actual = PropKind::from(&value);
883 if actual != expected {
884 return Err(MltError::PropertyKindMismatch {
885 index: key.index(),
886 expected,
887 actual,
888 });
889 }
890 *prop = value;
891 Ok(())
892 }
893}
894
895pub struct TileLayerBuilder {
896 layer: TileLayer,
897}
898
899impl TileLayerBuilder {
900 pub fn add_property(
901 &mut self,
902 name: impl Into<String>,
903 kind: PropKind,
904 ) -> MltResult<PropertyKey> {
905 self.layer.add_property(name, kind)
906 }
907
908 #[cfg(feature = "unstable-v2")]
909 pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
910 self.layer.add_m_value(name, kind)
911 }
912
913 #[cfg(feature = "unstable-v2")]
914 pub fn add_nested(
915 &mut self,
916 name: impl Into<String>,
917 kind: NestedKind,
918 ) -> MltResult<NestedKey> {
919 self.layer.add_nested(name, kind)
920 }
921
922 #[cfg(feature = "unstable-v2")]
923 pub fn set_z_step(&mut self, step: ZStep) -> MltResult<()> {
924 self.layer.set_z_step(step)
925 }
926
927 pub fn feature(&mut self, geometry: Geometry<i32>) -> TileFeatureBuilder<'_> {
928 let properties = self
929 .layer
930 .property_kinds
931 .iter()
932 .copied()
933 .map(PropValue::null)
934 .collect();
935 #[cfg(feature = "unstable-v2")]
936 let m_values = self
937 .layer
938 .m_value_kinds
939 .iter()
940 .copied()
941 .map(MValue::null)
942 .collect();
943 #[cfg(feature = "unstable-v2")]
944 let nested = self
945 .layer
946 .nested_kinds
947 .iter()
948 .map(NestedKind::null_value)
949 .collect();
950 TileFeatureBuilder {
951 layer: self,
952 feature: TileFeature {
953 id: None,
954 geometry,
955 properties,
956 #[cfg(feature = "unstable-v2")]
957 m_values,
958 #[cfg(feature = "unstable-v2")]
959 nested,
960 #[cfg(feature = "unstable-v2")]
961 z: Vec::new(),
962 },
963 }
964 }
965
966 pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
967 self.layer.push_feature(feature)
968 }
969
970 #[must_use]
971 pub fn finish(self) -> TileLayer {
972 self.layer
973 }
974}
975
976pub struct TileFeatureBuilder<'a> {
977 layer: &'a mut TileLayerBuilder,
978 feature: TileFeature,
979}
980
981impl TileFeatureBuilder<'_> {
982 pub fn id(&mut self, id: Option<u64>) -> &mut Self {
983 self.feature.id = id;
984 self
985 }
986
987 pub fn property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<&mut Self> {
988 self.feature.set_property(key, value)?;
989 Ok(self)
990 }
991
992 #[cfg(feature = "unstable-v2")]
993 pub fn m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<&mut Self> {
994 self.feature.set_m_value(key, value)?;
995 Ok(self)
996 }
997
998 #[cfg(feature = "unstable-v2")]
999 pub fn nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<&mut Self> {
1000 self.feature.set_nested(key, value)?;
1001 Ok(self)
1002 }
1003
1004 #[cfg(feature = "unstable-v2")]
1005 pub fn z(&mut self, z: Vec<i32>) -> MltResult<&mut Self> {
1006 self.feature.set_z(z)?;
1007 Ok(self)
1008 }
1009
1010 pub fn finish(self) -> MltResult<()> {
1011 self.layer.push_feature(self.feature)
1012 }
1013}
1014
1015#[derive(Debug, Clone, PartialEq)]
1023pub enum PropValue {
1024 Bool(Option<bool>),
1025 I8(Option<i8>),
1026 U8(Option<u8>),
1027 I32(Option<i32>),
1028 U32(Option<u32>),
1029 I64(Option<i64>),
1030 U64(Option<u64>),
1031 F32(Option<f32>),
1032 F64(Option<f64>),
1033 Str(Option<String>),
1034}
1035
1036impl PropValue {
1037 #[must_use]
1038 pub fn kind(&self) -> PropKind {
1039 self.into()
1040 }
1041}
1042
1043#[cfg(feature = "unstable-v2")]
1048#[derive(Debug, Clone, PartialEq)]
1049pub enum MValue {
1050 Bool(Option<Vec<bool>>),
1051 I8(Option<Vec<i8>>),
1052 U8(Option<Vec<u8>>),
1053 I32(Option<Vec<i32>>),
1054 U32(Option<Vec<u32>>),
1055 I64(Option<Vec<i64>>),
1056 U64(Option<Vec<u64>>),
1057 F32(Option<Vec<f32>>),
1058 F64(Option<Vec<f64>>),
1059 Str(Option<Vec<String>>),
1060}
1061
1062#[cfg(feature = "unstable-v2")]
1063impl MValue {
1064 #[must_use]
1065 pub fn is_null(&self) -> bool {
1066 self.count().is_none()
1067 }
1068}
1069
1070macro_rules! kind_mappings {
1071 (
1072 scalar { $($sv:ident),* $(,)? }
1073 string { $($gv:ident),* $(,)? }
1074 ) => {
1075 #[derive(Debug, Clone, Copy, PartialEq, Eq, strum::IntoStaticStr)]
1077 #[strum(serialize_all = "lowercase")]
1078 pub enum PropKind {
1079 $($sv,)*
1080 $($gv,)*
1081 }
1082
1083 impl From<&PropValue> for PropKind {
1084 fn from(prop: &PropValue) -> Self {
1085 match prop {
1086 $(PropValue::$sv(_) => Self::$sv,)*
1087 $(PropValue::$gv(_) => Self::$gv,)*
1088 }
1089 }
1090 }
1091
1092 impl PropValue {
1093 #[must_use]
1094 pub fn is_null(&self) -> bool {
1095 match self {
1096 $(Self::$sv(v) => v.is_none(),)*
1097 $(Self::$gv(v) => v.is_none(),)*
1098 }
1099 }
1100
1101 #[must_use]
1103 pub fn null(kind: PropKind) -> Self {
1104 match kind {
1105 $(PropKind::$sv => Self::$sv(None),)*
1106 $(PropKind::$gv => Self::$gv(None),)*
1107 }
1108 }
1109 }
1110
1111 #[cfg(feature = "unstable-v2")]
1112 impl MValue {
1113 #[must_use]
1114 pub fn kind(&self) -> PropKind {
1115 match self {
1116 $(Self::$sv(_) => PropKind::$sv,)*
1117 $(Self::$gv(_) => PropKind::$gv,)*
1118 }
1119 }
1120
1121 #[must_use]
1123 pub fn count(&self) -> Option<usize> {
1124 match self {
1125 $(Self::$sv(v) => v.as_ref().map(Vec::len),)*
1126 $(Self::$gv(v) => v.as_ref().map(Vec::len),)*
1127 }
1128 }
1129
1130 #[must_use]
1132 pub fn null(kind: PropKind) -> Self {
1133 match kind {
1134 $(PropKind::$sv => Self::$sv(None),)*
1135 $(PropKind::$gv => Self::$gv(None),)*
1136 }
1137 }
1138 }
1139 };
1140}
1141
1142with_kinds!(kind_mappings);
1143
1144pub(crate) fn stored_ring_len(ring: &LineString<i32>) -> usize {
1146 let coords = &ring.0;
1147 if coords.len() > 1 && coords.last() == coords.first() {
1148 coords.len() - 1
1149 } else {
1150 coords.len()
1151 }
1152}
1153
1154#[cfg(feature = "unstable-v2")]
1157pub(crate) fn stored_vertex_count(geom: &Geometry<i32>) -> usize {
1158 let polygon = |poly: &geo_types::Polygon<i32>| {
1159 std::iter::once(poly.exterior())
1160 .chain(poly.interiors())
1161 .map(stored_ring_len)
1162 .sum::<usize>()
1163 };
1164 match geom {
1165 Geometry::Point(_) => 1,
1166 Geometry::Line(_) => 2,
1167 Geometry::LineString(ls) => ls.0.len(),
1168 Geometry::Polygon(p) => polygon(p),
1169 Geometry::MultiPoint(mp) => mp.0.len(),
1170 Geometry::MultiLineString(mls) => mls.iter().map(|ls| ls.0.len()).sum(),
1171 Geometry::MultiPolygon(mp) => mp.iter().map(polygon).sum(),
1172 Geometry::Triangle(t) => polygon(&t.to_polygon()),
1173 Geometry::Rect(r) => polygon(&r.to_polygon()),
1174 Geometry::GeometryCollection(gc) => gc.iter().map(stored_vertex_count).sum(),
1175 }
1176}
1177
1178#[cfg(feature = "unstable-v2")]
1181fn validate_nested_kind(name: &str, kind: &NestedKind) -> MltResult<()> {
1182 if matches!(kind, NestedKind::Leaf(_)) {
1183 return Err(MltError::NestedRootIsLeaf(name.to_string()));
1184 }
1185 if kind.has_empty_map() {
1186 return Err(MltError::EmptyStructNode);
1187 }
1188 let depth = kind.depth();
1189 if depth > MAX_NESTED_DEPTH {
1190 return Err(MltError::NestedTooDeep(depth));
1191 }
1192 Ok(())
1193}
1194
1195fn validate_layer_name(name: &str) -> MltResult<()> {
1196 if name.is_empty() {
1197 Err(MltError::MissingLayerName)
1198 } else {
1199 Ok(())
1200 }
1201}
1202
1203pub(crate) fn reject_taken_name<'a>(
1207 name: &str,
1208 role: ColumnRole,
1209 taken: impl IntoIterator<Item = (&'a str, ColumnRole)>,
1210) -> MltResult<()> {
1211 for (seen, taken_by) in taken {
1212 if seen == name {
1213 return Err(MltError::DuplicateColumnName {
1214 name: name.to_string(),
1215 role,
1216 taken_by,
1217 });
1218 }
1219 }
1220 Ok(())
1221}
1222
1223fn validate_unique_names(names: &[String], role: ColumnRole) -> MltResult<()> {
1225 for (i, name) in names.iter().enumerate() {
1228 reject_taken_name(name, role, names[..i].iter().map(|n| (n.as_str(), role)))?;
1229 }
1230 Ok(())
1231}
1232
1233trait FeatureColumn: Sized {
1235 fn of(feature: &TileFeature) -> &[Self];
1237 fn column_kind(&self) -> PropKind;
1238 fn count_mismatch(expected: usize, actual: usize) -> MltError;
1240 fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError;
1242}
1243
1244impl FeatureColumn for PropValue {
1245 fn of(feature: &TileFeature) -> &[Self] {
1246 &feature.properties
1247 }
1248
1249 fn column_kind(&self) -> PropKind {
1250 PropKind::from(self)
1251 }
1252
1253 fn count_mismatch(expected: usize, actual: usize) -> MltError {
1254 MltError::PropertyLengthMismatch { expected, actual }
1255 }
1256
1257 fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1258 MltError::PropertyKindMismatch {
1259 index,
1260 expected,
1261 actual,
1262 }
1263 }
1264}
1265
1266#[cfg(feature = "unstable-v2")]
1267impl FeatureColumn for MValue {
1268 fn of(feature: &TileFeature) -> &[Self] {
1269 &feature.m_values
1270 }
1271
1272 fn column_kind(&self) -> PropKind {
1273 self.kind()
1274 }
1275
1276 fn count_mismatch(expected: usize, actual: usize) -> MltError {
1277 MltError::MValueColumnCountMismatch { expected, actual }
1278 }
1279
1280 fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1281 MltError::MValueKindMismatch {
1282 index,
1283 expected,
1284 actual,
1285 }
1286 }
1287}
1288
1289fn infer_kinds<T: FeatureColumn>(
1293 names: &[String],
1294 features: &[TileFeature],
1295) -> MltResult<Vec<PropKind>> {
1296 let mut kinds = vec![None; names.len()];
1297 for feature in features {
1298 let values = T::of(feature);
1299 if values.len() != names.len() {
1300 return Err(T::count_mismatch(names.len(), values.len()));
1301 }
1302 for (index, value) in values.iter().enumerate() {
1303 let actual = value.column_kind();
1304 match kinds[index] {
1305 Some(expected) if expected != actual => {
1306 return Err(T::kind_mismatch(index, expected, actual));
1307 }
1308 None => kinds[index] = Some(actual),
1309 _ => {}
1310 }
1311 }
1312 }
1313 Ok(kinds
1314 .into_iter()
1315 .map(|kind| kind.unwrap_or(PropKind::Str))
1316 .collect())
1317}
1318
1319fn validate_kinds<T: FeatureColumn>(kinds: &[PropKind], feature: &TileFeature) -> MltResult<()> {
1321 let values = T::of(feature);
1322 if values.len() != kinds.len() {
1323 return Err(T::count_mismatch(kinds.len(), values.len()));
1324 }
1325 for (index, (value, expected)) in values.iter().zip(kinds).enumerate() {
1326 let actual = value.column_kind();
1327 if actual != *expected {
1328 return Err(T::kind_mismatch(index, *expected, actual));
1329 }
1330 }
1331 Ok(())
1332}
1333
1334#[cfg(test)]
1335mod tests {
1336 use geo_types::{Geometry, Point};
1337
1338 use super::*;
1339
1340 fn point_feature(properties: Vec<PropValue>) -> TileFeature {
1341 TileFeature {
1342 id: None,
1343 geometry: Geometry::Point(Point::new(0, 0)),
1344 properties,
1345 #[cfg(feature = "unstable-v2")]
1346 m_values: Vec::new(),
1347 #[cfg(feature = "unstable-v2")]
1348 nested: Vec::new(),
1349 #[cfg(feature = "unstable-v2")]
1350 z: Vec::new(),
1351 }
1352 }
1353
1354 #[test]
1355 fn tile_layer_constructor_rejects_empty_name() {
1356 assert!(matches!(
1357 TileLayer::new("", 4096),
1358 Err(MltError::MissingLayerName)
1359 ));
1360 }
1361
1362 #[test]
1363 fn tile_layer_constructor_rejects_zero_extent() {
1364 assert!(matches!(
1365 TileLayer::new("layer", 0),
1366 Err(MltError::InvalidExtent(0))
1367 ));
1368 }
1369
1370 #[test]
1371 fn add_property_rejects_duplicate_names() {
1372 let mut layer = TileLayer::new("layer", 4096).unwrap();
1373 layer.add_property("name", PropKind::Str).unwrap();
1374 assert_eq!(
1375 layer
1376 .add_property("name", PropKind::Str)
1377 .unwrap_err()
1378 .to_string(),
1379 "duplicate column name name: the property column repeats the property column"
1380 );
1381 }
1382
1383 #[test]
1384 fn from_parts_allows_empty_property_name() {
1385 assert!(TileLayer::from_parts("layer", 4096, vec![String::new()], vec![]).is_ok());
1387 }
1388
1389 #[test]
1390 fn from_parts_rejects_duplicate_property_name() {
1391 assert_eq!(
1392 TileLayer::from_parts("layer", 4096, vec!["dup".into(), "dup".into()], vec![])
1393 .unwrap_err()
1394 .to_string(),
1395 "duplicate column name dup: the property column repeats the property column"
1396 );
1397 }
1398
1399 #[test]
1400 fn push_feature_validates_property_count() {
1401 let mut layer = TileLayer::new("layer", 4096).unwrap();
1402 layer.add_property("name", PropKind::Str).unwrap();
1403 assert!(matches!(
1404 layer.push_feature(point_feature(vec![])),
1405 Err(MltError::PropertyLengthMismatch {
1406 expected: 1,
1407 actual: 0
1408 })
1409 ));
1410 }
1411
1412 #[test]
1413 fn push_feature_validates_property_kind() {
1414 let mut layer = TileLayer::new("layer", 4096).unwrap();
1415 layer.add_property("flag", PropKind::Bool).unwrap();
1416 layer
1417 .push_feature(point_feature(vec![PropValue::Bool(Some(true))]))
1418 .unwrap();
1419 assert!(matches!(
1420 layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1421 Err(MltError::PropertyKindMismatch {
1422 index: 0,
1423 expected: PropKind::Bool,
1424 actual: PropKind::I32,
1425 })
1426 ));
1427 }
1428
1429 #[test]
1430 fn declared_property_kind_is_enforced_for_first_feature() {
1431 let mut layer = TileLayer::new("layer", 4096).unwrap();
1432 layer.add_property("flag", PropKind::Bool).unwrap();
1433 assert!(matches!(
1434 layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1435 Err(MltError::PropertyKindMismatch {
1436 index: 0,
1437 expected: PropKind::Bool,
1438 actual: PropKind::I32,
1439 })
1440 ));
1441 }
1442
1443 #[cfg(feature = "unstable-v2")]
1444 #[test]
1445 fn push_feature_validates_m_value_count() {
1446 let mut layer = TileLayer::new("layer", 4096).unwrap();
1447 layer.add_m_value("m", PropKind::I32).unwrap();
1448 assert!(matches!(
1449 layer.push_feature(point_feature(vec![])),
1450 Err(MltError::MValueColumnCountMismatch {
1451 expected: 1,
1452 actual: 0
1453 })
1454 ));
1455 }
1456
1457 #[test]
1458 fn with_id_is_new_with_that_id() {
1459 let geometry = Geometry::Point(Point::new(1, 2));
1460 let mut expected = TileFeature::new(geometry.clone());
1461 expected.id = Some(7);
1462 assert_eq!(TileFeature::with_id(geometry, 7), expected);
1463 }
1464
1465 #[cfg(feature = "unstable-v2")]
1466 #[test]
1467 fn push_feature_validates_one_value_per_vertex() {
1468 let mut layer = TileLayer::new("layer", 4096).unwrap();
1469 layer.add_m_value("m", PropKind::I32).unwrap();
1470 let mut feature = point_feature(vec![]);
1471 feature.m_values = vec![MValue::I32(Some(vec![1, 2]))];
1472 assert!(matches!(
1473 layer.push_feature(feature),
1474 Err(MltError::MValueVertexCountMismatch {
1475 index: 0,
1476 expected: 1,
1477 actual: 2,
1478 })
1479 ));
1480 }
1481
1482 #[cfg(feature = "unstable-v2")]
1483 #[test]
1484 fn a_polygon_does_not_count_its_closing_vertices() {
1485 let ring = |pts: &[(i32, i32)]| {
1486 let mut ls = LineString::new(
1487 pts.iter()
1488 .map(|&(x, y)| geo_types::Coord { x, y })
1489 .collect(),
1490 );
1491 ls.close();
1492 ls
1493 };
1494 let poly = geo_types::Polygon::new(
1495 ring(&[(0, 0), (0, 4), (4, 4), (4, 0)]),
1496 vec![ring(&[(1, 1), (1, 2), (2, 2)])],
1497 );
1498 let feature = TileFeature::new(Geometry::Polygon(poly));
1499 assert_eq!(feature.vertex_count(), 7);
1500 }
1501
1502 #[cfg(feature = "unstable-v2")]
1503 #[test]
1504 fn a_shape_counts_its_root_as_the_first_level() {
1505 let kind = NestedKind::list(NestedKind::map([("a", NestedKind::Leaf(PropKind::I32))]));
1506 assert_eq!(kind.depth(), 3);
1507 }
1508
1509 #[cfg(feature = "unstable-v2")]
1510 #[test]
1511 fn a_map_may_leave_out_a_field_but_not_add_one() {
1512 let kind = NestedKind::map([
1513 ("a", NestedKind::Leaf(PropKind::I32)),
1514 ("b", NestedKind::Leaf(PropKind::Str)),
1515 ]);
1516 assert!(kind.accepts(&NestedValue::map([(
1517 "a",
1518 NestedValue::Leaf(PropValue::I32(Some(1)))
1519 )])));
1520 assert!(!kind.accepts(&NestedValue::map([(
1521 "c",
1522 NestedValue::Leaf(PropValue::I32(Some(1)))
1523 )])));
1524 assert!(!kind.accepts(&NestedValue::map([(
1525 "a",
1526 NestedValue::Leaf(PropValue::Str(Some("x".into())))
1527 )])));
1528 }
1529
1530 #[cfg(feature = "unstable-v2")]
1531 fn list_kind() -> NestedKind {
1532 NestedKind::list(NestedKind::Leaf(PropKind::I32))
1533 }
1534
1535 #[cfg(feature = "unstable-v2")]
1536 #[test]
1537 fn add_m_value_rejects_a_name_a_property_has() {
1538 let mut layer = TileLayer::new("layer", 4096).unwrap();
1539 layer.add_property("n", PropKind::I32).unwrap();
1540 assert_eq!(
1541 layer
1542 .add_m_value("n", PropKind::I32)
1543 .unwrap_err()
1544 .to_string(),
1545 "duplicate column name n: the m-value column repeats the property column"
1546 );
1547 }
1548
1549 #[cfg(feature = "unstable-v2")]
1550 #[test]
1551 fn add_m_value_rejects_a_name_a_nested_column_has() {
1552 let mut layer = TileLayer::new("layer", 4096).unwrap();
1553 layer.add_nested("n", list_kind()).unwrap();
1554 assert_eq!(
1555 layer
1556 .add_m_value("n", PropKind::I32)
1557 .unwrap_err()
1558 .to_string(),
1559 "duplicate column name n: the m-value column repeats the nested column"
1560 );
1561 }
1562
1563 #[cfg(feature = "unstable-v2")]
1564 #[test]
1565 fn add_m_value_rejects_duplicate_names() {
1566 let mut layer = TileLayer::new("layer", 4096).unwrap();
1567 layer.add_m_value("n", PropKind::I32).unwrap();
1568 assert_eq!(
1569 layer
1570 .add_m_value("n", PropKind::I32)
1571 .unwrap_err()
1572 .to_string(),
1573 "duplicate column name n: the m-value column repeats the m-value column"
1574 );
1575 }
1576
1577 #[cfg(feature = "unstable-v2")]
1578 #[test]
1579 fn add_property_rejects_a_name_an_m_value_has() {
1580 let mut layer = TileLayer::new("layer", 4096).unwrap();
1581 layer.add_m_value("n", PropKind::I32).unwrap();
1582 assert_eq!(
1583 layer
1584 .add_property("n", PropKind::I32)
1585 .unwrap_err()
1586 .to_string(),
1587 "duplicate column name n: the property column repeats the m-value column"
1588 );
1589 }
1590
1591 #[cfg(feature = "unstable-v2")]
1592 #[test]
1593 fn add_property_rejects_a_name_a_nested_column_has() {
1594 let mut layer = TileLayer::new("layer", 4096).unwrap();
1595 layer.add_nested("n", list_kind()).unwrap();
1596 assert_eq!(
1597 layer
1598 .add_property("n", PropKind::I32)
1599 .unwrap_err()
1600 .to_string(),
1601 "duplicate column name n: the property column repeats the nested column"
1602 );
1603 }
1604
1605 #[cfg(feature = "unstable-v2")]
1606 #[test]
1607 fn add_nested_rejects_a_name_a_property_has() {
1608 let mut layer = TileLayer::new("layer", 4096).unwrap();
1609 layer.add_property("n", PropKind::I32).unwrap();
1610 assert_eq!(
1611 layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1612 "duplicate column name n: the nested column repeats the property column"
1613 );
1614 }
1615
1616 #[cfg(feature = "unstable-v2")]
1617 #[test]
1618 fn add_nested_rejects_a_name_an_m_value_already_has() {
1619 let mut layer = TileLayer::new("layer", 4096).unwrap();
1620 layer.add_m_value("n", PropKind::I32).unwrap();
1621 assert_eq!(
1622 layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1623 "duplicate column name n: the nested column repeats the m-value column"
1624 );
1625 }
1626
1627 #[cfg(feature = "unstable-v2")]
1628 #[test]
1629 fn add_nested_rejects_duplicate_names() {
1630 let mut layer = TileLayer::new("layer", 4096).unwrap();
1631 layer.add_nested("n", list_kind()).unwrap();
1632 assert_eq!(
1633 layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1634 "duplicate column name n: the nested column repeats the nested column"
1635 );
1636 }
1637
1638 #[cfg(feature = "unstable-v2")]
1639 #[test]
1640 fn set_nested_rejects_a_value_of_another_shape() {
1641 let mut layer = TileLayer::new("layer", 4096).unwrap();
1642 let key = layer
1643 .add_nested("n", NestedKind::list(NestedKind::Leaf(PropKind::I32)))
1644 .unwrap();
1645 let mut feature = point_feature(vec![]);
1646 feature.nested = vec![NestedValue::List(None)];
1647 assert_eq!(
1648 feature
1649 .set_nested(key, NestedValue::Map(None))
1650 .unwrap_err()
1651 .to_string(),
1652 "nested column 0 was given a value of another shape"
1653 );
1654 }
1655
1656 #[test]
1657 fn builder_uses_declared_property_kind_for_defaults() {
1658 let mut builder = TileLayer::builder("layer", 4096).unwrap();
1659 let flag = builder.add_property("flag", PropKind::Bool).unwrap();
1660 let mut feature = builder.feature(Geometry::Point(Point::new(0, 0)));
1661 feature.property(flag, PropValue::Bool(Some(true))).unwrap();
1662 feature.finish().unwrap();
1663 let layer = builder.finish();
1664
1665 assert_eq!(
1666 layer.features()[0].properties()[0],
1667 PropValue::Bool(Some(true))
1668 );
1669 }
1670}