1use std::{collections::TryReserveError, fmt::Display};
4
5use zip::result::ZipError;
6
7use crate::{SubblockMode, validate};
8
9#[derive(Debug, thiserror::Error)]
13pub enum Limit {
14 #[error("uncompressed JSON too big")]
15 JsonBytes,
16 #[error("uncompressed array or image too big")]
17 ArrayBytes,
18 #[error("image width or height is too large")]
19 ImageDim,
20}
21
22fn format_corners<T: Display>(corners: &[T; 6]) -> String {
23 format!(
24 "[{}, {}, {}] to [{}, {}, {}]",
25 corners[0], corners[1], corners[2], corners[3], corners[4], corners[5],
26 )
27}
28
29#[derive(Debug, Clone, PartialEq, thiserror::Error)]
31pub enum InvalidData {
32 #[error("Error: array length {found} does not match the declared length {expected}")]
34 LengthMismatch { found: u64, expected: u64 },
35 #[error("size value {value} is zero or less")]
37 SizeZeroOrLess { value: f64 },
38 #[error("discrete colormap boundary decreases")]
40 BoundaryDecreases,
41 #[error("index value {value} exceeds the maximum index {maximum}")]
43 IndexOutOfRange { value: u64, maximum: u64 },
44 #[error("block index {value:?} exceeds the maximum index {maximum:?}")]
46 BlockIndexOutOfRange { value: [u32; 3], maximum: [u32; 3] },
47 #[error("sub-block {} has zero or negative size", format_corners(corners))]
49 RegularSubblockZeroSize { corners: [u32; 6] },
50 #[error(
52 "sub-block {} exceeds the maximum {maximum:?}",
53 format_corners(corners)
54 )]
55 RegularSubblockOutsideParent {
56 corners: [u32; 6],
57 maximum: [u32; 3],
58 },
59 #[error("sub-block {} is invalid for {mode:?} mode", format_corners(corners))]
61 RegularSubblockNotInMode {
62 corners: [u32; 6],
63 mode: SubblockMode,
64 },
65 #[error("sub-block {} has zero or negative size", format_corners(corners))]
67 FreeformSubblockZeroSize { corners: [f64; 6] },
68 #[error(
70 "sub-block {} is outside the valid range of 0.0 to 1.0",
71 format_corners(corners)
72 )]
73 FreeformSubblockOutsideParent { corners: [f64; 6] },
74}
75
76#[derive(Debug, thiserror::Error)]
78#[non_exhaustive]
79pub enum Error {
80 #[error("Memory allocation failed")]
82 OutOfMemory,
83 #[error("File IO error: {0}")]
85 IoError(std::io::Error),
86 #[error("Error: the zip comment does not identify this as an OMF file: '{0}'")]
88 NotOmf(String),
89 #[error(
91 "Version error: the file uses OMF v{0}.{1} but this version library can only read 0.9 and 2.0"
92 )]
93 NewerVersion(u32, u32),
94 #[error("Version error: the file uses pre-release OMF v{0}.{1}-{2} and can't be loaded")]
96 PreReleaseVersion(u32, u32, String),
97 #[error("JSON deserialization error: {0}")]
99 DeserializationFailed(#[from] serde_json::Error),
100 #[error("JSON serialization error: {0}")]
102 SerializationFailed(serde_json::Error),
103 #[error("Validation failed")]
105 ValidationFailed(#[from] validate::Problems),
106 #[error("Error: can't cast from {0} to {1} without losing data")]
108 UnsafeCast(&'static str, &'static str),
109 #[error("Error: image is not in PNG or JPEG encoding")]
111 NotImageData,
112 #[error("Error: image is not in Parquet encoding")]
114 NotParquetData,
115 #[error("Error: safety limit exceeded")]
117 LimitExceeded(Limit),
118 #[error("Data error: {0}")]
120 InvalidData(Box<InvalidData>),
121 #[error("Error: missing archive member '{0}'")]
123 ZipMemberMissing(String),
124 #[error("Zip error: {0}")]
126 ZipError(String),
127 #[cfg(feature = "parquet")]
129 #[error("{}", mismatch_string(.0, .1))]
130 ParquetSchemaMismatch(
131 std::sync::Arc<parquet::schema::types::Type>,
132 std::sync::Arc<Vec<parquet::schema::types::Type>>,
133 ),
134 #[cfg(feature = "parquet")]
136 #[error("{0}")]
137 ParquetError(Box<parquet::errors::ParquetError>),
138 #[cfg(feature = "image")]
140 #[error("Image processing error: {0}")]
141 ImageError(Box<image::ImageError>),
142 #[cfg(feature = "omf1")]
144 #[error("OMF1 conversion failed: {0}")]
145 Omf1Error(#[from] crate::omf1::Omf1Error),
146}
147
148impl From<InvalidData> for Error {
149 fn from(value: InvalidData) -> Self {
150 Self::InvalidData(value.into())
151 }
152}
153
154impl From<std::io::Error> for Error {
155 fn from(error: std::io::Error) -> Self {
156 match error.kind() {
157 std::io::ErrorKind::OutOfMemory => Error::OutOfMemory,
158 std::io::ErrorKind::Other if error.get_ref().is_some_and(|r| r.is::<Error>()) => {
159 *error.into_inner().unwrap().downcast().unwrap()
160 }
161 _ => Error::IoError(error),
162 }
163 }
164}
165
166impl From<TryReserveError> for Error {
167 fn from(_: TryReserveError) -> Self {
168 Error::OutOfMemory
169 }
170}
171
172impl From<ZipError> for Error {
173 fn from(value: ZipError) -> Self {
174 match value {
175 ZipError::Io(e) => Self::IoError(e),
176 other => Self::ZipError(other.to_string()),
177 }
178 }
179}
180
181#[cfg(feature = "image")]
182impl From<image::ImageError> for Error {
183 fn from(value: image::ImageError) -> Self {
184 use image::error::LimitErrorKind;
185 match &value {
186 image::ImageError::Limits(err @ image::error::LimitError { .. }) => match err.kind() {
187 LimitErrorKind::DimensionError => Error::LimitExceeded(Limit::ImageDim),
188 LimitErrorKind::InsufficientMemory => Error::LimitExceeded(Limit::ArrayBytes),
189 _ => Error::ImageError(value.into()),
190 },
191 _ => Error::ImageError(value.into()),
192 }
193 }
194}
195
196#[cfg(feature = "parquet")]
197impl From<parquet::errors::ParquetError> for Error {
198 fn from(value: parquet::errors::ParquetError) -> Self {
199 Self::ParquetError(value.into())
200 }
201}
202
203#[cfg(feature = "parquet")]
204fn mismatch_string(
205 found: &parquet::schema::types::Type,
206 expected: &[parquet::schema::types::Type],
207) -> String {
208 use parquet::schema::{printer::print_schema, types::Type};
209
210 fn schema_string(ty: &Type) -> String {
211 let mut buf = Vec::new();
212 print_schema(&mut buf, ty);
213 String::from_utf8_lossy(&buf).trim_end().to_owned()
214 }
215 let mut out = format!(
216 "Parquet schema mismatch, found:\n{found}\n\n{expected_label}:\n",
217 found = schema_string(found),
218 expected_label = if expected.len() == 1 {
219 "Expected"
220 } else {
221 "Expected one of"
222 }
223 );
224 for ty in expected {
225 out.push_str(&schema_string(ty));
226 out.push('\n');
227 }
228 out
229}