1use std::borrow::Cow;
2use std::collections::HashSet;
3
4use derive_debug::Dbg;
5
6use crate::decoder::{DictionaryType, GeometryValues, RleLayout, StreamType};
7use crate::encoder::geometry::VertexBufferType;
8use crate::encoder::{IntEncoder, StagedId, StagedProperty};
9use crate::tile::Extent;
10use crate::{MltError, MltResult};
11
12#[derive(Debug, Clone, Default, PartialEq)]
14pub struct EncodedUnknown {
15 pub(crate) tag: u8,
16 pub(crate) value: Vec<u8>,
17}
18
19impl EncodedUnknown {
20 pub fn new(tag: u8, value: Vec<u8>) -> MltResult<Self> {
21 if tag == 1 {
22 return Err(MltError::ParsingColumnType(tag));
23 }
24 Ok(Self { tag, value })
25 }
26
27 #[must_use]
28 pub fn tag(&self) -> u32 {
29 u32::from(self.tag)
30 }
31
32 #[must_use]
33 pub fn data(&self) -> &[u8] {
34 &self.value
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq)]
41pub struct CurveParams {
42 pub shift: u32,
43 pub bits: u32,
44}
45
46impl Default for CurveParams {
47 fn default() -> Self {
48 Self { shift: 0, bits: 1 }
49 }
50}
51
52impl CurveParams {
53 #[must_use]
55 pub fn from_vertices(vertices: &[i32]) -> Self {
56 if vertices.is_empty() {
57 return Self::default();
58 }
59 let (min, max) = vertices
60 .iter()
61 .fold((i32::MAX, i32::MIN), |(mn, mx), &v| (mn.min(v), mx.max(v)));
62 crate::codecs::hilbert::hilbert_curve_params_from_bounds(min, max)
63 }
64}
65
66#[derive(Debug, PartialEq, Clone)]
73pub struct StagedLayer {
74 pub(crate) name: String,
75 pub(crate) extent: Extent,
76 pub(crate) id: StagedId,
77 pub(crate) geometry: GeometryValues,
78 pub(crate) properties: Vec<StagedProperty>,
79}
80
81#[cfg_attr(not(feature = "__private"), allow(dead_code))]
82impl StagedLayer {
83 pub fn new(
84 name: impl Into<String>,
85 extent: u32,
86 id: StagedId,
87 geometry: GeometryValues,
88 properties: Vec<StagedProperty>,
89 ) -> MltResult<Self> {
90 let name = name.into();
91 if name.is_empty() {
92 return Err(MltError::MissingLayerName);
93 }
94 let extent = Extent::new(extent)?;
95 let feature_count = geometry.feature_count();
96 if let Some(actual) = id.feature_count()
97 && actual != feature_count
98 {
99 return Err(MltError::StagedFeatureCountMismatch {
100 column: "id".into(),
101 expected: feature_count,
102 actual,
103 });
104 }
105 {
109 let mut seen: HashSet<Cow<str>> = HashSet::new();
110 for property in &properties {
111 let actual = property.feature_count();
112 if actual != feature_count {
113 return Err(MltError::StagedFeatureCountMismatch {
114 column: property.name().to_string(),
115 expected: feature_count,
116 actual,
117 });
118 }
119 match property {
120 StagedProperty::SharedDict(sd) => {
121 for item in &sd.items {
122 if !seen.insert(Cow::Owned(format!("{}{}", sd.prefix, item.suffix))) {
123 return Err(MltError::DuplicatePropertyName(format!(
124 "{}{}",
125 sd.prefix, item.suffix
126 )));
127 }
128 }
129 }
130 _ => {
131 if !seen.insert(Cow::Borrowed(property.name())) {
132 return Err(MltError::DuplicatePropertyName(
133 property.name().to_string(),
134 ));
135 }
136 }
137 }
138 }
139 }
140 Ok(Self {
141 name,
142 extent,
143 id,
144 geometry,
145 properties,
146 })
147 }
148
149 #[must_use]
150 pub fn name(&self) -> &str {
151 &self.name
152 }
153
154 #[must_use]
155 pub fn extent(&self) -> Extent {
156 self.extent
157 }
158
159 #[must_use]
160 pub fn id(&self) -> &StagedId {
161 &self.id
162 }
163
164 #[must_use]
165 pub fn geometry(&self) -> &GeometryValues {
166 &self.geometry
167 }
168
169 #[must_use]
170 pub fn properties(&self) -> &[StagedProperty] {
171 &self.properties
172 }
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
177#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
178pub enum WireVersion {
179 #[default]
181 V01,
182 #[cfg(feature = "unstable-v2")]
188 V02,
189}
190
191impl WireVersion {
192 #[must_use]
194 pub(crate) fn tag(self) -> u8 {
195 match self {
196 Self::V01 => 1,
197 #[cfg(feature = "unstable-v2")]
198 Self::V02 => 2,
199 }
200 }
201
202 #[must_use]
204 pub(crate) fn rle_layout(self) -> RleLayout {
205 match self {
206 Self::V01 => RleLayout::Split,
207 #[cfg(feature = "unstable-v2")]
208 Self::V02 => RleLayout::Interleaved,
209 }
210 }
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Hash)]
215#[expect(
216 clippy::struct_excessive_bools,
217 reason = "enums would not model this better, not a state machine"
218)]
219pub struct EncoderConfig {
220 wire_version: WireVersion,
222 tessellate: bool,
224 attempt_spatial_morton_sort: bool,
226 attempt_spatial_hilbert_sort: bool,
228 attempt_id_sort: bool,
230 allow_fsst: bool,
232 allow_fastpfor: bool,
234 allow_shared_dict: bool,
236}
237impl Default for EncoderConfig {
238 fn default() -> Self {
239 Self {
240 wire_version: WireVersion::V01,
241 tessellate: false,
242 attempt_spatial_morton_sort: true,
243 attempt_spatial_hilbert_sort: true,
244 attempt_id_sort: true,
245 allow_fsst: true,
246 allow_fastpfor: true,
247 allow_shared_dict: true,
248 }
249 }
250}
251
252impl EncoderConfig {
253 #[must_use]
254 pub fn wire_version(self) -> WireVersion {
255 self.wire_version
256 }
257
258 #[must_use]
259 pub fn tessellate(self) -> bool {
260 self.tessellate
261 }
262
263 #[must_use]
264 pub fn attempt_spatial_morton_sort(self) -> bool {
265 self.attempt_spatial_morton_sort
266 }
267
268 #[must_use]
269 pub fn attempt_spatial_hilbert_sort(self) -> bool {
270 self.attempt_spatial_hilbert_sort
271 }
272
273 #[must_use]
274 pub fn attempt_id_sort(self) -> bool {
275 self.attempt_id_sort
276 }
277
278 #[must_use]
279 pub fn allow_fsst(self) -> bool {
280 self.allow_fsst
281 }
282
283 #[must_use]
284 pub fn allow_fastpfor(self) -> bool {
285 self.allow_fastpfor && self.wire_version == WireVersion::V01
289 }
290
291 #[must_use]
292 pub fn allow_shared_dict(self) -> bool {
293 self.allow_shared_dict
294 }
295
296 #[must_use]
297 pub fn with_wire_version(mut self, version: WireVersion) -> Self {
298 self.wire_version = version;
299 self
300 }
301
302 #[must_use]
303 pub fn with_tessellation(mut self, enabled: bool) -> Self {
304 self.tessellate = enabled;
305 self
306 }
307
308 #[must_use]
309 pub fn with_spatial_morton_sort(mut self, enabled: bool) -> Self {
310 self.attempt_spatial_morton_sort = enabled;
311 self
312 }
313
314 #[must_use]
315 pub fn with_spatial_hilbert_sort(mut self, enabled: bool) -> Self {
316 self.attempt_spatial_hilbert_sort = enabled;
317 self
318 }
319
320 #[must_use]
321 pub fn with_id_sort(mut self, enabled: bool) -> Self {
322 self.attempt_id_sort = enabled;
323 self
324 }
325
326 #[must_use]
327 pub fn with_fsst(mut self, enabled: bool) -> Self {
328 self.allow_fsst = enabled;
329 self
330 }
331
332 #[must_use]
333 pub fn with_fastpfor(mut self, enabled: bool) -> Self {
334 self.allow_fastpfor = enabled;
335 self
336 }
337
338 #[must_use]
339 pub fn with_shared_dict(mut self, enabled: bool) -> Self {
340 self.allow_shared_dict = enabled;
341 self
342 }
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq)]
354pub enum StrEncoding {
355 Plain,
356 Dict,
357 Fsst,
358 FsstDict,
359}
360
361#[derive(Debug, Clone, Copy, PartialEq)]
362pub enum ColumnKind {
363 Id,
364 Geometry,
365 Property,
366}
367
368#[derive(Clone, Copy, Debug, PartialEq)]
370pub struct StreamCtx<'a> {
371 pub kind: ColumnKind,
372 pub stream_type: StreamType,
373 pub name: &'a str,
374 pub subname: &'a str,
375}
376impl<'a> StreamCtx<'a> {
377 #[inline]
379 #[must_use]
380 pub const fn new(
381 kind: ColumnKind,
382 stream_type: StreamType,
383 name: &'a str,
384 subname: &'a str,
385 ) -> Self {
386 Self {
387 kind,
388 stream_type,
389 name,
390 subname,
391 }
392 }
393
394 #[inline]
395 #[must_use]
396 pub const fn id(stream_type: StreamType) -> Self {
397 Self::new(ColumnKind::Id, stream_type, "", "")
398 }
399
400 #[inline]
401 #[must_use]
402 pub const fn geom(stream_type: StreamType, name: &'a str) -> Self {
403 Self::new(ColumnKind::Geometry, stream_type, name, "")
404 }
405
406 #[inline]
407 #[must_use]
408 pub const fn prop(stream_type: StreamType, name: &'a str) -> Self {
409 Self::new(ColumnKind::Property, stream_type, name, "")
410 }
411
412 #[inline]
413 #[must_use]
414 pub const fn prop_data(name: &'a str) -> Self {
415 let stream_type = StreamType::Data(DictionaryType::None);
416 Self::new(ColumnKind::Property, stream_type, name, "")
417 }
418
419 #[inline]
420 #[must_use]
421 pub const fn prop2(stream_type: StreamType, prefix: &'a str, suffix: &'a str) -> Self {
422 Self::new(ColumnKind::Property, stream_type, prefix, suffix)
423 }
424}
425
426#[derive(Dbg)]
434pub struct ExplicitEncoder {
435 pub vertex_buffer_type: VertexBufferType,
437 #[dbg(skip)]
439 pub force_stream: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> bool>,
440 #[dbg(skip)]
442 pub get_int_encoder: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> IntEncoder>,
443 #[dbg(skip)]
445 pub get_str_encoding: Box<dyn Fn(&str) -> StrEncoding>,
446}