1use serde::{de::Visitor, Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4#[serde(rename_all = "PascalCase")]
5pub struct ScannerCapabilities {
6 pub version: String,
7 pub make_and_model: String,
8 #[serde(default, skip_serializing_if = "Option::is_none")]
9 pub manufacturer: Option<String>,
10 pub serial_number: String,
11 #[serde(rename = "UUID")]
12 pub uuid: String,
13 #[serde(rename = "AdminURI")]
14 pub admin_uri: String,
15 #[serde(rename = "IconURI")]
16 pub icon_uri: String,
17 #[serde(default, skip_serializing_if = "Certifications::is_empty")]
18 pub certifications: Certifications,
19 pub platen: Platen,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub adf: Option<Adf>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub compression_factor_support: Option<CompressionFactorSupport>,
24 #[serde(default, skip_serializing_if = "SupportedMediaTypes::is_empty")]
25 pub supported_media_types: SupportedMediaTypes,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub sharpen_support: Option<SharpenSupport>,
28}
29
30#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
31#[serde(rename_all = "PascalCase")]
32pub struct Certifications {
33 pub certification: Vec<Certification>,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "PascalCase")]
38pub struct Certification {
39 pub name: String,
40 pub version: String,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "PascalCase")]
45pub struct Platen {
46 pub platen_input_caps: InputCaps,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(rename_all = "PascalCase")]
51pub struct Adf {
52 pub adf_simplex_input_caps: InputCaps,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
56#[serde(rename_all = "PascalCase")]
57pub struct InputCaps {
58 pub min_width: u32,
59 pub max_width: u32,
60 pub min_height: u32,
61 pub max_height: u32,
62 pub max_scan_regions: u32,
63 pub setting_profiles: SettingProfiles,
64 pub supported_intents: SupportedIntents,
65 pub max_optical_x_resolution: u32,
66 pub max_optical_y_resolution: u32,
67 pub risky_left_margin: u32,
68 pub risky_right_margin: u32,
69 pub risky_top_margin: u32,
70 pub risky_bottom_margin: u32,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "PascalCase")]
75pub struct SettingProfiles {
76 pub setting_profile: SettingProfile,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
80#[serde(rename_all = "PascalCase")]
81pub struct SettingProfile {
82 pub color_modes: ColorModes,
83 #[serde(default, skip_serializing_if = "ContentTypes::is_empty")]
84 pub content_types: ContentTypes,
85 pub document_formats: DocumentFormats,
86 pub supported_resolutions: SupportedResolutions,
87 pub color_spaces: ColorSpaces,
88 #[serde(default, skip_serializing_if = "Option::is_none")]
89 pub ccd_channels: Option<CcdChannels>,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93#[serde(rename_all = "PascalCase")]
94pub struct ColorModes {
95 pub color_mode: Vec<ColorMode>,
96}
97
98#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
99#[serde(rename_all = "PascalCase")]
100pub struct ContentTypes {
101 pub content_type: Vec<ContentType>,
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105#[serde(rename_all = "PascalCase")]
106pub struct DocumentFormats {
107 pub document_format: Vec<String>,
108 pub document_format_ext: Vec<String>,
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
112#[serde(rename_all = "PascalCase")]
113pub struct SupportedResolutions {
114 pub discrete_resolutions: DiscreteResolutions,
115}
116
117#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
118#[serde(rename_all = "PascalCase")]
119pub struct DiscreteResolutions {
120 pub discrete_resolution: Vec<DiscreteResolution>,
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
124#[serde(rename_all = "PascalCase")]
125pub struct DiscreteResolution {
126 pub x_resolution: u32,
127 pub y_resolution: u32,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
131#[serde(rename_all = "PascalCase")]
132pub struct ColorSpaces {
133 pub color_space: Vec<String>,
134}
135
136#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
137#[serde(rename_all = "PascalCase")]
138pub struct CcdChannels {
139 pub ccd_channel: Vec<CcdChannel>,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
143#[serde(rename_all = "PascalCase")]
144pub struct SupportedIntents {
145 pub intent: Vec<ScanIntent>,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
149#[serde(rename_all = "PascalCase")]
150pub struct CompressionFactorSupport {
151 pub min: u32,
152 pub max: u32,
153 pub normal: u32,
154 pub step: u32,
155}
156
157#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
158#[serde(rename_all = "PascalCase")]
159pub struct SupportedMediaTypes {
160 pub media_type: Vec<String>,
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
164#[serde(rename_all = "PascalCase")]
165pub struct SharpenSupport {
166 pub min: u32,
167 pub max: u32,
168 pub normal: u32,
169 pub step: u32,
170}
171
172#[derive(Debug, Clone, PartialEq, Eq)]
173pub enum ColorMode {
174 BlackAndWhite1,
179 Grayscale8,
181 Grayscale16,
183 RGB24,
185 RGB48,
187}
188
189#[derive(Debug, Clone, PartialEq, Eq)]
190pub enum ContentType {
191 Photo,
192 Text,
193 TextAndPhoto,
194 LineArt,
195 Magazine,
196 Halftone,
197 Auto,
198 Custom(String),
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub enum CcdChannel {
203 Red,
205 Green,
207 Blue,
209 NTSC,
211 GrayCcd,
213 GrayCcdEmulated,
216}
217
218#[derive(Debug, Clone, PartialEq, Eq)]
219pub enum ScanIntent {
220 Document,
222 TextAndGraphic,
224 Photo,
226 Preview,
228 Object,
230 BusinessCard,
232 Custom(String),
233}
234
235struct ColorModeVisitor;
236struct ContentTypeVisitor;
237struct CcdChannelVisitor;
238struct ScanIntentVisitor;
239
240impl Certifications {
241 fn is_empty(&self) -> bool {
242 self.certification.is_empty()
243 }
244}
245
246impl ContentTypes {
247 fn is_empty(&self) -> bool {
248 self.content_type.is_empty()
249 }
250}
251
252impl SupportedMediaTypes {
253 fn is_empty(&self) -> bool {
254 self.media_type.is_empty()
255 }
256}
257
258impl ColorModes {
259 pub fn color(&self) -> Option<ColorMode> {
262 if self.color_mode.contains(&ColorMode::RGB48) {
263 Some(ColorMode::RGB48)
264 } else if self.color_mode.contains(&ColorMode::RGB24) {
265 Some(ColorMode::RGB24)
266 } else {
267 None
268 }
269 }
270}
271
272impl Serialize for ColorMode {
273 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
274 where
275 S: serde::Serializer,
276 {
277 serializer.serialize_str(match self {
278 Self::BlackAndWhite1 => "BlackAndWhite1",
279 Self::Grayscale8 => "Grayscale8",
280 Self::Grayscale16 => "Grayscale16",
281 Self::RGB24 => "RGB24",
282 Self::RGB48 => "RGB48",
283 })
284 }
285}
286
287impl<'de> Deserialize<'de> for ColorMode {
288 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
289 where
290 D: serde::Deserializer<'de>,
291 {
292 deserializer.deserialize_str(ColorModeVisitor)
293 }
294}
295
296impl<'de> Visitor<'de> for ColorModeVisitor {
297 type Value = ColorMode;
298
299 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
300 write!(formatter, "string")
301 }
302
303 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
304 where
305 E: serde::de::Error,
306 {
307 Ok(match v {
308 "BlackAndWhite1" => ColorMode::BlackAndWhite1,
309 "Grayscale8" => ColorMode::Grayscale8,
310 "Grayscale16" => ColorMode::Grayscale16,
311 "RGB24" => ColorMode::RGB24,
312 "RGB48" => ColorMode::RGB48,
313 _ => {
314 return Err(serde::de::Error::invalid_value(
315 serde::de::Unexpected::Str(v),
316 &"valid ColorMode value",
317 ))
318 }
319 })
320 }
321}
322
323impl Serialize for ContentType {
324 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
325 where
326 S: serde::Serializer,
327 {
328 serializer.serialize_str(match self {
329 Self::Photo => "Photo",
330 Self::Text => "Text",
331 Self::TextAndPhoto => "TextAndPhoto",
332 Self::LineArt => "LineArt",
333 Self::Magazine => "Magazine",
334 Self::Halftone => "Halftone",
335 Self::Auto => "Auto",
336 Self::Custom(custom) => custom,
337 })
338 }
339}
340
341impl<'de> Deserialize<'de> for ContentType {
342 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
343 where
344 D: serde::Deserializer<'de>,
345 {
346 deserializer.deserialize_str(ContentTypeVisitor)
347 }
348}
349
350impl<'de> Visitor<'de> for ContentTypeVisitor {
351 type Value = ContentType;
352
353 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
354 write!(formatter, "string")
355 }
356
357 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
358 where
359 E: serde::de::Error,
360 {
361 Ok(match v {
362 "Photo" => ContentType::Photo,
363 "Text" => ContentType::Text,
364 "TextAndPhoto" => ContentType::TextAndPhoto,
365 "LineArt" => ContentType::LineArt,
366 "Magazine" => ContentType::Magazine,
367 "Halftone" => ContentType::Halftone,
368 "Auto" => ContentType::Auto,
369 custom => ContentType::Custom(custom.to_owned()),
370 })
371 }
372}
373
374impl Serialize for CcdChannel {
375 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
376 where
377 S: serde::Serializer,
378 {
379 serializer.serialize_str(match self {
380 CcdChannel::Red => "Red",
381 CcdChannel::Green => "Green",
382 CcdChannel::Blue => "Blue",
383 CcdChannel::NTSC => "NTSC",
384 CcdChannel::GrayCcd => "GrayCcd",
385 CcdChannel::GrayCcdEmulated => "GrayCcdEmulated",
386 })
387 }
388}
389
390impl<'de> Deserialize<'de> for CcdChannel {
391 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
392 where
393 D: serde::Deserializer<'de>,
394 {
395 deserializer.deserialize_str(CcdChannelVisitor)
396 }
397}
398
399impl<'de> Visitor<'de> for CcdChannelVisitor {
400 type Value = CcdChannel;
401
402 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
403 write!(formatter, "string")
404 }
405
406 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
407 where
408 E: serde::de::Error,
409 {
410 Ok(match v {
411 "Red" => CcdChannel::Red,
412 "Green" => CcdChannel::Green,
413 "Blue" => CcdChannel::Blue,
414 "NTSC" => CcdChannel::NTSC,
415 "GrayCcd" => CcdChannel::GrayCcd,
416 "GrayCcdEmulated" => CcdChannel::GrayCcdEmulated,
417 _ => {
418 return Err(serde::de::Error::invalid_value(
419 serde::de::Unexpected::Str(v),
420 &"valid ColorMode value",
421 ))
422 }
423 })
424 }
425}
426
427impl Serialize for ScanIntent {
428 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
429 where
430 S: serde::Serializer,
431 {
432 serializer.serialize_str(match self {
433 Self::Document => "Document",
434 Self::TextAndGraphic => "TextAndGraphic",
435 Self::Photo => "Photo",
436 Self::Preview => "Preview",
437 Self::Object => "Object",
438 Self::BusinessCard => "BusinessCard",
439 Self::Custom(custom) => custom,
440 })
441 }
442}
443
444impl<'de> Deserialize<'de> for ScanIntent {
445 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
446 where
447 D: serde::Deserializer<'de>,
448 {
449 deserializer.deserialize_str(ScanIntentVisitor)
450 }
451}
452
453impl<'de> Visitor<'de> for ScanIntentVisitor {
454 type Value = ScanIntent;
455
456 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
457 write!(formatter, "string")
458 }
459
460 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
461 where
462 E: serde::de::Error,
463 {
464 Ok(match v {
465 "Document" => ScanIntent::Document,
466 "TextAndGraphic" => ScanIntent::TextAndGraphic,
467 "Photo" => ScanIntent::Photo,
468 "Preview" => ScanIntent::Preview,
469 "Object" => ScanIntent::Object,
470 "BusinessCard" => ScanIntent::BusinessCard,
471 custom => ScanIntent::Custom(custom.to_owned()),
472 })
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use super::*;
479
480 #[test]
481 pub fn test_capabilities_deser() {
482 for raw_xml in [
483 include_str!("../test-data/capabilities/brother_mfc_j497dw.xml"),
484 include_str!("../test-data/capabilities/canon_ts5300_series.xml"),
485 include_str!("../test-data/capabilities/canon_ts7450.xml"),
486 ]
487 .into_iter()
488 {
489 serde_xml_rs::from_str::<ScannerCapabilities>(raw_xml)
490 .expect("capabilities deserializing failure");
491 }
492 }
493}