Skip to main content

mlt_core/
errors.rs

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