Skip to main content

mlt_core/
tile.rs

1//! Owned, row-oriented tile model.
2//!
3//! This is the representation tiles are built in and converted to/from:
4//! it is independent of the wire format, and is shared by the encoder, the MVT and
5//! `GeoJSON` converters, and the language bindings.
6//! The decoder's columnar types live in [`crate::decoder`].
7
8#[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/// Non-zero tile extent.
17///
18/// Use [`Extent::new`] to validate raw integer input before storing it in
19/// owned row or staged layer structures.
20#[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/// The kind of column a name belongs to, which a layer's names are unique across.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display)]
44pub enum ColumnRole {
45    /// An ordinary column of values, a shared dictionary's children included.
46    #[strum(serialize = "property")]
47    Property,
48    /// A vertex-scoped column of the m-value section.
49    #[cfg(feature = "unstable-v2")]
50    #[strum(serialize = "m-value")]
51    MValue,
52    /// A shredded column of maps or lists.
53    #[cfg(feature = "unstable-v2")]
54    #[strum(serialize = "nested")]
55    Nested,
56}
57
58/// Row-oriented working form for the optimizer.
59///
60/// All features are stored as a flat [`Vec<TileFeature>`] so that sorting is
61/// a single `sort_by_cached_key` call.  The `property_names` vec is parallel
62/// to every `TileFeature::properties` slice in this layer.
63#[derive(Debug, Clone, PartialEq)]
64pub struct TileLayer {
65    pub(crate) name: String,
66    pub(crate) extent: Extent,
67    /// Column names, parallel to `TileFeature::properties`.
68    pub(crate) property_names: Vec<String>,
69    /// Column types, parallel to `TileFeature::properties`.
70    pub(crate) property_kinds: Vec<PropKind>,
71    /// Vertex-scoped column names, parallel to `TileFeature::m_values`.
72    #[cfg(feature = "unstable-v2")]
73    pub(crate) m_value_names: Vec<String>,
74    /// Vertex-scoped column types, parallel to `TileFeature::m_values`.
75    #[cfg(feature = "unstable-v2")]
76    pub(crate) m_value_kinds: Vec<PropKind>,
77    /// Nested column names, parallel to `TileFeature::nested`.
78    #[cfg(feature = "unstable-v2")]
79    pub(crate) nested_names: Vec<String>,
80    /// Nested column shapes, parallel to `TileFeature::nested`.
81    #[cfg(feature = "unstable-v2")]
82    pub(crate) nested_kinds: Vec<NestedKind>,
83    pub(crate) features: Vec<TileFeature>,
84}
85
86/// A single map feature in row form.
87#[derive(Debug, Clone, PartialEq)]
88pub struct TileFeature {
89    pub(crate) id: Option<u64>,
90    /// Geometry as a [`geo_types`] form
91    pub(crate) geometry: Geometry<i32>,
92    /// One value per property column, in the same order as
93    /// [`TileLayer::property_names`].
94    pub(crate) properties: Vec<PropValue>,
95    /// One entry per m-value column, in the same order as
96    /// [`TileLayer::m_value_names`], each holding one value per vertex of this
97    /// feature's geometry or none at all.
98    #[cfg(feature = "unstable-v2")]
99    pub(crate) m_values: Vec<MValue>,
100    /// One value per nested column, in the same order as [`TileLayer::nested_names`].
101    #[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/// Handle to one of a layer's m-value columns, as [`PropertyKey`] is to a property column.
116#[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/// Handle to one of a layer's nested columns, as [`PropertyKey`] is to a property column.
129#[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/// How deep a nested column may go, counting its root as the first level.
142#[cfg(feature = "unstable-v2")]
143pub(crate) const MAX_NESTED_DEPTH: usize = 8;
144
145/// The shape of one nested column, the union of every value it holds.
146#[cfg(feature = "unstable-v2")]
147#[derive(Debug, Clone, PartialEq, Eq)]
148pub enum NestedKind {
149    Leaf(PropKind),
150    List(Box<Self>),
151    /// String keys, each with its own kind, so a heterogeneous object is one of these.
152    Map(BTreeMap<String, Self>),
153}
154
155#[cfg(feature = "unstable-v2")]
156impl NestedKind {
157    /// A map of the given fields, the shape a struct of them shreds into.
158    #[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    /// A list of `element`.
164    #[must_use]
165    pub fn list(element: Self) -> Self {
166        Self::List(Box::new(element))
167    }
168
169    /// How many levels this shape has, counting itself as the first.
170    #[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    /// The value a feature that carries nothing for this column holds.
180    #[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    /// Whether `value` is one this shape describes.
190    ///
191    /// A map may leave a field out, which is how a null field is written.
192    #[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    /// Whether a shape with no fields, which no struct node can hold, sits anywhere in this one.
210    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/// One feature's value for one nested column.
220///
221/// A map leaves out the keys it has no value for, so a null field is an absent one.
222#[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    /// A map of the given entries.
233    #[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    /// A list of the given items.
241    #[must_use]
242    pub fn list(items: impl IntoIterator<Item = Self>) -> Self {
243        Self::List(Some(items.into_iter().collect()))
244    }
245
246    /// Whether this carries nothing at all, which is how a nested value is null.
247    #[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    /// The heap bytes this value holds, which the decoder charges its budget for.
257    #[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    /// Whether the two are the same kind of value, which is all a feature can check
272    /// without the column's shape.
273    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    /// Reject a column name any column of this layer already holds.
369    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    /// Declare a vertex-scoped column, whose values every feature then holds or does not.
417    #[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    /// Name the vertex-scoped columns the features already carry, as
430    /// [`Self::from_parts`] names the property columns.
431    #[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    /// Declare a nested column of the given shape, which every feature then holds a value of.
458    ///
459    /// A scalar root is an ordinary property column, so [`NestedKind::Leaf`] is rejected here.
460    #[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    /// Name and shape the nested columns the features already carry, as
478    /// [`Self::from_parts`] names the property columns.
479    #[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    /// Check a feature's nested values against the layer's columns: one value per
522    /// column, of the shape the column declared.
523    #[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    /// Check a feature's m-values against the layer's columns: one entry per
543    /// column, of the column's kind, holding one value per vertex it has.
544    #[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    /// How many vertices this feature stores, which is how many values each of
611    /// its m-values holds.
612    ///
613    /// A polygon ring's closing vertex is not stored, so it is not counted.
614    #[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    /// Set this feature's values for one m-value column, which must hold one
627    /// value per vertex unless the feature has none at all.
628    #[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    /// Set this feature's value for one nested column, which must be the kind of
666    /// value the column holds.
667    ///
668    /// Only the layer knows the column's full shape, so that is checked when the
669    /// feature is pushed.
670    #[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/// A single typed value for one property of one feature.
815///
816/// Mirrors the scalar variants of `ParsedProperty` at the per-feature
817/// level. `SharedDict` items are flattened: each sub-field becomes its own
818/// `PropValue::Str` entry in `TileFeature::properties`, with the
819/// corresponding entry in `TileLayer::property_names` set to
820/// `"prefix:suffix"`.
821#[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/// A feature's values for one vertex-scoped column: one per vertex, or none at all.
843///
844/// The option is around the whole vector because a null is a feature's, not a
845/// vertex's: a feature either measures every one of its vertices or none of them.
846#[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        /// The data type of one property or m-value column.
875        #[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            /// The null value of `kind`.
901            #[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            /// How many values this holds, or [`None`] when the feature carries none.
921            #[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            /// The empty value of `kind`, which is what a feature with no values holds.
930            #[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
943/// How many of a ring's coordinates MLT stores, which is all of them but a closing one.
944pub(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/// How many vertices a geometry contributes to the layer's vertex sequence, which
954/// is what an m-value column holds one value per.
955#[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/// Check a nested column's shape: an interior root, no empty field set, and no
978/// more levels than the wire allows.
979#[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
1002/// Reject a column name one of `taken` already holds, naming the kind of column that holds it.
1003///
1004/// A layer has one namespace of column names, so this is the only rule every named column obeys.
1005pub(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
1022/// Reject a name repeated within one list of column names.
1023fn validate_unique_names(names: &[String], role: ColumnRole) -> MltResult<()> {
1024    // Linear scan, not a HashSet: column counts are small, so this skips a per-layer alloc.
1025    // Empty names are allowed; real MVT tiles contain them.
1026    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
1032/// One feature's values for one kind of column, so the checks over them are written once.
1033trait FeatureColumn: Sized {
1034    /// This kind of column's values, one per column of the layer.
1035    fn of(feature: &TileFeature) -> &[Self];
1036    fn column_kind(&self) -> PropKind;
1037    /// A feature holding a different number of values than the layer has columns.
1038    fn count_mismatch(expected: usize, actual: usize) -> MltError;
1039    /// A value of a different kind than its column.
1040    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
1088/// The kind of each column, taken from the features that carry values for it.
1089///
1090/// A column no feature carries a value for is [`PropKind::Str`].
1091fn 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
1118/// Check a feature's values for one kind of column: one per column, of the column's kind.
1119fn 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        // Real MVT tiles contain empty keys.
1183        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}