Skip to main content

mlt_core/decoder/
model.rs

1use std::fmt;
2use std::num::NonZeroU32;
3
4use num_enum::TryFromPrimitive;
5
6use crate::decoder::{Geometry, Id, Property};
7use crate::{DecodeState, Lazy, MltError, MltResult, Parsed};
8
9/// Non-zero tile extent.
10///
11/// Use [`Extent::new`] to validate raw integer input before storing it in
12/// owned row or staged layer structures.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct Extent(NonZeroU32);
15
16impl Extent {
17    pub fn new(value: u32) -> MltResult<Self> {
18        NonZeroU32::new(value)
19            .map(Self)
20            .ok_or(MltError::InvalidExtent(value))
21    }
22
23    #[must_use]
24    pub fn get(self) -> u32 {
25        self.0.get()
26    }
27}
28
29impl From<Extent> for NonZeroU32 {
30    fn from(value: Extent) -> Self {
31        value.0
32    }
33}
34
35/// A layer that can be one of the known types, or an unknown.
36///
37/// The decode-state type parameter `S` mirrors [`Layer01<'a, S>`]:
38/// - `Layer<'a>` / `Layer<'a, Lazy>` — freshly parsed; columns may still be raw bytes.
39/// - `Layer<'a, Parsed>` — returned by [`Layer::decode_all`]; all columns are decoded. Use `ParsedLayer` alias.
40#[non_exhaustive]
41pub enum Layer<'a, S: DecodeState = Lazy> {
42    /// MVT-compatible layer (tag = 1)
43    Tag01(Layer01<'a, S>),
44    /// Unknown layer with tag, size, and value
45    Unknown(Unknown<'a>),
46}
47pub type ParsedLayer<'a> = Layer<'a, Parsed>;
48
49impl<'a, S: DecodeState> fmt::Debug for Layer<'a, S>
50where
51    Layer01<'a, S>: fmt::Debug,
52{
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        match self {
55            Self::Tag01(l) => f.debug_tuple("Tag01").field(l).finish(),
56            Self::Unknown(u) => f.debug_tuple("Unknown").field(u).finish(),
57        }
58    }
59}
60
61/// Unknown layer data, stored as encoded bytes.
62///
63/// Returned inside [`Layer::Unknown`] for any layer tag that is not recognized
64/// by this version of the library. Consumers can inspect the tag and raw bytes
65/// to forward or log the layer without losing data.
66#[derive(Debug, Clone, Default, PartialEq)]
67pub struct Unknown<'a> {
68    pub(crate) tag: u8,
69    pub(crate) value: &'a [u8],
70}
71
72impl<'a> Unknown<'a> {
73    /// The raw layer tag identifying this unrecognised layer type.
74    #[must_use]
75    pub fn tag(&self) -> u32 {
76        u32::from(self.tag)
77    }
78
79    /// The raw encoded bytes of this layer's body.
80    #[must_use]
81    pub fn data(&self) -> &'a [u8] {
82        self.value
83    }
84}
85
86/// Column definition
87#[derive(Debug, PartialEq)]
88pub struct Column<'a> {
89    pub(crate) typ: ColumnType,
90    pub(crate) name: Option<&'a str>,
91    pub(crate) children: Vec<Self>,
92}
93
94/// Column data type, as stored in the tile
95#[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)]
96#[repr(u8)]
97pub enum ColumnType {
98    Id = 0,
99    OptId = 1,
100    LongId = 2,
101    OptLongId = 3,
102    Geometry = 4,
103    Bool = 10,
104    OptBool = 11,
105    I8 = 12,
106    OptI8 = 13,
107    U8 = 14,
108    OptU8 = 15,
109    I32 = 16,
110    OptI32 = 17,
111    U32 = 18,
112    OptU32 = 19,
113    I64 = 20,
114    OptI64 = 21,
115    U64 = 22,
116    OptU64 = 23,
117    F32 = 24,
118    OptF32 = 25,
119    F64 = 26,
120    OptF64 = 27,
121    Str = 28,
122    OptStr = 29,
123    SharedDict = 30,
124}
125
126/// Representation of an MLT feature table layer with tag `0x01` during decoding.
127///
128/// The type parameter `S` controls how columns are stored:
129///
130/// - `Layer01<'a>` / `Layer01<'a, Lazy>` (default) — columns are `LazyParsed` enums
131///   that may be raw or decoded. Use [`Layer01::decode_all`] to transition to `Layer01<Parsed>`.
132///
133/// - `Layer01<'a, Parsed>` — all columns are fully decoded. The fields `id`, `geometry`, and
134///   `properties` hold the parsed types directly, allowing infallible readonly access.
135///   There is a `ParsedLayer01<'a>` type alias for this.
136pub struct Layer01<'a, S: DecodeState = Lazy> {
137    pub(crate) name: &'a str,
138    pub(crate) extent: Extent,
139    pub(crate) id: Option<Id<'a, S>>,
140    pub(crate) geometry: Geometry<'a, S>,
141    pub(crate) properties: Vec<Property<'a, S>>,
142    #[cfg(fuzzing)]
143    pub(crate) layer_order: Vec<crate::decoder::fuzzing::LayerOrdering>,
144}
145
146pub type ParsedLayer01<'a> = Layer01<'a, Parsed>;
147
148impl<'a, S: DecodeState> Layer01<'a, S> {
149    #[must_use]
150    pub fn name(&self) -> &'a str {
151        self.name
152    }
153
154    #[must_use]
155    pub fn extent(&self) -> Extent {
156        self.extent
157    }
158}
159
160impl<'a, S> fmt::Debug for Layer01<'a, S>
161where
162    S: DecodeState,
163    Option<Id<'a, S>>: fmt::Debug,
164    Geometry<'a, S>: fmt::Debug,
165    Vec<Property<'a, S>>: fmt::Debug,
166{
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        let mut s = f.debug_struct("Layer01");
169        s.field("name", &self.name)
170            .field("extent", &self.extent)
171            .field("id", &self.id)
172            .field("geometry", &self.geometry)
173            .field("properties", &self.properties);
174        #[cfg(fuzzing)]
175        s.field("layer_order", &self.layer_order);
176        s.finish()
177    }
178}
179
180impl<'a, S> Clone for Layer01<'a, S>
181where
182    S: DecodeState,
183    Option<Id<'a, S>>: Clone,
184    Geometry<'a, S>: Clone,
185    Vec<Property<'a, S>>: Clone,
186{
187    fn clone(&self) -> Self {
188        Self {
189            name: self.name,
190            extent: self.extent,
191            id: self.id.clone(),
192            geometry: self.geometry.clone(),
193            properties: self.properties.clone(),
194            #[cfg(fuzzing)]
195            layer_order: self.layer_order.clone(),
196        }
197    }
198}
199
200/// Row-oriented working form for the optimizer.
201///
202/// All features are stored as a flat [`Vec<TileFeature>`] so that sorting is
203/// a single `sort_by_cached_key` call.  The `property_names` vec is parallel
204/// to every `TileFeature::properties` slice in this layer.
205#[derive(Debug, Clone, PartialEq)]
206pub struct TileLayer {
207    pub(crate) name: String,
208    pub(crate) extent: Extent,
209    /// Column names, parallel to `TileFeature::properties`.
210    pub(crate) property_names: Vec<String>,
211    /// Column types, parallel to `TileFeature::properties`.
212    pub(crate) property_kinds: Vec<PropKind>,
213    pub(crate) features: Vec<TileFeature>,
214}
215
216/// A single map feature in row form.
217#[derive(Debug, Clone, PartialEq)]
218pub struct TileFeature {
219    pub(crate) id: Option<u64>,
220    /// Geometry as a [`geo_types`] form
221    pub(crate) geometry: geo_types::Geometry<i32>,
222    /// One value per property column, in the same order as
223    /// [`TileLayer::property_names`].
224    pub(crate) properties: Vec<PropValue>,
225}
226
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
228pub struct PropertyKey(usize);
229
230impl PropertyKey {
231    #[must_use]
232    pub fn index(self) -> usize {
233        self.0
234    }
235}
236
237impl TileLayer {
238    pub fn new(name: impl Into<String>, extent: u32) -> MltResult<Self> {
239        Self::with_capacity(name, extent, 0)
240    }
241
242    pub fn with_capacity(name: impl Into<String>, extent: u32, features: usize) -> MltResult<Self> {
243        let name = name.into();
244        validate_layer_name(&name)?;
245        let extent = Extent::new(extent)?;
246        Ok(Self {
247            name,
248            extent,
249            property_names: Vec::new(),
250            property_kinds: Vec::new(),
251            features: Vec::with_capacity(features),
252        })
253    }
254
255    pub(crate) fn from_parts(
256        name: impl Into<String>,
257        extent: u32,
258        property_names: Vec<String>,
259        features: Vec<TileFeature>,
260    ) -> MltResult<Self> {
261        let name = name.into();
262        validate_layer_name(&name)?;
263        let extent = Extent::new(extent)?;
264        validate_property_names(&property_names)?;
265        let property_kinds = infer_property_kinds(&property_names, &features)?;
266        let layer = Self {
267            name,
268            extent,
269            property_names,
270            property_kinds,
271            features,
272        };
273        Ok(layer)
274    }
275
276    #[must_use]
277    pub fn name(&self) -> &str {
278        &self.name
279    }
280
281    #[must_use]
282    pub fn extent(&self) -> Extent {
283        self.extent
284    }
285
286    #[must_use]
287    pub fn property_names(&self) -> &[String] {
288        &self.property_names
289    }
290
291    #[must_use]
292    pub fn features(&self) -> &[TileFeature] {
293        &self.features
294    }
295
296    #[must_use]
297    pub(crate) fn features_mut(&mut self) -> &mut [TileFeature] {
298        &mut self.features
299    }
300
301    #[must_use]
302    pub fn feature_count(&self) -> usize {
303        self.features.len()
304    }
305
306    pub fn add_property(
307        &mut self,
308        name: impl Into<String>,
309        kind: PropKind,
310    ) -> MltResult<PropertyKey> {
311        let name = name.into();
312        if self.property_names.contains(&name) {
313            return Err(MltError::DuplicatePropertyName(name));
314        }
315        for feature in &mut self.features {
316            feature.properties.push(PropValue::null(kind));
317        }
318        self.property_names.push(name);
319        self.property_kinds.push(kind);
320        Ok(PropertyKey(self.property_names.len() - 1))
321    }
322
323    pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
324        self.validate_feature(&feature)?;
325        self.features.push(feature);
326        Ok(())
327    }
328
329    pub fn builder(name: impl Into<String>, extent: u32) -> MltResult<TileLayerBuilder> {
330        Ok(TileLayerBuilder {
331            layer: Self::new(name, extent)?,
332        })
333    }
334
335    fn validate_feature(&self, feature: &TileFeature) -> MltResult<()> {
336        let expected = self.property_names.len();
337        let actual = feature.properties.len();
338        if actual != expected {
339            return Err(MltError::PropertyLengthMismatch { expected, actual });
340        }
341        for (idx, prop) in feature.properties.iter().enumerate() {
342            let expected = self.property_kinds[idx];
343            let actual = PropKind::from(prop);
344            if actual != expected {
345                return Err(MltError::PropertyKindMismatch {
346                    index: idx,
347                    expected,
348                    actual,
349                });
350            }
351        }
352        Ok(())
353    }
354}
355
356impl TileFeature {
357    #[must_use]
358    pub fn new(geometry: geo_types::Geometry<i32>) -> Self {
359        Self {
360            id: None,
361            geometry,
362            properties: Vec::new(),
363        }
364    }
365
366    #[must_use]
367    pub fn with_id(geometry: geo_types::Geometry<i32>, id: u64) -> Self {
368        Self {
369            id: Some(id),
370            geometry,
371            properties: Vec::new(),
372        }
373    }
374
375    #[must_use]
376    pub fn id(&self) -> Option<u64> {
377        self.id
378    }
379
380    #[must_use]
381    pub fn geometry(&self) -> &geo_types::Geometry<i32> {
382        &self.geometry
383    }
384
385    #[must_use]
386    pub fn properties(&self) -> &[PropValue] {
387        &self.properties
388    }
389
390    #[must_use]
391    pub(crate) fn properties_mut(&mut self) -> &mut [PropValue] {
392        &mut self.properties
393    }
394
395    pub fn set_property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<()> {
396        let Some(prop) = self.properties.get_mut(key.index()) else {
397            return Err(MltError::PropertyLengthMismatch {
398                expected: key.index() + 1,
399                actual: self.properties.len(),
400            });
401        };
402        let expected = PropKind::from(&*prop);
403        let actual = PropKind::from(&value);
404        if actual != expected {
405            return Err(MltError::PropertyKindMismatch {
406                index: key.index(),
407                expected,
408                actual,
409            });
410        }
411        *prop = value;
412        Ok(())
413    }
414}
415
416pub struct TileLayerBuilder {
417    layer: TileLayer,
418}
419
420impl TileLayerBuilder {
421    pub fn add_property(
422        &mut self,
423        name: impl Into<String>,
424        kind: PropKind,
425    ) -> MltResult<PropertyKey> {
426        self.layer.add_property(name, kind)
427    }
428
429    pub fn feature(&mut self, geometry: geo_types::Geometry<i32>) -> TileFeatureBuilder<'_> {
430        let properties = self
431            .layer
432            .property_kinds
433            .iter()
434            .copied()
435            .map(PropValue::null)
436            .collect();
437        TileFeatureBuilder {
438            layer: self,
439            feature: TileFeature {
440                id: None,
441                geometry,
442                properties,
443            },
444        }
445    }
446
447    pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
448        self.layer.push_feature(feature)
449    }
450
451    #[must_use]
452    pub fn finish(self) -> TileLayer {
453        self.layer
454    }
455}
456
457pub struct TileFeatureBuilder<'a> {
458    layer: &'a mut TileLayerBuilder,
459    feature: TileFeature,
460}
461
462impl TileFeatureBuilder<'_> {
463    pub fn id(&mut self, id: Option<u64>) -> &mut Self {
464        self.feature.id = id;
465        self
466    }
467
468    pub fn property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<&mut Self> {
469        self.feature.set_property(key, value)?;
470        Ok(self)
471    }
472
473    pub fn finish(self) -> MltResult<()> {
474        self.layer.push_feature(self.feature)
475    }
476}
477
478/// A single typed value for one property of one feature.
479///
480/// Mirrors the scalar variants of `ParsedProperty` at the per-feature
481/// level. `SharedDict` items are flattened: each sub-field becomes its own
482/// `PropValue::Str` entry in `TileFeature::properties`, with the
483/// corresponding entry in `TileLayer::property_names` set to
484/// `"prefix:suffix"`.
485#[derive(Debug, Clone, PartialEq)]
486pub enum PropValue {
487    Bool(Option<bool>),
488    I8(Option<i8>),
489    U8(Option<u8>),
490    I32(Option<i32>),
491    U32(Option<u32>),
492    I64(Option<i64>),
493    U64(Option<u64>),
494    F32(Option<f32>),
495    F64(Option<f64>),
496    Str(Option<String>),
497}
498
499impl PropValue {
500    #[must_use]
501    pub fn kind(&self) -> PropKind {
502        self.into()
503    }
504
505    #[must_use]
506    pub fn is_null(&self) -> bool {
507        match self {
508            Self::Bool(v) => v.is_none(),
509            Self::I8(v) => v.is_none(),
510            Self::U8(v) => v.is_none(),
511            Self::I32(v) => v.is_none(),
512            Self::U32(v) => v.is_none(),
513            Self::I64(v) => v.is_none(),
514            Self::U64(v) => v.is_none(),
515            Self::F32(v) => v.is_none(),
516            Self::F64(v) => v.is_none(),
517            Self::Str(v) => v.is_none(),
518        }
519    }
520
521    #[must_use]
522    pub fn null(kind: PropKind) -> Self {
523        match kind {
524            PropKind::Bool => Self::Bool(None),
525            PropKind::I8 => Self::I8(None),
526            PropKind::U8 => Self::U8(None),
527            PropKind::I32 => Self::I32(None),
528            PropKind::U32 => Self::U32(None),
529            PropKind::I64 => Self::I64(None),
530            PropKind::U64 => Self::U64(None),
531            PropKind::F32 => Self::F32(None),
532            PropKind::F64 => Self::F64(None),
533            PropKind::Str => Self::Str(None),
534        }
535    }
536}
537
538#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::IntoStaticStr)]
539#[strum(serialize_all = "lowercase")]
540pub enum PropKind {
541    Bool,
542    I8,
543    U8,
544    I32,
545    U32,
546    I64,
547    U64,
548    F32,
549    F64,
550    Str,
551}
552
553fn validate_layer_name(name: &str) -> MltResult<()> {
554    if name.is_empty() {
555        Err(MltError::MissingLayerName)
556    } else {
557        Ok(())
558    }
559}
560
561fn validate_property_names(names: &[String]) -> MltResult<()> {
562    // Linear scan, not a HashSet: column counts are small, so this skips a per-layer alloc.
563    // Empty names are allowed; real MVT tiles contain them.
564    for (i, name) in names.iter().enumerate() {
565        if names[..i].iter().any(|n| n == name) {
566            return Err(MltError::DuplicatePropertyName(name.clone()));
567        }
568    }
569    Ok(())
570}
571
572fn infer_property_kinds(names: &[String], features: &[TileFeature]) -> MltResult<Vec<PropKind>> {
573    let mut kinds = vec![None; names.len()];
574    for feature in features {
575        let expected = names.len();
576        let actual = feature.properties.len();
577        if actual != expected {
578            return Err(MltError::PropertyLengthMismatch { expected, actual });
579        }
580        for (idx, prop) in feature.properties.iter().enumerate() {
581            let actual = PropKind::from(prop);
582            match kinds[idx] {
583                Some(expected) if expected != actual => {
584                    return Err(MltError::PropertyKindMismatch {
585                        index: idx,
586                        expected,
587                        actual,
588                    });
589                }
590                None => kinds[idx] = Some(actual),
591                _ => {}
592            }
593        }
594    }
595    Ok(kinds
596        .into_iter()
597        .map(|kind| kind.unwrap_or(PropKind::Str))
598        .collect())
599}
600impl From<&PropValue> for PropKind {
601    fn from(prop: &PropValue) -> Self {
602        match prop {
603            PropValue::Bool(_) => Self::Bool,
604            PropValue::I8(_) => Self::I8,
605            PropValue::U8(_) => Self::U8,
606            PropValue::I32(_) => Self::I32,
607            PropValue::U32(_) => Self::U32,
608            PropValue::I64(_) => Self::I64,
609            PropValue::U64(_) => Self::U64,
610            PropValue::F32(_) => Self::F32,
611            PropValue::F64(_) => Self::F64,
612            PropValue::Str(_) => Self::Str,
613        }
614    }
615}
616
617#[cfg(test)]
618mod tests {
619    use geo_types::{Geometry, Point};
620
621    use super::*;
622
623    fn point_feature(properties: Vec<PropValue>) -> TileFeature {
624        TileFeature {
625            id: None,
626            geometry: Geometry::Point(Point::new(0, 0)),
627            properties,
628        }
629    }
630
631    #[test]
632    fn tile_layer_constructor_rejects_empty_name() {
633        assert!(matches!(
634            TileLayer::new("", 4096),
635            Err(MltError::MissingLayerName)
636        ));
637    }
638
639    #[test]
640    fn tile_layer_constructor_rejects_zero_extent() {
641        assert!(matches!(
642            TileLayer::new("layer", 0),
643            Err(MltError::InvalidExtent(0))
644        ));
645    }
646
647    #[test]
648    fn add_property_rejects_duplicate_names() {
649        let mut layer = TileLayer::new("layer", 4096).unwrap();
650        layer.add_property("name", PropKind::Str).unwrap();
651        assert!(matches!(
652            layer.add_property("name", PropKind::Str),
653            Err(MltError::DuplicatePropertyName(name)) if name == "name"
654        ));
655    }
656
657    #[test]
658    fn from_parts_allows_empty_property_name() {
659        // Real MVT tiles contain empty keys.
660        assert!(TileLayer::from_parts("layer", 4096, vec![String::new()], vec![]).is_ok());
661    }
662
663    #[test]
664    fn from_parts_rejects_duplicate_property_name() {
665        assert!(matches!(
666            TileLayer::from_parts("layer", 4096, vec!["dup".into(), "dup".into()], vec![]),
667            Err(MltError::DuplicatePropertyName(name)) if name == "dup"
668        ));
669    }
670
671    #[test]
672    fn push_feature_validates_property_count() {
673        let mut layer = TileLayer::new("layer", 4096).unwrap();
674        layer.add_property("name", PropKind::Str).unwrap();
675        assert!(matches!(
676            layer.push_feature(point_feature(vec![])),
677            Err(MltError::PropertyLengthMismatch {
678                expected: 1,
679                actual: 0
680            })
681        ));
682    }
683
684    #[test]
685    fn push_feature_validates_property_kind() {
686        let mut layer = TileLayer::new("layer", 4096).unwrap();
687        layer.add_property("flag", PropKind::Bool).unwrap();
688        layer
689            .push_feature(point_feature(vec![PropValue::Bool(Some(true))]))
690            .unwrap();
691        assert!(matches!(
692            layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
693            Err(MltError::PropertyKindMismatch {
694                index: 0,
695                expected: PropKind::Bool,
696                actual: PropKind::I32,
697            })
698        ));
699    }
700
701    #[test]
702    fn declared_property_kind_is_enforced_for_first_feature() {
703        let mut layer = TileLayer::new("layer", 4096).unwrap();
704        layer.add_property("flag", PropKind::Bool).unwrap();
705        assert!(matches!(
706            layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
707            Err(MltError::PropertyKindMismatch {
708                index: 0,
709                expected: PropKind::Bool,
710                actual: PropKind::I32,
711            })
712        ));
713    }
714
715    #[test]
716    fn builder_uses_declared_property_kind_for_defaults() {
717        let mut builder = TileLayer::builder("layer", 4096).unwrap();
718        let flag = builder.add_property("flag", PropKind::Bool).unwrap();
719        let mut feature = builder.feature(Geometry::Point(Point::new(0, 0)));
720        feature.property(flag, PropValue::Bool(Some(true))).unwrap();
721        feature.finish().unwrap();
722        let layer = builder.finish();
723
724        assert_eq!(
725            layer.features()[0].properties()[0],
726            PropValue::Bool(Some(true))
727        );
728    }
729}