Skip to main content

mlt_core/
errors.rs

1use std::num::TryFromIntError;
2
3use num_enum::TryFromPrimitiveError;
4
5use crate::decoder::{
6    GeometryType, LogicalEncoding, LogicalTechnique, PhysicalEncoding, StreamType,
7};
8
9pub type MltResult<T> = Result<T, MltError>;
10pub(crate) type MltRefResult<'a, T> = Result<(&'a [u8], T), MltError>;
11
12#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum MltError {
15    #[error("cannot decode {0} as {1}")]
16    DataWidthMismatch(&'static str, &'static str),
17    #[error("dictionary index {0} out of bounds (len={1})")]
18    DictIndexOutOfBounds(u32, usize),
19    #[error("duplicate value found where unique required")]
20    DuplicateValue,
21    #[error("Integer overflow")]
22    IntegerOverflow,
23    #[error("missing geometry column in feature table")]
24    MissingGeometry,
25    #[error("missing layer name")]
26    MissingLayerName,
27    #[error("invalid extent: {0}")]
28    InvalidExtent(u32),
29    #[error("missing property name")]
30    MissingPropertyName,
31    #[error("duplicate property name: {0}")]
32    DuplicatePropertyName(String),
33    #[error("feature property count mismatch: expected {expected}, got {actual}")]
34    PropertyLengthMismatch { expected: usize, actual: usize },
35    #[error("property {index} kind mismatch: expected {expected:?}, got {actual:?}")]
36    PropertyKindMismatch {
37        index: usize,
38        expected: crate::tile::PropKind,
39        actual: crate::tile::PropKind,
40    },
41    #[error("staged column {column} feature count mismatch: expected {expected}, got {actual}")]
42    StagedFeatureCountMismatch {
43        column: String,
44        expected: usize,
45        actual: usize,
46    },
47    #[error("missing string stream: {0}")]
48    MissingStringStream(&'static str),
49    #[error("multiple geometry columns found (only one allowed)")]
50    MultipleGeometryColumns,
51    #[error("multiple ID columns found (only one allowed)")]
52    MultipleIdColumns,
53    #[error("varint uses more bytes than necessary (non-canonical encoding)")]
54    NonCanonicalVarInt,
55    #[error("{0} is not decoded")]
56    NotDecoded(&'static str),
57    #[error("decoded data is not in encoded form")]
58    NotEncoded,
59    #[error("error parsing column type: code={0}")]
60    ParsingColumnType(u8),
61    #[error("error parsing v2 stream encoding byte: 0x{0:02X}")]
62    ParsingEncodingByte(u8),
63    #[cfg(feature = "unstable-v2")]
64    #[error("error parsing v2 geometry layout: code={0}")]
65    ParsingGeoLayout(u8),
66    #[cfg(feature = "unstable-v2")]
67    #[error("error parsing v2 layer layout: byte=0x{0:02X}")]
68    ParsingLayerLayout(u8),
69    #[error("error parsing logical technique: code={0}")]
70    ParsingLogicalTechnique(u8),
71    #[error("error parsing physical encoding: code={0}")]
72    ParsingPhysicalEncoding(u8),
73    #[error("error parsing stream type: code={0}")]
74    ParsingStreamType(u8),
75    #[error("found {0} bytes after the expected end of layer")]
76    TrailingLayerData(usize),
77    #[error("unexpected end of input (unable to take {0} bytes)")]
78    UnableToTake(u32),
79    #[error("unexpected stream type {0:?}")]
80    UnexpectedStreamType(StreamType),
81    #[error("unexpected stream type {0:?}, expected {1} for {2}")]
82    UnexpectedStreamType2(StreamType, &'static str, &'static str),
83    #[error("unsupported logical encoding {0:?} for {1}")]
84    UnsupportedLogicalEncoding(LogicalEncoding, &'static str),
85    #[error("invalid combination of logical encodings: {0:?} + {1:?}")]
86    InvalidLogicalEncodings(LogicalTechnique, LogicalTechnique),
87    #[error("layer has zero size")]
88    ZeroLayerSize,
89    #[error("The encoder used to optimise data is incompatible")]
90    BadEncoderDataCombination,
91    #[error("StagedLayer::encode_explicit requires Encoder.explicit to be Some(_)")]
92    MissingExplicitEncoder,
93
94    // Wire/codec decoding (bytes -> primitives)
95    #[error("buffer underflow: needed {0} bytes, but only {1} remain")]
96    BufferUnderflow(u32, usize),
97    #[error("FastPFor decode failed: expected={0} got={1}")]
98    FastPforDecode(u32, usize),
99    #[error("invalid RLE run length (cannot convert to usize): value={0}")]
100    RleRunLenInvalid(i128),
101
102    // Structural constraints (lengths, counts, shapes)
103    #[error("geometry requires at least 1 stream, got 0")]
104    GeometryWithoutStreams,
105    #[error("FastPFor data byte length expected multiple of 4, got {0}")]
106    InvalidFastPforByteLength(usize),
107    #[error("vec2 delta stream size expected to be non-empty and multiple of 2, got {0}")]
108    InvalidPairStreamSize(usize),
109    #[error("decodable stream size expected {1}, got {0}")]
110    InvalidDecodingStreamSize(usize, usize),
111    #[error("IDs missing for encoding (expected Some IDs, got None)")]
112    IdsMissingForEncoding,
113    #[error("missing struct encoder for struct")]
114    MissingStructEncoderForStruct,
115    #[error("previous decode/parsing attempt failed")]
116    PriorParseFailure,
117    #[error("FSST-compressed data is malformed: {0}")]
118    MalformedFsst(&'static str),
119    #[error("presence stream has {0} bits set but {1} values provided")]
120    PresenceValueCountMismatch(usize, usize),
121    #[error("need to encode before being able to write")]
122    NeedsEncodingBeforeWriting,
123    #[error("memory limit exceeded: limit={limit}, used={used}, requested={requested}")]
124    MemoryLimitExceeded {
125        limit: u32,
126        used: u32,
127        requested: u32,
128    },
129    #[error("not implemented: {0}")]
130    NotImplemented(&'static str),
131    #[error("unsupported property value and encoder combination: {0:?} + {1:?}")]
132    UnsupportedPropertyEncoderCombination(&'static str, &'static str),
133    #[error("mixed property types are not allowed in column {0} ({1})")]
134    MixedPropertyTypes(usize, String),
135    #[error("shared dictionary requires at least 2 streams, got {0}")]
136    SharedDictRequiresStreams(usize),
137    #[error("unsupported string stream count (expected between 2 and 5): {0}")]
138    UnsupportedStringStreamCount(usize),
139    #[error("Structs are not allowed to be optional")]
140    TriedToEncodeOptionalStruct,
141    #[error(
142        "encoding instruction count mismatch: expected {input_len} instructions for {input_len} properties, got {config_len}"
143    )]
144    EncodingInstructionCountMismatch { input_len: usize, config_len: usize },
145    #[error("struct child data streams expected exactly 1 value, got {0}")]
146    UnexpectedStructChildCount(u32),
147    // Note that {expected}+1 is allowed for the legacy Java encoder bug
148    #[error("SharedDict stream count is {actual}, expected {expected}")]
149    InvalidSharedDictStreamCount { actual: u32, expected: u32 },
150    #[error("unsupported physical encoding: {0}")]
151    UnsupportedPhysicalEncoding(&'static str),
152    #[error("unsupported physical encoding: {0:?} for {1}")]
153    UnsupportedPhysicalEncodingForType(PhysicalEncoding, &'static str),
154    #[error(
155        "Extent {extent} cannot be encoded to morton due to morton allowing max. 16 bits, but {required_bits} would be required"
156    )]
157    VertexMortonNotCompatibleWithExtent { extent: u32, required_bits: u32 },
158    #[error("Morton stream uses {0} bits, but at most 16 bits are supported")]
159    InvalidMortonBits(u32),
160
161    // Geometry decode errors (field = variable name, geom_type for context)
162    #[error("MVT error: {0}")]
163    BadMvtGeometry(&'static str),
164    #[error("geometry[{0}]: index out of bounds")]
165    GeometryIndexOutOfBounds(usize),
166    #[error("geometry[{index}]: {field}[{idx}] out of bounds (len={len})")]
167    GeometryOutOfBounds {
168        index: usize,
169        field: &'static str,
170        idx: usize,
171        len: usize,
172    },
173    #[error("geometry[{index}]: vertex {vertex} out of bounds (count={count})")]
174    GeometryVertexOutOfBounds {
175        index: usize,
176        vertex: usize,
177        count: usize,
178    },
179    #[error("geometry[{0}]: {1} requires geometry_offsets")]
180    NoGeometryOffsets(usize, GeometryType),
181    #[error("geometry[{0}]: {1} requires part_offsets")]
182    NoPartOffsets(usize, GeometryType),
183    #[error("geometry[{0}]: {1} requires ring_offsets")]
184    NoRingOffsets(usize, GeometryType),
185    #[error("geometry[{0}]: unexpected offset combination for {1}")]
186    UnexpectedOffsetCombination(usize, GeometryType),
187
188    #[error("FastPFor error: {0}")]
189    FastPfor(#[from] fastpfor::FastPForError),
190    #[error(transparent)]
191    Io(#[from] std::io::Error),
192    #[error("Serde JSON error: {0}")]
193    SerdeJsonError(#[from] serde_json::Error),
194    #[error("integer conversion error: {0}")]
195    TryFromIntError(#[from] TryFromIntError),
196    #[error("num_enum conversion error: {0}")]
197    TryFromPrimitive(#[from] TryFromPrimitiveError<GeometryType>),
198    #[error("UTF-8 decode error: {0}")]
199    Utf8(#[from] std::str::Utf8Error),
200    #[error("UTF-8 decode error: {0}")]
201    FromUtf8(#[from] std::string::FromUtf8Error),
202    #[error("MVT error: {0}")]
203    Mvt(#[from] fast_mvt::MvtError),
204    #[error("MVT JSON value error: {0}")]
205    MvtJsonValue(#[from] fast_mvt::MvtJsonValueError),
206}
207
208impl From<MltError> for std::io::Error {
209    fn from(value: MltError) -> Self {
210        if let MltError::Io(e) = value {
211            e
212        } else {
213            Self::other(value)
214        }
215    }
216}
217
218pub(crate) trait AsMltError<T> {
219    fn or_overflow(&self) -> MltResult<T>;
220}
221
222impl<T: Copy> AsMltError<T> for Option<T> {
223    #[inline]
224    fn or_overflow(&self) -> MltResult<T> {
225        self.ok_or(MltError::IntegerOverflow)
226    }
227}
228
229impl AsMltError<u32> for Result<u32, TryFromIntError> {
230    #[inline]
231    fn or_overflow(&self) -> MltResult<u32> {
232        self.map_err(|_| MltError::IntegerOverflow)
233    }
234}
235
236#[inline]
237pub(crate) fn fail_if_invalid_stream_size(actual: usize, expected: usize) -> MltResult<()> {
238    if actual == expected {
239        Ok(())
240    } else {
241        Err(MltError::InvalidDecodingStreamSize(actual, expected))
242    }
243}