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    /// Widen every `I8` and `U8` property column to `I32` and `U32`.
364    pub(crate) fn widen_8bit_ints(&mut self) {
365        for kind in &mut self.property_kinds {
366            if *kind == PropKind::I8 {
367                *kind = PropKind::I32;
368            } else if *kind == PropKind::U8 {
369                *kind = PropKind::U32;
370            }
371        }
372        for prop in self.features.iter_mut().flat_map(|f| &mut f.properties) {
373            if let PropValue::I8(v) = *prop {
374                *prop = PropValue::I32(v.map(i32::from));
375            } else if let PropValue::U8(v) = *prop {
376                *prop = PropValue::U32(v.map(u32::from));
377            }
378        }
379    }
380
381    #[must_use]
382    pub fn feature_count(&self) -> usize {
383        self.features.len()
384    }
385
386    /// Reject a column name any column of this layer already holds.
387    fn reject_name_in_use(&self, name: &str, role: ColumnRole) -> MltResult<()> {
388        let taken = self
389            .property_names
390            .iter()
391            .map(|n| (n.as_str(), ColumnRole::Property));
392        #[cfg(feature = "unstable-v2")]
393        let taken = taken
394            .chain(
395                self.m_value_names
396                    .iter()
397                    .map(|n| (n.as_str(), ColumnRole::MValue)),
398            )
399            .chain(
400                self.nested_names
401                    .iter()
402                    .map(|n| (n.as_str(), ColumnRole::Nested)),
403            );
404        reject_taken_name(name, role, taken)
405    }
406
407    pub fn add_property(
408        &mut self,
409        name: impl Into<String>,
410        kind: PropKind,
411    ) -> MltResult<PropertyKey> {
412        let name = name.into();
413        self.reject_name_in_use(&name, ColumnRole::Property)?;
414        for feature in &mut self.features {
415            feature.properties.push(PropValue::null(kind));
416        }
417        self.property_names.push(name);
418        self.property_kinds.push(kind);
419        Ok(PropertyKey(self.property_names.len() - 1))
420    }
421
422    #[cfg(feature = "unstable-v2")]
423    #[must_use]
424    pub fn m_value_names(&self) -> &[String] {
425        &self.m_value_names
426    }
427
428    #[cfg(feature = "unstable-v2")]
429    #[must_use]
430    pub fn m_value_kinds(&self) -> &[PropKind] {
431        &self.m_value_kinds
432    }
433
434    /// Declare a vertex-scoped column, whose values every feature then holds or does not.
435    #[cfg(feature = "unstable-v2")]
436    pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
437        let name = name.into();
438        self.reject_name_in_use(&name, ColumnRole::MValue)?;
439        for feature in &mut self.features {
440            feature.m_values.push(MValue::null(kind));
441        }
442        self.m_value_names.push(name);
443        self.m_value_kinds.push(kind);
444        Ok(MValueKey(self.m_value_names.len() - 1))
445    }
446
447    /// Name the vertex-scoped columns the features already carry, as
448    /// [`Self::from_parts`] names the property columns.
449    #[cfg(feature = "unstable-v2")]
450    pub(crate) fn with_m_value_names(mut self, names: Vec<String>) -> MltResult<Self> {
451        validate_unique_names(&names, ColumnRole::MValue)?;
452        for name in &names {
453            self.reject_name_in_use(name, ColumnRole::MValue)?;
454        }
455        self.m_value_kinds = infer_kinds::<MValue>(&names, &self.features)?;
456        self.m_value_names = names;
457        for feature in &self.features {
458            self.validate_m_values(feature)?;
459        }
460        Ok(self)
461    }
462
463    #[cfg(feature = "unstable-v2")]
464    #[must_use]
465    pub fn nested_names(&self) -> &[String] {
466        &self.nested_names
467    }
468
469    #[cfg(feature = "unstable-v2")]
470    #[must_use]
471    pub fn nested_kinds(&self) -> &[NestedKind] {
472        &self.nested_kinds
473    }
474
475    /// Declare a nested column of the given shape, which every feature then holds a value of.
476    ///
477    /// A scalar root is an ordinary property column, so [`NestedKind::Leaf`] is rejected here.
478    #[cfg(feature = "unstable-v2")]
479    pub fn add_nested(
480        &mut self,
481        name: impl Into<String>,
482        kind: NestedKind,
483    ) -> MltResult<NestedKey> {
484        let name = name.into();
485        self.reject_name_in_use(&name, ColumnRole::Nested)?;
486        validate_nested_kind(&name, &kind)?;
487        for feature in &mut self.features {
488            feature.nested.push(kind.null_value());
489        }
490        self.nested_names.push(name);
491        self.nested_kinds.push(kind);
492        Ok(NestedKey(self.nested_names.len() - 1))
493    }
494
495    /// Name and shape the nested columns the features already carry, as
496    /// [`Self::from_parts`] names the property columns.
497    #[cfg(feature = "unstable-v2")]
498    pub(crate) fn with_nested(
499        mut self,
500        names: Vec<String>,
501        kinds: Vec<NestedKind>,
502    ) -> MltResult<Self> {
503        validate_unique_names(&names, ColumnRole::Nested)?;
504        for name in &names {
505            self.reject_name_in_use(name, ColumnRole::Nested)?;
506        }
507        for (name, kind) in names.iter().zip(&kinds) {
508            validate_nested_kind(name, kind)?;
509        }
510        self.nested_names = names;
511        self.nested_kinds = kinds;
512        for feature in &self.features {
513            self.validate_nested(feature)?;
514        }
515        Ok(self)
516    }
517
518    pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
519        self.validate_feature(&feature)?;
520        self.features.push(feature);
521        Ok(())
522    }
523
524    pub fn builder(name: impl Into<String>, extent: u32) -> MltResult<TileLayerBuilder> {
525        Ok(TileLayerBuilder {
526            layer: Self::new(name, extent)?,
527        })
528    }
529
530    fn validate_feature(&self, feature: &TileFeature) -> MltResult<()> {
531        validate_kinds::<PropValue>(&self.property_kinds, feature)?;
532        #[cfg(feature = "unstable-v2")]
533        self.validate_m_values(feature)?;
534        #[cfg(feature = "unstable-v2")]
535        self.validate_nested(feature)?;
536        Ok(())
537    }
538
539    /// Check a feature's nested values against the layer's columns: one value per
540    /// column, of the shape the column declared.
541    #[cfg(feature = "unstable-v2")]
542    fn validate_nested(&self, feature: &TileFeature) -> MltResult<()> {
543        if feature.nested.len() != self.nested_kinds.len() {
544            return Err(MltError::NestedColumnCountMismatch {
545                expected: self.nested_kinds.len(),
546                actual: feature.nested.len(),
547            });
548        }
549        for (index, (value, kind)) in feature.nested.iter().zip(&self.nested_kinds).enumerate() {
550            if !kind.accepts(value) {
551                return Err(MltError::NestedValueMismatch {
552                    index,
553                    name: self.nested_names[index].clone(),
554                });
555            }
556        }
557        Ok(())
558    }
559
560    /// Check a feature's m-values against the layer's columns: one entry per
561    /// column, of the column's kind, holding one value per vertex it has.
562    #[cfg(feature = "unstable-v2")]
563    fn validate_m_values(&self, feature: &TileFeature) -> MltResult<()> {
564        validate_kinds::<MValue>(&self.m_value_kinds, feature)?;
565        let vertices = feature.vertex_count();
566        for (index, m_value) in feature.m_values.iter().enumerate() {
567            if let Some(len) = m_value.count()
568                && len != vertices
569            {
570                return Err(MltError::MValueVertexCountMismatch {
571                    index,
572                    expected: vertices,
573                    actual: len,
574                });
575            }
576        }
577        Ok(())
578    }
579}
580
581impl TileFeature {
582    #[must_use]
583    pub fn new(geometry: Geometry<i32>) -> Self {
584        Self {
585            id: None,
586            geometry,
587            properties: Vec::new(),
588            #[cfg(feature = "unstable-v2")]
589            m_values: Vec::new(),
590            #[cfg(feature = "unstable-v2")]
591            nested: Vec::new(),
592        }
593    }
594
595    #[must_use]
596    pub fn with_id(geometry: Geometry<i32>, id: u64) -> Self {
597        Self {
598            id: Some(id),
599            geometry,
600            properties: Vec::new(),
601            #[cfg(feature = "unstable-v2")]
602            m_values: Vec::new(),
603            #[cfg(feature = "unstable-v2")]
604            nested: Vec::new(),
605        }
606    }
607
608    #[must_use]
609    pub fn id(&self) -> Option<u64> {
610        self.id
611    }
612
613    #[must_use]
614    pub fn geometry(&self) -> &Geometry<i32> {
615        &self.geometry
616    }
617
618    #[must_use]
619    pub fn properties(&self) -> &[PropValue] {
620        &self.properties
621    }
622
623    #[must_use]
624    pub(crate) fn properties_mut(&mut self) -> &mut [PropValue] {
625        &mut self.properties
626    }
627
628    /// How many vertices this feature stores, which is how many values each of
629    /// its m-values holds.
630    ///
631    /// A polygon ring's closing vertex is not stored, so it is not counted.
632    #[cfg(feature = "unstable-v2")]
633    #[must_use]
634    pub fn vertex_count(&self) -> usize {
635        stored_vertex_count(&self.geometry)
636    }
637
638    #[cfg(feature = "unstable-v2")]
639    #[must_use]
640    pub fn m_values(&self) -> &[MValue] {
641        &self.m_values
642    }
643
644    /// Set this feature's values for one m-value column, which must hold one
645    /// value per vertex unless the feature has none at all.
646    #[cfg(feature = "unstable-v2")]
647    pub fn set_m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<()> {
648        let vertices = self.vertex_count();
649        let Some(slot) = self.m_values.get_mut(key.index()) else {
650            return Err(MltError::UnknownMValueKey {
651                index: key.index(),
652                columns: self.m_values.len(),
653            });
654        };
655        let expected = slot.kind();
656        let actual = value.kind();
657        if actual != expected {
658            return Err(MltError::MValueKindMismatch {
659                index: key.index(),
660                expected,
661                actual,
662            });
663        }
664        if let Some(len) = value.count()
665            && len != vertices
666        {
667            return Err(MltError::MValueVertexCountMismatch {
668                index: key.index(),
669                expected: vertices,
670                actual: len,
671            });
672        }
673        *slot = value;
674        Ok(())
675    }
676
677    #[cfg(feature = "unstable-v2")]
678    #[must_use]
679    pub fn nested(&self) -> &[NestedValue] {
680        &self.nested
681    }
682
683    /// Set this feature's value for one nested column, which must be the kind of
684    /// value the column holds.
685    ///
686    /// Only the layer knows the column's full shape, so that is checked when the
687    /// feature is pushed.
688    #[cfg(feature = "unstable-v2")]
689    pub fn set_nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<()> {
690        let columns = self.nested.len();
691        let Some(slot) = self.nested.get_mut(key.index()) else {
692            return Err(MltError::UnknownNestedKey {
693                index: key.index(),
694                columns,
695            });
696        };
697        if !slot.same_shape(&value) {
698            return Err(MltError::NestedShapeMismatch { index: key.index() });
699        }
700        *slot = value;
701        Ok(())
702    }
703
704    pub fn set_property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<()> {
705        let Some(prop) = self.properties.get_mut(key.index()) else {
706            return Err(MltError::PropertyLengthMismatch {
707                expected: key.index() + 1,
708                actual: self.properties.len(),
709            });
710        };
711        let expected = PropKind::from(&*prop);
712        let actual = PropKind::from(&value);
713        if actual != expected {
714            return Err(MltError::PropertyKindMismatch {
715                index: key.index(),
716                expected,
717                actual,
718            });
719        }
720        *prop = value;
721        Ok(())
722    }
723}
724
725pub struct TileLayerBuilder {
726    layer: TileLayer,
727}
728
729impl TileLayerBuilder {
730    pub fn add_property(
731        &mut self,
732        name: impl Into<String>,
733        kind: PropKind,
734    ) -> MltResult<PropertyKey> {
735        self.layer.add_property(name, kind)
736    }
737
738    #[cfg(feature = "unstable-v2")]
739    pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
740        self.layer.add_m_value(name, kind)
741    }
742
743    #[cfg(feature = "unstable-v2")]
744    pub fn add_nested(
745        &mut self,
746        name: impl Into<String>,
747        kind: NestedKind,
748    ) -> MltResult<NestedKey> {
749        self.layer.add_nested(name, kind)
750    }
751
752    pub fn feature(&mut self, geometry: Geometry<i32>) -> TileFeatureBuilder<'_> {
753        let properties = self
754            .layer
755            .property_kinds
756            .iter()
757            .copied()
758            .map(PropValue::null)
759            .collect();
760        #[cfg(feature = "unstable-v2")]
761        let m_values = self
762            .layer
763            .m_value_kinds
764            .iter()
765            .copied()
766            .map(MValue::null)
767            .collect();
768        #[cfg(feature = "unstable-v2")]
769        let nested = self
770            .layer
771            .nested_kinds
772            .iter()
773            .map(NestedKind::null_value)
774            .collect();
775        TileFeatureBuilder {
776            layer: self,
777            feature: TileFeature {
778                id: None,
779                geometry,
780                properties,
781                #[cfg(feature = "unstable-v2")]
782                m_values,
783                #[cfg(feature = "unstable-v2")]
784                nested,
785            },
786        }
787    }
788
789    pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
790        self.layer.push_feature(feature)
791    }
792
793    #[must_use]
794    pub fn finish(self) -> TileLayer {
795        self.layer
796    }
797}
798
799pub struct TileFeatureBuilder<'a> {
800    layer: &'a mut TileLayerBuilder,
801    feature: TileFeature,
802}
803
804impl TileFeatureBuilder<'_> {
805    pub fn id(&mut self, id: Option<u64>) -> &mut Self {
806        self.feature.id = id;
807        self
808    }
809
810    pub fn property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<&mut Self> {
811        self.feature.set_property(key, value)?;
812        Ok(self)
813    }
814
815    #[cfg(feature = "unstable-v2")]
816    pub fn m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<&mut Self> {
817        self.feature.set_m_value(key, value)?;
818        Ok(self)
819    }
820
821    #[cfg(feature = "unstable-v2")]
822    pub fn nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<&mut Self> {
823        self.feature.set_nested(key, value)?;
824        Ok(self)
825    }
826
827    pub fn finish(self) -> MltResult<()> {
828        self.layer.push_feature(self.feature)
829    }
830}
831
832/// A single typed value for one property of one feature.
833///
834/// Mirrors the scalar variants of `ParsedProperty` at the per-feature
835/// level. `SharedDict` items are flattened: each sub-field becomes its own
836/// `PropValue::Str` entry in `TileFeature::properties`, with the
837/// corresponding entry in `TileLayer::property_names` set to
838/// `"prefix:suffix"`.
839#[derive(Debug, Clone, PartialEq)]
840pub enum PropValue {
841    Bool(Option<bool>),
842    I8(Option<i8>),
843    U8(Option<u8>),
844    I32(Option<i32>),
845    U32(Option<u32>),
846    I64(Option<i64>),
847    U64(Option<u64>),
848    F32(Option<f32>),
849    F64(Option<f64>),
850    Str(Option<String>),
851}
852
853impl PropValue {
854    #[must_use]
855    pub fn kind(&self) -> PropKind {
856        self.into()
857    }
858}
859
860/// A feature's values for one vertex-scoped column: one per vertex, or none at all.
861///
862/// The option is around the whole vector because a null is a feature's, not a
863/// vertex's: a feature either measures every one of its vertices or none of them.
864#[cfg(feature = "unstable-v2")]
865#[derive(Debug, Clone, PartialEq)]
866pub enum MValue {
867    Bool(Option<Vec<bool>>),
868    I8(Option<Vec<i8>>),
869    U8(Option<Vec<u8>>),
870    I32(Option<Vec<i32>>),
871    U32(Option<Vec<u32>>),
872    I64(Option<Vec<i64>>),
873    U64(Option<Vec<u64>>),
874    F32(Option<Vec<f32>>),
875    F64(Option<Vec<f64>>),
876    Str(Option<Vec<String>>),
877}
878
879#[cfg(feature = "unstable-v2")]
880impl MValue {
881    #[must_use]
882    pub fn is_null(&self) -> bool {
883        self.count().is_none()
884    }
885}
886
887macro_rules! kind_mappings {
888    (
889        scalar { $($sv:ident),* $(,)? }
890        string { $($gv:ident),* $(,)? }
891    ) => {
892        /// The data type of one property or m-value column.
893        #[derive(Debug, Clone, Copy, PartialEq, Eq, strum::IntoStaticStr)]
894        #[strum(serialize_all = "lowercase")]
895        pub enum PropKind {
896            $($sv,)*
897            $($gv,)*
898        }
899
900        impl From<&PropValue> for PropKind {
901            fn from(prop: &PropValue) -> Self {
902                match prop {
903                    $(PropValue::$sv(_) => Self::$sv,)*
904                    $(PropValue::$gv(_) => Self::$gv,)*
905                }
906            }
907        }
908
909        impl PropValue {
910            #[must_use]
911            pub fn is_null(&self) -> bool {
912                match self {
913                    $(Self::$sv(v) => v.is_none(),)*
914                    $(Self::$gv(v) => v.is_none(),)*
915                }
916            }
917
918            /// The null value of `kind`.
919            #[must_use]
920            pub fn null(kind: PropKind) -> Self {
921                match kind {
922                    $(PropKind::$sv => Self::$sv(None),)*
923                    $(PropKind::$gv => Self::$gv(None),)*
924                }
925            }
926        }
927
928        #[cfg(feature = "unstable-v2")]
929        impl MValue {
930            #[must_use]
931            pub fn kind(&self) -> PropKind {
932                match self {
933                    $(Self::$sv(_) => PropKind::$sv,)*
934                    $(Self::$gv(_) => PropKind::$gv,)*
935                }
936            }
937
938            /// How many values this holds, or [`None`] when the feature carries none.
939            #[must_use]
940            pub fn count(&self) -> Option<usize> {
941                match self {
942                    $(Self::$sv(v) => v.as_ref().map(Vec::len),)*
943                    $(Self::$gv(v) => v.as_ref().map(Vec::len),)*
944                }
945            }
946
947            /// The empty value of `kind`, which is what a feature with no values holds.
948            #[must_use]
949            pub fn null(kind: PropKind) -> Self {
950                match kind {
951                    $(PropKind::$sv => Self::$sv(None),)*
952                    $(PropKind::$gv => Self::$gv(None),)*
953                }
954            }
955        }
956    };
957}
958
959with_kinds!(kind_mappings);
960
961/// How many of a ring's coordinates MLT stores, which is all of them but a closing one.
962pub(crate) fn stored_ring_len(ring: &LineString<i32>) -> usize {
963    let coords = &ring.0;
964    if coords.len() > 1 && coords.last() == coords.first() {
965        coords.len() - 1
966    } else {
967        coords.len()
968    }
969}
970
971/// How many vertices a geometry contributes to the layer's vertex sequence, which
972/// is what an m-value column holds one value per.
973#[cfg(feature = "unstable-v2")]
974pub(crate) fn stored_vertex_count(geom: &Geometry<i32>) -> usize {
975    let polygon = |poly: &geo_types::Polygon<i32>| {
976        std::iter::once(poly.exterior())
977            .chain(poly.interiors())
978            .map(stored_ring_len)
979            .sum::<usize>()
980    };
981    match geom {
982        Geometry::Point(_) => 1,
983        Geometry::Line(_) => 2,
984        Geometry::LineString(ls) => ls.0.len(),
985        Geometry::Polygon(p) => polygon(p),
986        Geometry::MultiPoint(mp) => mp.0.len(),
987        Geometry::MultiLineString(mls) => mls.iter().map(|ls| ls.0.len()).sum(),
988        Geometry::MultiPolygon(mp) => mp.iter().map(polygon).sum(),
989        Geometry::Triangle(t) => polygon(&t.to_polygon()),
990        Geometry::Rect(r) => polygon(&r.to_polygon()),
991        Geometry::GeometryCollection(gc) => gc.iter().map(stored_vertex_count).sum(),
992    }
993}
994
995/// Check a nested column's shape: an interior root, no empty field set, and no
996/// more levels than the wire allows.
997#[cfg(feature = "unstable-v2")]
998fn validate_nested_kind(name: &str, kind: &NestedKind) -> MltResult<()> {
999    if matches!(kind, NestedKind::Leaf(_)) {
1000        return Err(MltError::NestedRootIsLeaf(name.to_string()));
1001    }
1002    if kind.has_empty_map() {
1003        return Err(MltError::EmptyStructNode);
1004    }
1005    let depth = kind.depth();
1006    if depth > MAX_NESTED_DEPTH {
1007        return Err(MltError::NestedTooDeep(depth));
1008    }
1009    Ok(())
1010}
1011
1012fn validate_layer_name(name: &str) -> MltResult<()> {
1013    if name.is_empty() {
1014        Err(MltError::MissingLayerName)
1015    } else {
1016        Ok(())
1017    }
1018}
1019
1020/// Reject a column name one of `taken` already holds, naming the kind of column that holds it.
1021///
1022/// A layer has one namespace of column names, so this is the only rule every named column obeys.
1023pub(crate) fn reject_taken_name<'a>(
1024    name: &str,
1025    role: ColumnRole,
1026    taken: impl IntoIterator<Item = (&'a str, ColumnRole)>,
1027) -> MltResult<()> {
1028    for (seen, taken_by) in taken {
1029        if seen == name {
1030            return Err(MltError::DuplicateColumnName {
1031                name: name.to_string(),
1032                role,
1033                taken_by,
1034            });
1035        }
1036    }
1037    Ok(())
1038}
1039
1040/// Reject a name repeated within one list of column names.
1041fn validate_unique_names(names: &[String], role: ColumnRole) -> MltResult<()> {
1042    // Linear scan, not a HashSet: column counts are small, so this skips a per-layer alloc.
1043    // Empty names are allowed; real MVT tiles contain them.
1044    for (i, name) in names.iter().enumerate() {
1045        reject_taken_name(name, role, names[..i].iter().map(|n| (n.as_str(), role)))?;
1046    }
1047    Ok(())
1048}
1049
1050/// One feature's values for one kind of column, so the checks over them are written once.
1051trait FeatureColumn: Sized {
1052    /// This kind of column's values, one per column of the layer.
1053    fn of(feature: &TileFeature) -> &[Self];
1054    fn column_kind(&self) -> PropKind;
1055    /// A feature holding a different number of values than the layer has columns.
1056    fn count_mismatch(expected: usize, actual: usize) -> MltError;
1057    /// A value of a different kind than its column.
1058    fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError;
1059}
1060
1061impl FeatureColumn for PropValue {
1062    fn of(feature: &TileFeature) -> &[Self] {
1063        &feature.properties
1064    }
1065
1066    fn column_kind(&self) -> PropKind {
1067        PropKind::from(self)
1068    }
1069
1070    fn count_mismatch(expected: usize, actual: usize) -> MltError {
1071        MltError::PropertyLengthMismatch { expected, actual }
1072    }
1073
1074    fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1075        MltError::PropertyKindMismatch {
1076            index,
1077            expected,
1078            actual,
1079        }
1080    }
1081}
1082
1083#[cfg(feature = "unstable-v2")]
1084impl FeatureColumn for MValue {
1085    fn of(feature: &TileFeature) -> &[Self] {
1086        &feature.m_values
1087    }
1088
1089    fn column_kind(&self) -> PropKind {
1090        self.kind()
1091    }
1092
1093    fn count_mismatch(expected: usize, actual: usize) -> MltError {
1094        MltError::MValueColumnCountMismatch { expected, actual }
1095    }
1096
1097    fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1098        MltError::MValueKindMismatch {
1099            index,
1100            expected,
1101            actual,
1102        }
1103    }
1104}
1105
1106/// The kind of each column, taken from the features that carry values for it.
1107///
1108/// A column no feature carries a value for is [`PropKind::Str`].
1109fn infer_kinds<T: FeatureColumn>(
1110    names: &[String],
1111    features: &[TileFeature],
1112) -> MltResult<Vec<PropKind>> {
1113    let mut kinds = vec![None; names.len()];
1114    for feature in features {
1115        let values = T::of(feature);
1116        if values.len() != names.len() {
1117            return Err(T::count_mismatch(names.len(), values.len()));
1118        }
1119        for (index, value) in values.iter().enumerate() {
1120            let actual = value.column_kind();
1121            match kinds[index] {
1122                Some(expected) if expected != actual => {
1123                    return Err(T::kind_mismatch(index, expected, actual));
1124                }
1125                None => kinds[index] = Some(actual),
1126                _ => {}
1127            }
1128        }
1129    }
1130    Ok(kinds
1131        .into_iter()
1132        .map(|kind| kind.unwrap_or(PropKind::Str))
1133        .collect())
1134}
1135
1136/// Check a feature's values for one kind of column: one per column, of the column's kind.
1137fn validate_kinds<T: FeatureColumn>(kinds: &[PropKind], feature: &TileFeature) -> MltResult<()> {
1138    let values = T::of(feature);
1139    if values.len() != kinds.len() {
1140        return Err(T::count_mismatch(kinds.len(), values.len()));
1141    }
1142    for (index, (value, expected)) in values.iter().zip(kinds).enumerate() {
1143        let actual = value.column_kind();
1144        if actual != *expected {
1145            return Err(T::kind_mismatch(index, *expected, actual));
1146        }
1147    }
1148    Ok(())
1149}
1150
1151#[cfg(test)]
1152mod tests {
1153    use geo_types::{Geometry, Point};
1154
1155    use super::*;
1156
1157    fn point_feature(properties: Vec<PropValue>) -> TileFeature {
1158        TileFeature {
1159            id: None,
1160            geometry: Geometry::Point(Point::new(0, 0)),
1161            properties,
1162            #[cfg(feature = "unstable-v2")]
1163            m_values: Vec::new(),
1164            #[cfg(feature = "unstable-v2")]
1165            nested: Vec::new(),
1166        }
1167    }
1168
1169    #[test]
1170    fn tile_layer_constructor_rejects_empty_name() {
1171        assert!(matches!(
1172            TileLayer::new("", 4096),
1173            Err(MltError::MissingLayerName)
1174        ));
1175    }
1176
1177    #[test]
1178    fn tile_layer_constructor_rejects_zero_extent() {
1179        assert!(matches!(
1180            TileLayer::new("layer", 0),
1181            Err(MltError::InvalidExtent(0))
1182        ));
1183    }
1184
1185    #[test]
1186    fn add_property_rejects_duplicate_names() {
1187        let mut layer = TileLayer::new("layer", 4096).unwrap();
1188        layer.add_property("name", PropKind::Str).unwrap();
1189        assert_eq!(
1190            layer
1191                .add_property("name", PropKind::Str)
1192                .unwrap_err()
1193                .to_string(),
1194            "duplicate column name name: the property column repeats the property column"
1195        );
1196    }
1197
1198    #[test]
1199    fn from_parts_allows_empty_property_name() {
1200        // Real MVT tiles contain empty keys.
1201        assert!(TileLayer::from_parts("layer", 4096, vec![String::new()], vec![]).is_ok());
1202    }
1203
1204    #[test]
1205    fn from_parts_rejects_duplicate_property_name() {
1206        assert_eq!(
1207            TileLayer::from_parts("layer", 4096, vec!["dup".into(), "dup".into()], vec![])
1208                .unwrap_err()
1209                .to_string(),
1210            "duplicate column name dup: the property column repeats the property column"
1211        );
1212    }
1213
1214    #[test]
1215    fn push_feature_validates_property_count() {
1216        let mut layer = TileLayer::new("layer", 4096).unwrap();
1217        layer.add_property("name", PropKind::Str).unwrap();
1218        assert!(matches!(
1219            layer.push_feature(point_feature(vec![])),
1220            Err(MltError::PropertyLengthMismatch {
1221                expected: 1,
1222                actual: 0
1223            })
1224        ));
1225    }
1226
1227    #[test]
1228    fn push_feature_validates_property_kind() {
1229        let mut layer = TileLayer::new("layer", 4096).unwrap();
1230        layer.add_property("flag", PropKind::Bool).unwrap();
1231        layer
1232            .push_feature(point_feature(vec![PropValue::Bool(Some(true))]))
1233            .unwrap();
1234        assert!(matches!(
1235            layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1236            Err(MltError::PropertyKindMismatch {
1237                index: 0,
1238                expected: PropKind::Bool,
1239                actual: PropKind::I32,
1240            })
1241        ));
1242    }
1243
1244    #[test]
1245    fn declared_property_kind_is_enforced_for_first_feature() {
1246        let mut layer = TileLayer::new("layer", 4096).unwrap();
1247        layer.add_property("flag", PropKind::Bool).unwrap();
1248        assert!(matches!(
1249            layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1250            Err(MltError::PropertyKindMismatch {
1251                index: 0,
1252                expected: PropKind::Bool,
1253                actual: PropKind::I32,
1254            })
1255        ));
1256    }
1257
1258    #[cfg(feature = "unstable-v2")]
1259    #[test]
1260    fn push_feature_validates_m_value_count() {
1261        let mut layer = TileLayer::new("layer", 4096).unwrap();
1262        layer.add_m_value("m", PropKind::I32).unwrap();
1263        assert!(matches!(
1264            layer.push_feature(point_feature(vec![])),
1265            Err(MltError::MValueColumnCountMismatch {
1266                expected: 1,
1267                actual: 0
1268            })
1269        ));
1270    }
1271
1272    #[cfg(feature = "unstable-v2")]
1273    #[test]
1274    fn push_feature_validates_one_value_per_vertex() {
1275        let mut layer = TileLayer::new("layer", 4096).unwrap();
1276        layer.add_m_value("m", PropKind::I32).unwrap();
1277        let mut feature = point_feature(vec![]);
1278        feature.m_values = vec![MValue::I32(Some(vec![1, 2]))];
1279        assert!(matches!(
1280            layer.push_feature(feature),
1281            Err(MltError::MValueVertexCountMismatch {
1282                index: 0,
1283                expected: 1,
1284                actual: 2,
1285            })
1286        ));
1287    }
1288
1289    #[cfg(feature = "unstable-v2")]
1290    #[test]
1291    fn a_polygon_does_not_count_its_closing_vertices() {
1292        let ring = |pts: &[(i32, i32)]| {
1293            let mut ls = LineString::new(
1294                pts.iter()
1295                    .map(|&(x, y)| geo_types::Coord { x, y })
1296                    .collect(),
1297            );
1298            ls.close();
1299            ls
1300        };
1301        let poly = geo_types::Polygon::new(
1302            ring(&[(0, 0), (0, 4), (4, 4), (4, 0)]),
1303            vec![ring(&[(1, 1), (1, 2), (2, 2)])],
1304        );
1305        let feature = TileFeature::new(Geometry::Polygon(poly));
1306        assert_eq!(feature.vertex_count(), 7);
1307    }
1308
1309    #[cfg(feature = "unstable-v2")]
1310    #[test]
1311    fn a_shape_counts_its_root_as_the_first_level() {
1312        let kind = NestedKind::list(NestedKind::map([("a", NestedKind::Leaf(PropKind::I32))]));
1313        assert_eq!(kind.depth(), 3);
1314    }
1315
1316    #[cfg(feature = "unstable-v2")]
1317    #[test]
1318    fn a_map_may_leave_out_a_field_but_not_add_one() {
1319        let kind = NestedKind::map([
1320            ("a", NestedKind::Leaf(PropKind::I32)),
1321            ("b", NestedKind::Leaf(PropKind::Str)),
1322        ]);
1323        assert!(kind.accepts(&NestedValue::map([(
1324            "a",
1325            NestedValue::Leaf(PropValue::I32(Some(1)))
1326        )])));
1327        assert!(!kind.accepts(&NestedValue::map([(
1328            "c",
1329            NestedValue::Leaf(PropValue::I32(Some(1)))
1330        )])));
1331        assert!(!kind.accepts(&NestedValue::map([(
1332            "a",
1333            NestedValue::Leaf(PropValue::Str(Some("x".into())))
1334        )])));
1335    }
1336
1337    #[cfg(feature = "unstable-v2")]
1338    fn list_kind() -> NestedKind {
1339        NestedKind::list(NestedKind::Leaf(PropKind::I32))
1340    }
1341
1342    #[cfg(feature = "unstable-v2")]
1343    #[test]
1344    fn add_m_value_rejects_a_name_a_property_has() {
1345        let mut layer = TileLayer::new("layer", 4096).unwrap();
1346        layer.add_property("n", PropKind::I32).unwrap();
1347        assert_eq!(
1348            layer
1349                .add_m_value("n", PropKind::I32)
1350                .unwrap_err()
1351                .to_string(),
1352            "duplicate column name n: the m-value column repeats the property column"
1353        );
1354    }
1355
1356    #[cfg(feature = "unstable-v2")]
1357    #[test]
1358    fn add_m_value_rejects_a_name_a_nested_column_has() {
1359        let mut layer = TileLayer::new("layer", 4096).unwrap();
1360        layer.add_nested("n", list_kind()).unwrap();
1361        assert_eq!(
1362            layer
1363                .add_m_value("n", PropKind::I32)
1364                .unwrap_err()
1365                .to_string(),
1366            "duplicate column name n: the m-value column repeats the nested column"
1367        );
1368    }
1369
1370    #[cfg(feature = "unstable-v2")]
1371    #[test]
1372    fn add_m_value_rejects_duplicate_names() {
1373        let mut layer = TileLayer::new("layer", 4096).unwrap();
1374        layer.add_m_value("n", PropKind::I32).unwrap();
1375        assert_eq!(
1376            layer
1377                .add_m_value("n", PropKind::I32)
1378                .unwrap_err()
1379                .to_string(),
1380            "duplicate column name n: the m-value column repeats the m-value column"
1381        );
1382    }
1383
1384    #[cfg(feature = "unstable-v2")]
1385    #[test]
1386    fn add_property_rejects_a_name_an_m_value_has() {
1387        let mut layer = TileLayer::new("layer", 4096).unwrap();
1388        layer.add_m_value("n", PropKind::I32).unwrap();
1389        assert_eq!(
1390            layer
1391                .add_property("n", PropKind::I32)
1392                .unwrap_err()
1393                .to_string(),
1394            "duplicate column name n: the property column repeats the m-value column"
1395        );
1396    }
1397
1398    #[cfg(feature = "unstable-v2")]
1399    #[test]
1400    fn add_property_rejects_a_name_a_nested_column_has() {
1401        let mut layer = TileLayer::new("layer", 4096).unwrap();
1402        layer.add_nested("n", list_kind()).unwrap();
1403        assert_eq!(
1404            layer
1405                .add_property("n", PropKind::I32)
1406                .unwrap_err()
1407                .to_string(),
1408            "duplicate column name n: the property column repeats the nested column"
1409        );
1410    }
1411
1412    #[cfg(feature = "unstable-v2")]
1413    #[test]
1414    fn add_nested_rejects_a_name_a_property_has() {
1415        let mut layer = TileLayer::new("layer", 4096).unwrap();
1416        layer.add_property("n", PropKind::I32).unwrap();
1417        assert_eq!(
1418            layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1419            "duplicate column name n: the nested column repeats the property column"
1420        );
1421    }
1422
1423    #[cfg(feature = "unstable-v2")]
1424    #[test]
1425    fn add_nested_rejects_a_name_an_m_value_already_has() {
1426        let mut layer = TileLayer::new("layer", 4096).unwrap();
1427        layer.add_m_value("n", PropKind::I32).unwrap();
1428        assert_eq!(
1429            layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1430            "duplicate column name n: the nested column repeats the m-value column"
1431        );
1432    }
1433
1434    #[cfg(feature = "unstable-v2")]
1435    #[test]
1436    fn add_nested_rejects_duplicate_names() {
1437        let mut layer = TileLayer::new("layer", 4096).unwrap();
1438        layer.add_nested("n", list_kind()).unwrap();
1439        assert_eq!(
1440            layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1441            "duplicate column name n: the nested column repeats the nested column"
1442        );
1443    }
1444
1445    #[cfg(feature = "unstable-v2")]
1446    #[test]
1447    fn set_nested_rejects_a_value_of_another_shape() {
1448        let mut layer = TileLayer::new("layer", 4096).unwrap();
1449        let key = layer
1450            .add_nested("n", NestedKind::list(NestedKind::Leaf(PropKind::I32)))
1451            .unwrap();
1452        let mut feature = point_feature(vec![]);
1453        feature.nested = vec![NestedValue::List(None)];
1454        assert_eq!(
1455            feature
1456                .set_nested(key, NestedValue::Map(None))
1457                .unwrap_err()
1458                .to_string(),
1459            "nested column 0 was given a value of another shape"
1460        );
1461    }
1462
1463    #[test]
1464    fn builder_uses_declared_property_kind_for_defaults() {
1465        let mut builder = TileLayer::builder("layer", 4096).unwrap();
1466        let flag = builder.add_property("flag", PropKind::Bool).unwrap();
1467        let mut feature = builder.feature(Geometry::Point(Point::new(0, 0)));
1468        feature.property(flag, PropValue::Bool(Some(true))).unwrap();
1469        feature.finish().unwrap();
1470        let layer = builder.finish();
1471
1472        assert_eq!(
1473            layer.features()[0].properties()[0],
1474            PropValue::Bool(Some(true))
1475        );
1476    }
1477}