Skip to main content

czi_rs/
error.rs

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>;