1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum CziError {
5 #[error("file error: {source}")]
6 File { source: FileError },
7
8 #[error("input error: {source}")]
9 Input { source: InputError },
10
11 #[error("internal error: {source}")]
12 Internal { source: InternalError },
13
14 #[error("unsupported: {source}")]
15 Unsupported { source: UnsupportedError },
16}
17
18#[derive(Error, Debug)]
19pub enum FileError {
20 #[error("IO error: {0}")]
21 Io(#[from] std::io::Error),
22
23 #[error("invalid CZI file: {context}")]
24 InvalidFormat { context: String },
25
26 #[error("invalid segment magic at offset {offset}: expected '{expected}', got '{actual}'")]
27 InvalidMagic {
28 offset: u64,
29 expected: String,
30 actual: String,
31 },
32
33 #[error("metadata XML is invalid UTF-8: {context}")]
34 InvalidUtf8 { context: String },
35
36 #[error("metadata parse error: {context}")]
37 MetadataParse { context: String },
38
39 #[error("decompression error: {context}")]
40 Decompression { context: String },
41}
42
43#[derive(Error, Debug)]
44pub enum InputError {
45 #[error("missing required dimension '{dimension}'")]
46 MissingDimension { dimension: String },
47
48 #[error("{field} index out of range: got {index}, max {max}")]
49 OutOfRange {
50 field: String,
51 index: usize,
52 max: usize,
53 },
54
55 #[error("invalid input for {field}: {detail}")]
56 InvalidArgument { field: String, detail: String },
57}
58
59#[derive(Error, Debug)]
60pub enum InternalError {
61 #[error("arithmetic overflow during {operation}")]
62 Overflow { operation: String },
63}
64
65#[derive(Error, Debug)]
66pub enum UnsupportedError {
67 #[error("unsupported subblock directory schema '{schema}'")]
68 DirectorySchema { schema: String },
69
70 #[error("unsupported subblock header schema '{schema}'")]
71 SubBlockSchema { schema: String },
72
73 #[error("unsupported compression mode '{mode}'")]
74 Compression { mode: String },
75
76 #[error("unsupported pixel type '{pixel_type}'")]
77 PixelType { pixel_type: String },
78}
79
80#[derive(Copy, Clone, Debug, PartialEq, Eq)]
81pub enum ErrorSource {
82 File,
83 Input,
84 Internal,
85 Unsupported,
86}
87
88impl CziError {
89 pub fn source(&self) -> ErrorSource {
90 match self {
91 Self::File { .. } => ErrorSource::File,
92 Self::Input { .. } => ErrorSource::Input,
93 Self::Internal { .. } => ErrorSource::Internal,
94 Self::Unsupported { .. } => ErrorSource::Unsupported,
95 }
96 }
97
98 pub fn file_invalid_format(context: impl Into<String>) -> Self {
99 Self::File {
100 source: FileError::InvalidFormat {
101 context: context.into(),
102 },
103 }
104 }
105
106 pub fn file_invalid_magic(
107 offset: u64,
108 expected: impl Into<String>,
109 actual: impl Into<String>,
110 ) -> Self {
111 Self::File {
112 source: FileError::InvalidMagic {
113 offset,
114 expected: expected.into(),
115 actual: actual.into(),
116 },
117 }
118 }
119
120 pub fn file_metadata(context: impl Into<String>) -> Self {
121 Self::File {
122 source: FileError::MetadataParse {
123 context: context.into(),
124 },
125 }
126 }
127
128 pub fn file_invalid_utf8(context: impl Into<String>) -> Self {
129 Self::File {
130 source: FileError::InvalidUtf8 {
131 context: context.into(),
132 },
133 }
134 }
135
136 pub fn file_decompression(context: impl Into<String>) -> Self {
137 Self::File {
138 source: FileError::Decompression {
139 context: context.into(),
140 },
141 }
142 }
143
144 pub fn input_out_of_range(field: impl Into<String>, index: usize, max: usize) -> Self {
145 Self::Input {
146 source: InputError::OutOfRange {
147 field: field.into(),
148 index,
149 max,
150 },
151 }
152 }
153
154 pub fn input_missing_dim(dimension: impl Into<String>) -> Self {
155 Self::Input {
156 source: InputError::MissingDimension {
157 dimension: dimension.into(),
158 },
159 }
160 }
161
162 pub fn input_argument(field: impl Into<String>, detail: impl Into<String>) -> Self {
163 Self::Input {
164 source: InputError::InvalidArgument {
165 field: field.into(),
166 detail: detail.into(),
167 },
168 }
169 }
170
171 pub fn internal_overflow(operation: impl Into<String>) -> Self {
172 Self::Internal {
173 source: InternalError::Overflow {
174 operation: operation.into(),
175 },
176 }
177 }
178
179 pub fn unsupported_directory_schema(schema: impl Into<String>) -> Self {
180 Self::Unsupported {
181 source: UnsupportedError::DirectorySchema {
182 schema: schema.into(),
183 },
184 }
185 }
186
187 pub fn unsupported_subblock_schema(schema: impl Into<String>) -> Self {
188 Self::Unsupported {
189 source: UnsupportedError::SubBlockSchema {
190 schema: schema.into(),
191 },
192 }
193 }
194
195 pub fn unsupported_compression(mode: impl Into<String>) -> Self {
196 Self::Unsupported {
197 source: UnsupportedError::Compression { mode: mode.into() },
198 }
199 }
200
201 pub fn unsupported_pixel_type(pixel_type: impl Into<String>) -> Self {
202 Self::Unsupported {
203 source: UnsupportedError::PixelType {
204 pixel_type: pixel_type.into(),
205 },
206 }
207 }
208}
209
210impl From<std::io::Error> for CziError {
211 fn from(value: std::io::Error) -> Self {
212 Self::File {
213 source: FileError::Io(value),
214 }
215 }
216}
217
218pub type Result<T> = std::result::Result<T, CziError>;