zenpixels_convert/
error.rs1use crate::{PixelDescriptor, TransferFunction};
4use core::fmt;
5
6#[derive(Debug, Clone, PartialEq)]
19#[non_exhaustive]
20pub enum ConvertError {
21 NoMatch { source: PixelDescriptor },
23 NoPath {
25 from: PixelDescriptor,
26 to: PixelDescriptor,
27 },
28 BufferSize { expected: usize, actual: usize },
30 InvalidWidth(u32),
32 EmptyFormatList,
34 UnsupportedTransfer {
36 from: TransferFunction,
37 to: TransferFunction,
38 },
39 AlphaNotOpaque,
41 DepthReductionForbidden,
43 AlphaRemovalForbidden,
45 RgbToGray,
47 AllocationFailed,
49 Buffer(zenpixels::BufferError),
53 CmsError(alloc::string::String),
55 }
59
60impl fmt::Display for ConvertError {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 match self {
63 Self::NoMatch { source } => {
64 write!(
65 f,
66 "no supported format matches source {:?}/{:?}",
67 source.channel_type(),
68 source.layout()
69 )
70 }
71 Self::NoPath { from, to } => {
72 write!(
73 f,
74 "no conversion path from {:?}/{:?} to {:?}/{:?}",
75 from.channel_type(),
76 from.layout(),
77 to.channel_type(),
78 to.layout()
79 )?;
80 if from.signal_range != to.signal_range {
83 write!(
84 f,
85 " (signal range {} -> {}: no narrow<->full conversion kernels exist; \
86 relabeling without rescaling would corrupt pixel values)",
87 from.signal_range, to.signal_range
88 )?;
89 }
90 Ok(())
91 }
92 Self::BufferSize { expected, actual } => {
93 write!(
94 f,
95 "buffer size mismatch: expected {expected} bytes, got {actual}"
96 )
97 }
98 Self::InvalidWidth(w) => write!(f, "invalid width: {w}"),
99 Self::EmptyFormatList => write!(f, "supported format list is empty"),
100 Self::UnsupportedTransfer { from, to } => {
101 write!(f, "unsupported transfer conversion: {from:?} → {to:?}")
102 }
103 Self::AlphaNotOpaque => write!(f, "alpha channel is not fully opaque"),
104 Self::DepthReductionForbidden => write!(f, "depth reduction forbidden by policy"),
105 Self::AlphaRemovalForbidden => write!(f, "alpha removal forbidden by policy"),
106 Self::RgbToGray => {
107 write!(f, "RGB-to-grayscale requires explicit luma coefficients")
108 }
109 Self::AllocationFailed => write!(f, "buffer allocation failed"),
110 Self::Buffer(e) => write!(f, "buffer construction failed: {e}"),
111 Self::CmsError(msg) => write!(f, "CMS transform failed: {msg}"),
112 }
113 }
114}
115
116impl From<zenpixels::BufferError> for ConvertError {
117 fn from(err: zenpixels::BufferError) -> Self {
121 Self::Buffer(err)
122 }
123}
124
125#[cfg(feature = "std")]
126impl std::error::Error for ConvertError {}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use alloc::format;
132
133 #[test]
134 fn display_no_match() {
135 let e = ConvertError::NoMatch {
136 source: PixelDescriptor::RGB8_SRGB,
137 };
138 let s = format!("{e}");
139 assert!(s.contains("no supported format"));
140 assert!(s.contains("U8"));
141 assert!(s.contains("Rgb"));
142 }
143
144 #[test]
145 fn display_no_path() {
146 let e = ConvertError::NoPath {
147 from: PixelDescriptor::RGB8_SRGB,
148 to: PixelDescriptor::GRAY8_SRGB,
149 };
150 let s = format!("{e}");
151 assert!(s.contains("no conversion path"));
152 }
153
154 #[test]
155 fn display_buffer_size() {
156 let e = ConvertError::BufferSize {
157 expected: 1024,
158 actual: 512,
159 };
160 let s = format!("{e}");
161 assert!(s.contains("1024"));
162 assert!(s.contains("512"));
163 }
164
165 #[test]
166 fn display_invalid_width() {
167 let e = ConvertError::InvalidWidth(0);
168 assert!(format!("{e}").contains("0"));
169 }
170
171 #[test]
172 fn display_empty_format_list() {
173 let s = format!("{}", ConvertError::EmptyFormatList);
174 assert!(s.contains("empty"));
175 }
176
177 #[test]
178 fn display_unsupported_transfer() {
179 let e = ConvertError::UnsupportedTransfer {
180 from: TransferFunction::Pq,
181 to: TransferFunction::Hlg,
182 };
183 let s = format!("{e}");
184 assert!(s.contains("Pq"));
185 assert!(s.contains("Hlg"));
186 }
187
188 #[test]
189 fn display_alpha_not_opaque() {
190 assert!(format!("{}", ConvertError::AlphaNotOpaque).contains("opaque"));
191 }
192
193 #[test]
194 fn display_depth_reduction_forbidden() {
195 assert!(format!("{}", ConvertError::DepthReductionForbidden).contains("forbidden"));
196 }
197
198 #[test]
199 fn display_alpha_removal_forbidden() {
200 assert!(format!("{}", ConvertError::AlphaRemovalForbidden).contains("forbidden"));
201 }
202
203 #[test]
204 fn display_rgb_to_gray() {
205 assert!(format!("{}", ConvertError::RgbToGray).contains("luma"));
206 }
207
208 #[test]
209 fn display_allocation_failed() {
210 assert!(format!("{}", ConvertError::AllocationFailed).contains("allocation"));
211 }
212
213 #[test]
214 fn display_cms_error() {
215 let e = ConvertError::CmsError(alloc::string::String::from("profile mismatch"));
216 let s = format!("{e}");
217 assert!(s.contains("CMS transform failed"));
218 assert!(s.contains("profile mismatch"));
219 }
220
221 #[test]
222 fn error_eq() {
223 assert_eq!(ConvertError::AlphaNotOpaque, ConvertError::AlphaNotOpaque);
224 assert_ne!(ConvertError::AlphaNotOpaque, ConvertError::RgbToGray);
225 }
226
227 #[test]
228 fn error_debug() {
229 let e = ConvertError::AllocationFailed;
230 let s = format!("{e:?}");
231 assert!(s.contains("AllocationFailed"));
232 }
233
234 #[test]
235 fn error_clone() {
236 let e = ConvertError::BufferSize {
237 expected: 100,
238 actual: 50,
239 };
240 let e2 = e.clone();
241 assert_eq!(e, e2);
242 }
243}