Skip to main content

mlt_core/decoder/
model.rs

1//! Version-agnostic decode-side layer types.
2//!
3//! [`Layer01`] is the in-memory columnar form for *both* tag `0x01` and tag
4//! `0x02` layers - the two differ only in wire format, which lives in
5//! [`super::model01`] and `model02`.
6
7use std::fmt;
8
9use crate::decoder::{Geometry, GeometryValues, Id, Property};
10#[cfg(feature = "unstable-v2")]
11use crate::decoder::{MValueColumn, Nested};
12use crate::tile::Extent;
13use crate::{DecodeState, Lazy, Parsed};
14
15/// A layer that can be one of the known types, or an unknown.
16///
17/// The decode-state type parameter `S` mirrors [`Layer01<'a, S>`]:
18/// - `Layer<'a>` / `Layer<'a, Lazy>` - freshly parsed; columns may still be raw bytes.
19/// - `Layer<'a, Parsed>` - returned by [`Layer::decode_all`]; all columns are decoded. Use `ParsedLayer` alias.
20#[non_exhaustive]
21pub enum Layer<'a, S: DecodeState = Lazy> {
22    /// MVT-compatible layer (tag = 1)
23    Tag01(Layer01<'a, S>),
24    /// Experimental v2 layer (tag = 2).
25    ///
26    /// Parsed into the same in-memory columnar representation as `Tag01` but with an more compact wire format.
27    #[cfg(feature = "unstable-v2")]
28    Tag02(Layer01<'a, S>),
29    /// Unknown layer with tag, size, and value
30    Unknown(Unknown<'a>),
31}
32pub type ParsedLayer<'a> = Layer<'a, Parsed>;
33
34impl<'a, S: DecodeState> fmt::Debug for Layer<'a, S>
35where
36    Layer01<'a, S>: fmt::Debug,
37{
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            Self::Tag01(l) => f.debug_tuple("Tag01").field(l).finish(),
41            #[cfg(feature = "unstable-v2")]
42            Self::Tag02(l) => f.debug_tuple("Tag02").field(l).finish(),
43            Self::Unknown(u) => f.debug_tuple("Unknown").field(u).finish(),
44        }
45    }
46}
47
48/// Unknown layer data, stored as encoded bytes.
49///
50/// Returned inside [`Layer::Unknown`] for any layer tag that is not recognized
51/// by this version of the library. Consumers can inspect the tag and raw bytes
52/// to forward or log the layer without losing data.
53#[derive(Debug, Clone, Default, PartialEq)]
54pub struct Unknown<'a> {
55    pub(crate) tag: u8,
56    pub(crate) value: &'a [u8],
57}
58
59impl<'a> Unknown<'a> {
60    /// The raw layer tag identifying this unrecognised layer type.
61    #[must_use]
62    pub fn tag(&self) -> u32 {
63        u32::from(self.tag)
64    }
65
66    /// The raw encoded bytes of this layer's body.
67    #[must_use]
68    pub fn data(&self) -> &'a [u8] {
69        self.value
70    }
71}
72
73/// Representation of an MLT feature table layer during decoding.
74///
75/// Used for both tag `0x01` and tag `0x02` layers - the name is historical.
76///
77/// The type parameter `S` controls how columns are stored:
78///
79/// - `Layer01<'a>` / `Layer01<'a, Lazy>` (default) - columns are `LazyParsed` enums
80///   that may be raw or decoded. Use [`Layer01::decode_all`] to transition to `Layer01<Parsed>`.
81///
82/// - `Layer01<'a, Parsed>` - all columns are fully decoded. The fields `id`, `geometry`, and
83///   `properties` hold the parsed types directly, allowing infallible readonly access.
84///   There is a `ParsedLayer01<'a>` type alias for this.
85pub struct Layer01<'a, S: DecodeState = Lazy> {
86    pub(crate) name: &'a str,
87    pub(crate) extent: Extent,
88    pub(crate) id: Option<Id<'a, S>>,
89    pub(crate) geometry: Geometry<'a, S>,
90    pub(crate) properties: Vec<Property<'a, S>>,
91    /// Nested columns, which only a v2 layer carries.
92    #[cfg(feature = "unstable-v2")]
93    pub(crate) nested: Vec<Nested<'a, S>>,
94    /// Vertex-scoped columns, which only a v2 layer carries.
95    #[cfg(feature = "unstable-v2")]
96    pub(crate) m_values: Vec<MValueColumn<'a, S>>,
97    #[cfg(fuzzing)]
98    pub(crate) layer_order: Vec<crate::decoder::fuzzing::LayerOrdering>,
99}
100
101pub type ParsedLayer01<'a> = Layer01<'a, Parsed>;
102
103impl<'a, S: DecodeState> Layer01<'a, S> {
104    #[must_use]
105    pub fn name(&self) -> &'a str {
106        self.name
107    }
108
109    #[must_use]
110    pub fn extent(&self) -> Extent {
111        self.extent
112    }
113}
114
115impl ParsedLayer01<'_> {
116    /// Returns the decoded geometry buffer for this layer.
117    ///
118    /// Provides access to the columnar geometry arrays (vertex buffer, offset arrays, geometry
119    /// types) for advanced use cases such as building typed arrays for WebAssembly or
120    /// performing spatial indexing. For iterating feature geometries as `geo_types` values,
121    /// prefer [`iter_features`](Self::iter_features) instead.
122    #[must_use]
123    pub fn geometry_values(&self) -> &GeometryValues {
124        &self.geometry
125    }
126
127    #[must_use]
128    pub fn feature_count(&self) -> usize {
129        self.geometry.vector_types.len()
130    }
131
132    /// The layer's vertex-scoped columns, each running over every vertex of
133    /// every feature it marks present.
134    #[cfg(feature = "unstable-v2")]
135    #[must_use]
136    pub fn m_values(&self) -> &[crate::decoder::ParsedMValue<'_>] {
137        &self.m_values
138    }
139
140    /// The layer's nested columns, each a tree over the features it marks present.
141    #[cfg(feature = "unstable-v2")]
142    #[must_use]
143    pub fn nested(&self) -> &[crate::decoder::ParsedNested<'_>] {
144        &self.nested
145    }
146}
147
148/// [`fmt::Debug`] and [`Clone`] for a layer, whose column bounds a `where` clause
149/// has to spell out because the decode state hides them behind a projection.
150///
151/// The m-value columns are only a field in a v2 build, so the bound on them only
152/// exists there, which a `where` clause cannot say for itself. Invoked with the
153/// column type in a v2 build and with nothing at all otherwise.
154macro_rules! layer01_traits {
155    ($($m_values:ty, $nested:ty)?) => {
156        impl<'a, S> fmt::Debug for Layer01<'a, S>
157        where
158            S: DecodeState,
159            Option<Id<'a, S>>: fmt::Debug,
160            Geometry<'a, S>: fmt::Debug,
161            Vec<Property<'a, S>>: fmt::Debug,
162            $($m_values: fmt::Debug, $nested: fmt::Debug,)?
163        {
164            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165                let mut s = f.debug_struct("Layer01");
166                s.field("name", &self.name)
167                    .field("extent", &self.extent)
168                    .field("id", &self.id)
169                    .field("geometry", &self.geometry)
170                    .field("properties", &self.properties);
171                #[cfg(feature = "unstable-v2")]
172                s.field("nested", &self.nested);
173                #[cfg(feature = "unstable-v2")]
174                s.field("m_values", &self.m_values);
175                #[cfg(fuzzing)]
176                s.field("layer_order", &self.layer_order);
177                s.finish()
178            }
179        }
180
181        impl<'a, S> Clone for Layer01<'a, S>
182        where
183            S: DecodeState,
184            Option<Id<'a, S>>: Clone,
185            Geometry<'a, S>: Clone,
186            Vec<Property<'a, S>>: Clone,
187            $($m_values: Clone, $nested: Clone,)?
188        {
189            fn clone(&self) -> Self {
190                Self {
191                    name: self.name,
192                    extent: self.extent,
193                    id: self.id.clone(),
194                    geometry: self.geometry.clone(),
195                    properties: self.properties.clone(),
196                    #[cfg(feature = "unstable-v2")]
197                    nested: self.nested.clone(),
198                    #[cfg(feature = "unstable-v2")]
199                    m_values: self.m_values.clone(),
200                    #[cfg(fuzzing)]
201                    layer_order: self.layer_order.clone(),
202                }
203            }
204        }
205    };
206}
207
208#[cfg(feature = "unstable-v2")]
209layer01_traits!(Vec<MValueColumn<'a, S>>, Vec<Nested<'a, S>>);
210#[cfg(not(feature = "unstable-v2"))]
211layer01_traits!();