Skip to main content

mlt_core/decoder/
model.rs

1use std::fmt;
2
3use num_enum::TryFromPrimitive;
4
5use crate::decoder::{Geometry, Id, Property};
6use crate::{DecodeState, Lazy, Parsed};
7
8/// A layer that can be one of the known types, or an unknown.
9///
10/// The decode-state type parameter `S` mirrors [`Layer01<'a, S>`]:
11/// - `Layer<'a>` / `Layer<'a, Lazy>` — freshly parsed; columns may still be raw bytes.
12/// - `Layer<'a, Parsed>` — returned by [`Layer::decode_all`]; all columns are decoded. Use `ParsedLayer` alias.
13#[non_exhaustive]
14pub enum Layer<'a, S: DecodeState = Lazy> {
15    /// MVT-compatible layer (tag = 1)
16    Tag01(Layer01<'a, S>),
17    /// Unknown layer with tag, size, and value
18    Unknown(Unknown<'a>),
19}
20pub type ParsedLayer<'a> = Layer<'a, Parsed>;
21
22impl<'a, S: DecodeState> fmt::Debug for Layer<'a, S>
23where
24    Layer01<'a, S>: fmt::Debug,
25{
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            Self::Tag01(l) => f.debug_tuple("Tag01").field(l).finish(),
29            Self::Unknown(u) => f.debug_tuple("Unknown").field(u).finish(),
30        }
31    }
32}
33
34/// Unknown layer data, stored as encoded bytes.
35///
36/// Returned inside [`Layer::Unknown`] for any layer tag that is not recognized
37/// by this version of the library. Consumers can inspect the tag and raw bytes
38/// to forward or log the layer without losing data.
39#[derive(Debug, Clone, Default, PartialEq)]
40pub struct Unknown<'a> {
41    pub(crate) tag: u8,
42    pub(crate) value: &'a [u8],
43}
44
45impl<'a> Unknown<'a> {
46    /// The raw layer tag identifying this unrecognised layer type.
47    #[must_use]
48    pub fn tag(&self) -> u32 {
49        u32::from(self.tag)
50    }
51
52    /// The raw encoded bytes of this layer's body.
53    #[must_use]
54    pub fn data(&self) -> &'a [u8] {
55        self.value
56    }
57}
58
59/// Column definition
60#[derive(Debug, PartialEq)]
61pub struct Column<'a> {
62    pub(crate) typ: ColumnType,
63    pub(crate) name: Option<&'a str>,
64    pub(crate) children: Vec<Self>,
65}
66
67/// Column data type, as stored in the tile
68#[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)]
69#[repr(u8)]
70pub enum ColumnType {
71    Id = 0,
72    OptId = 1,
73    LongId = 2,
74    OptLongId = 3,
75    Geometry = 4,
76    Bool = 10,
77    OptBool = 11,
78    I8 = 12,
79    OptI8 = 13,
80    U8 = 14,
81    OptU8 = 15,
82    I32 = 16,
83    OptI32 = 17,
84    U32 = 18,
85    OptU32 = 19,
86    I64 = 20,
87    OptI64 = 21,
88    U64 = 22,
89    OptU64 = 23,
90    F32 = 24,
91    OptF32 = 25,
92    F64 = 26,
93    OptF64 = 27,
94    Str = 28,
95    OptStr = 29,
96    SharedDict = 30,
97}
98
99/// Representation of an MLT feature table layer with tag `0x01` during decoding.
100///
101/// The type parameter `S` controls how columns are stored:
102///
103/// - `Layer01<'a>` / `Layer01<'a, Lazy>` (default) — columns are [`LazyParsed`](crate::LazyParsed) enums
104///   that may be raw or decoded. Use [`Layer01::decode_all`] to transition to `Layer01<Parsed>`.
105///
106/// - `Layer01<'a, Parsed>` — all columns are fully decoded. The fields `id`, `geometry`, and
107///   `properties` hold the parsed types directly, allowing infallible readonly access.
108///   There is a `ParsedLayer01<'a>` type alias for this.
109pub struct Layer01<'a, S: DecodeState = Lazy> {
110    pub name: &'a str,
111    pub extent: u32,
112    pub(crate) id: Option<Id<'a, S>>,
113    pub(crate) geometry: Geometry<'a, S>,
114    pub(crate) properties: Vec<Property<'a, S>>,
115    #[cfg(fuzzing)]
116    pub(crate) layer_order: Vec<crate::decoder::fuzzing::LayerOrdering>,
117}
118
119pub type ParsedLayer01<'a> = Layer01<'a, Parsed>;
120
121impl<'a, S> fmt::Debug for Layer01<'a, S>
122where
123    S: DecodeState,
124    Option<Id<'a, S>>: fmt::Debug,
125    Geometry<'a, S>: fmt::Debug,
126    Vec<Property<'a, S>>: fmt::Debug,
127{
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        let mut s = f.debug_struct("Layer01");
130        s.field("name", &self.name)
131            .field("extent", &self.extent)
132            .field("id", &self.id)
133            .field("geometry", &self.geometry)
134            .field("properties", &self.properties);
135        #[cfg(fuzzing)]
136        s.field("layer_order", &self.layer_order);
137        s.finish()
138    }
139}
140
141impl<'a, S> Clone for Layer01<'a, S>
142where
143    S: DecodeState,
144    Option<Id<'a, S>>: Clone,
145    Geometry<'a, S>: Clone,
146    Vec<Property<'a, S>>: Clone,
147{
148    fn clone(&self) -> Self {
149        Self {
150            name: self.name,
151            extent: self.extent,
152            id: self.id.clone(),
153            geometry: self.geometry.clone(),
154            properties: self.properties.clone(),
155            #[cfg(fuzzing)]
156            layer_order: self.layer_order.clone(),
157        }
158    }
159}
160
161/// Row-oriented working form for the optimizer.
162///
163/// All features are stored as a flat [`Vec<TileFeature>`] so that sorting is
164/// a single `sort_by_cached_key` call.  The `property_names` vec is parallel
165/// to every `TileFeature::properties` slice in this layer.
166#[derive(Debug, Clone, PartialEq)]
167pub struct TileLayer {
168    pub name: String,
169    pub extent: u32,
170    /// Column names, parallel to `TileFeature::properties`.
171    pub property_names: Vec<String>,
172    pub features: Vec<TileFeature>,
173}
174
175/// A single map feature in row form.
176#[derive(Debug, Clone, PartialEq)]
177pub struct TileFeature {
178    pub id: Option<u64>,
179    /// Geometry as a [`geo_types`] form
180    pub geometry: geo_types::Geometry<i32>,
181    /// One value per property column, in the same order as
182    /// [`TileLayer::property_names`].
183    pub properties: Vec<PropValue>,
184}
185
186/// A single typed value for one property of one feature.
187///
188/// Mirrors the scalar variants of `ParsedProperty` at the per-feature
189/// level. `SharedDict` items are flattened: each sub-field becomes its own
190/// `PropValue::Str` entry in `TileFeature::properties`, with the
191/// corresponding entry in `TileLayer::property_names` set to
192/// `"prefix:suffix"`.
193#[derive(Debug, Clone, PartialEq)]
194pub enum PropValue {
195    Bool(Option<bool>),
196    I8(Option<i8>),
197    U8(Option<u8>),
198    I32(Option<i32>),
199    U32(Option<u32>),
200    I64(Option<i64>),
201    U64(Option<u64>),
202    F32(Option<f32>),
203    F64(Option<f64>),
204    Str(Option<String>),
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::IntoStaticStr)]
208#[strum(serialize_all = "lowercase")]
209pub enum PropKind {
210    Bool,
211    I8,
212    U8,
213    I32,
214    U32,
215    I64,
216    U64,
217    F32,
218    F64,
219    Str,
220}
221impl From<&PropValue> for PropKind {
222    fn from(prop: &PropValue) -> Self {
223        match prop {
224            PropValue::Bool(_) => Self::Bool,
225            PropValue::I8(_) => Self::I8,
226            PropValue::U8(_) => Self::U8,
227            PropValue::I32(_) => Self::I32,
228            PropValue::U32(_) => Self::U32,
229            PropValue::I64(_) => Self::I64,
230            PropValue::U64(_) => Self::U64,
231            PropValue::F32(_) => Self::F32,
232            PropValue::F64(_) => Self::F64,
233            PropValue::Str(_) => Self::Str,
234        }
235    }
236}