Skip to main content

mlt_core/
errors.rs

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