winprint_ext/ticket/document/
reader.rs1use super::{
2 ParameterDef, ParameterInit, PrintCapabilitiesDocument, PrintFeature, PrintFeatureOption,
3 PrintSchemaDocument, PrintTicketDocument, Property, PropertyValue, ScoredProperty, NS_PSF,
4 NS_XSD, NS_XSI,
5};
6use std::{fmt::Debug, io::Cursor};
7use thiserror::Error;
8use xml::{
9 common::{Position, TextPosition},
10 name::OwnedName,
11 namespace::Namespace,
12 reader::XmlEvent,
13 EventReader,
14};
15
16#[derive(Error, Debug)]
17pub enum ParsePrintSchemaError {
19 #[error("Invalid xml")]
21 InvalidXml(#[source] xml::reader::Error),
22 #[error("Invalid print schema: (at {pos}) {reason}")]
24 InvalidPrintSchema {
25 pos: TextPosition,
27 reason: String,
29 },
30 #[error("Wrong document type: expected {expected} but found {found}")]
32 WrongDocumentType {
33 expected: &'static str,
35 found: &'static str,
37 },
38}
39
40pub trait ParsableXmlDocument: Sized {
42 type Error;
44
45 fn parse<R>(reader: &mut EventReader<R>) -> Result<Self, Self::Error>
47 where
48 R: std::io::Read;
49
50 fn parse_from_bytes(xml: impl AsRef<[u8]>) -> Result<Self, Self::Error> {
52 let mut reader = EventReader::new(Cursor::new(xml));
53 Self::parse(&mut reader)
54 }
55}
56
57fn parse_qname(namespace: &Namespace, value: &str) -> OwnedName {
58 let prefix_index = value.find(':');
59 if let Some(prefix_index) = prefix_index {
60 let (prefix, local_name) = value.split_at(prefix_index);
61 let local_name = &local_name[1..];
62 OwnedName {
63 local_name: local_name.to_string(),
64 namespace: namespace.get(prefix).map(str::to_string),
65 prefix: Some(prefix.to_string()),
66 }
67 } else {
68 OwnedName {
69 local_name: value.to_string(),
70 namespace: None,
71 prefix: None,
72 }
73 }
74}
75
76fn parse_name_attribute(
77 attributes: &[xml::attribute::OwnedAttribute],
78 namespace: &Namespace,
79) -> Option<OwnedName> {
80 let attribute_value = attributes
81 .iter()
82 .find(|x| x.name.local_name == "name" && x.name.namespace.is_none())
83 .map(|x| x.value.clone());
84 attribute_value.map(|x| parse_qname(namespace, &x))
85}
86
87fn parse_type_attribute(
88 attributes: &[xml::attribute::OwnedAttribute],
89 namespace: &Namespace,
90) -> Option<OwnedName> {
91 let attribute_value = attributes
92 .iter()
93 .find(|x| x.name.local_name == "type" && x.name.namespace_ref() == Some(NS_XSI))
94 .map(|x| x.value.clone());
95 attribute_value.map(|x| parse_qname(namespace, &x))
96}
97
98struct PsfValueContext {
99 pos: TextPosition,
100 value: String,
101 value_type: OwnedName,
102 namespace: Namespace,
103}
104
105impl PsfValueContext {
106 fn parse(self) -> Result<PropertyValue, ParsePrintSchemaError> {
107 if self.value_type.namespace_ref() == Some(NS_XSD) {
108 match self.value_type.local_name.as_str() {
109 "string" => return Ok(PropertyValue::String(self.value)),
110 "integer" => {
111 return self.value.trim().parse().map(PropertyValue::Integer).map_err(|_| {
112 ParsePrintSchemaError::InvalidPrintSchema {
113 pos: self.pos,
114 reason: "Invalid integer".to_string(),
115 }
116 })
117 }
118 "QName" => {
119 return Ok(PropertyValue::QName(parse_qname(
120 &self.namespace,
121 &self.value,
122 )))
123 }
124 _ => {}
125 };
126 }
127 Ok(PropertyValue::Unknown(self.value_type, self.value))
128 }
129}
130
131impl ParsableXmlDocument for PrintSchemaDocument {
132 type Error = ParsePrintSchemaError;
133 fn parse<R>(reader: &mut EventReader<R>) -> Result<Self, Self::Error>
134 where
135 R: std::io::Read,
136 {
137 let mut depth: usize = 0;
138
139 let mut option_name: Option<OwnedName> = None;
140 let mut parameter_ref: Option<OwnedName> = None;
141
142 let mut parameter_def_name: Option<OwnedName> = None;
143 let mut parameter_def_container: Option<Vec<ParameterDef>> = None;
144
145 let mut parameter_init_name: Option<OwnedName> = None;
146 let mut parameter_init_container: Option<Vec<ParameterInit>> = None;
147
148 let mut feature_name: Vec<OwnedName> = Vec::new();
149 let mut feature_containers: Vec<Vec<PrintFeature>> = Vec::new();
150
151 let mut option_containers: Vec<Vec<PrintFeatureOption>> = Vec::new();
152
153 let mut property_name: Vec<OwnedName> = Vec::new();
154 let mut property_containers: Vec<Vec<Property>> = Vec::new();
155
156 let mut scored_property_name: Vec<Option<OwnedName>> = Vec::new();
157 let mut scored_property_containers: Vec<Vec<ScoredProperty>> = Vec::new();
158
159 let mut value_context: Option<PsfValueContext> = None;
160 let mut parsed_value: Option<PropertyValue> = None;
161
162 loop {
163 let e = match reader.next() {
164 Ok(e) => e,
165 Err(e) => return Err(ParsePrintSchemaError::InvalidXml(e)),
166 };
167 match e {
168 XmlEvent::StartElement {
169 name,
170 attributes,
171 namespace,
172 } => {
173 depth += 1;
174
175 if name.namespace_ref() == Some(NS_PSF) {
176 match name.local_name.as_str() {
177 "PrintCapabilities" => {
178 if depth > 1 {
179 return Err(ParsePrintSchemaError::InvalidPrintSchema {
180 pos: reader.position(),
181 reason: "PrintCapabilities should be root element"
182 .to_string(),
183 });
184 }
185 feature_containers.push(Vec::new());
187 property_containers.push(Vec::new());
188 parameter_def_container.replace(Vec::new());
189 }
190 "PrintTicket" => {
191 if depth > 1 {
192 return Err(ParsePrintSchemaError::InvalidPrintSchema {
193 pos: reader.position(),
194 reason: "PrintTicket should be root element".to_string(),
195 });
196 }
197 feature_containers.push(Vec::new());
199 property_containers.push(Vec::new());
200 parameter_init_container.replace(Vec::new());
201 }
202 "ParameterDef" => {
203 parameter_def_name = parse_name_attribute(&attributes, &namespace);
204 property_containers.push(Vec::new());
205 }
206 "ParameterInit" => {
207 parameter_init_name = parse_name_attribute(&attributes, &namespace);
208 }
209 "Feature" => {
210 feature_name.push(
211 parse_name_attribute(&attributes, &namespace).ok_or_else(
212 || ParsePrintSchemaError::InvalidPrintSchema {
213 pos: reader.position(),
214 reason: "Feature name not found".to_string(),
215 },
216 )?,
217 );
218
219 feature_containers.push(Vec::new());
221 property_containers.push(Vec::new());
222 option_containers.push(Vec::new());
223 }
224 "Option" => {
225 option_name = parse_name_attribute(&attributes, &namespace);
226 property_containers.push(Vec::new());
227 scored_property_containers.push(Vec::new());
228 }
229 "ParameterRef" => {
230 parameter_ref = parse_name_attribute(&attributes, &namespace);
231 }
232 "ScoredProperty" => {
233 scored_property_name
234 .push(parse_name_attribute(&attributes, &namespace));
235
236 property_containers.push(Vec::new());
238 scored_property_containers.push(Vec::new());
239
240 parsed_value.take();
242 parameter_ref.take();
243 }
244 "Property" => {
245 property_name.push(
246 parse_name_attribute(&attributes, &namespace).ok_or_else(
247 || ParsePrintSchemaError::InvalidPrintSchema {
248 pos: reader.position(),
249 reason: "Property name not found".to_string(),
250 },
251 )?,
252 );
253
254 property_containers.push(Vec::new());
256
257 parsed_value.take();
259 }
260 "Value" => {
261 if let Some(value_type) =
262 parse_type_attribute(&attributes, &namespace)
263 {
264 value_context.replace(PsfValueContext {
265 pos: reader.position(),
266 value: String::new(),
267 value_type,
268 namespace,
269 });
270 }
271 }
272 _ => {
273 return Err(ParsePrintSchemaError::InvalidPrintSchema {
274 pos: reader.position(),
275 reason: format!("Invalid element: {}", name),
276 })
277 }
278 }
279 }
280 }
281 XmlEvent::EndElement { name } => {
282 depth -= 1;
283
284 if name.namespace_ref() == Some(NS_PSF) {
285 match name.local_name.as_str() {
286 "PrintCapabilities" => {
287 return Ok(PrintCapabilitiesDocument {
288 parameter_defs: parameter_def_container.unwrap(),
289 features: feature_containers.pop().unwrap(),
290 properties: property_containers.pop().unwrap(),
291 }
292 .into());
293 }
294 "PrintTicket" => {
295 return Ok(PrintTicketDocument {
296 parameter_inits: parameter_init_container.unwrap(),
297 features: feature_containers.pop().unwrap(),
298 properties: property_containers.pop().unwrap(),
299 }
300 .into());
301 }
302 "ParameterDef" => {
303 let parameter_def_name = parameter_def_name.take().unwrap();
305 let properties = property_containers.pop().unwrap();
306
307 let parent = parameter_def_container.as_mut().ok_or_else(|| {
308 ParsePrintSchemaError::InvalidPrintSchema {
309 pos: reader.position(),
310 reason: "ParameterDef cannot be here".to_string(),
311 }
312 })?;
313 parent.push(ParameterDef {
314 name: parameter_def_name,
315 properties,
316 });
317 }
318 "ParameterInit" => {
319 let parameter_init_name = parameter_init_name.take().unwrap();
321
322 let value = parsed_value.take().ok_or_else(|| {
325 ParsePrintSchemaError::InvalidPrintSchema {
326 pos: reader.position(),
327 reason: "ParameterInit value not found".to_string(),
328 }
329 })?;
330
331 let parent =
332 parameter_init_container.as_mut().ok_or_else(|| {
333 ParsePrintSchemaError::InvalidPrintSchema {
334 pos: reader.position(),
335 reason: "ParameterInit cannot be here".to_string(),
336 }
337 })?;
338 parent.push(ParameterInit {
339 name: parameter_init_name,
340 value,
341 });
342 }
343 "Feature" => {
344 let frature_name = feature_name.pop().unwrap();
346 let features = feature_containers.pop().unwrap();
347 let properties = property_containers.pop().unwrap();
348 let options = option_containers.pop().unwrap();
349
350 let parent = feature_containers.last_mut().ok_or_else(|| {
351 ParsePrintSchemaError::InvalidPrintSchema {
352 pos: reader.position(),
353 reason: "Feature cannot be here".to_string(),
354 }
355 })?;
356 parent.push(PrintFeature {
357 name: frature_name,
358 properties,
359 options,
360 features,
361 });
362 }
363 "Option" => {
364 let option_name = option_name.take();
366 let properties = property_containers.pop().unwrap();
367 let scored_properties = scored_property_containers.pop().unwrap();
368
369 let parent = option_containers.last_mut().ok_or_else(|| {
370 ParsePrintSchemaError::InvalidPrintSchema {
371 pos: reader.position(),
372 reason: "Option cannot be here".to_string(),
373 }
374 })?;
375 parent.push(PrintFeatureOption {
376 name: option_name,
377 scored_properties,
378 properties,
379 });
380 }
381 "ScoredProperty" => {
382 let scored_property_name = scored_property_name.pop().unwrap();
384 let properties = property_containers.pop().unwrap();
385 let scored_properties = scored_property_containers.pop().unwrap();
386
387 let parent =
388 scored_property_containers.last_mut().ok_or_else(|| {
389 ParsePrintSchemaError::InvalidPrintSchema {
390 pos: reader.position(),
391 reason: "ScoredProperty cannot be here".to_string(),
392 }
393 })?;
394 parent.push(ScoredProperty {
395 name: scored_property_name,
396 parameter_ref: parameter_ref.take(),
397 value: parsed_value.take(),
398 properties,
399 scored_properties,
400 });
401 }
402 "Property" => {
403 let property_name = property_name.pop().unwrap();
405 let properties = property_containers.pop().unwrap();
406
407 let parent = property_containers.last_mut().ok_or_else(|| {
408 ParsePrintSchemaError::InvalidPrintSchema {
409 pos: reader.position(),
410 reason: "Property cannot be here".to_string(),
411 }
412 })?;
413 parent.push(Property {
414 name: property_name,
415 value: parsed_value.take(),
416 properties,
417 });
418 }
419 "Value" => {
420 if let Some(value_context) = value_context.take() {
421 parsed_value.replace(value_context.parse()?);
422 }
423 }
424 _ => {}
425 }
426 }
427 }
428 XmlEvent::Characters(s) => {
429 if let Some(c) = value_context.as_mut() {
430 c.value.push_str(&s);
431 }
432 }
433 XmlEvent::Whitespace(_) => {
434 if let Some(c) = value_context.as_mut() {
435 c.value.push(' ');
436 }
437 }
438 XmlEvent::CData(s) => {
439 if let Some(c) = value_context.as_mut() {
440 c.value.push_str(&s);
441 }
442 }
443 XmlEvent::EndDocument => break,
444 _ => {}
445 }
446 }
447
448 Err(ParsePrintSchemaError::InvalidPrintSchema {
449 pos: reader.position(),
450 reason: "No valid root element found".to_string(),
451 })
452 }
453}
454
455impl ParsableXmlDocument for PrintCapabilitiesDocument {
456 type Error = ParsePrintSchemaError;
457 fn parse<R>(reader: &mut EventReader<R>) -> Result<Self, Self::Error>
458 where
459 R: std::io::Read,
460 {
461 PrintSchemaDocument::parse(reader).and_then(|x| match x {
462 PrintSchemaDocument::PrintCapabilities(document) => Ok(document),
463 PrintSchemaDocument::PrintTicket(_) => Err(ParsePrintSchemaError::WrongDocumentType {
464 expected: "PrintCapabilities",
465 found: "PrintTicket",
466 }),
467 })
468 }
469}
470
471impl ParsableXmlDocument for PrintTicketDocument {
472 type Error = ParsePrintSchemaError;
473 fn parse<R>(reader: &mut EventReader<R>) -> Result<Self, Self::Error>
474 where
475 R: std::io::Read,
476 {
477 PrintSchemaDocument::parse(reader).and_then(|x| match x {
478 PrintSchemaDocument::PrintTicket(document) => Ok(document),
479 PrintSchemaDocument::PrintCapabilities(_) => {
480 Err(ParsePrintSchemaError::WrongDocumentType {
481 expected: "PrintTicket",
482 found: "PrintCapabilities",
483 })
484 }
485 })
486 }
487}
488
489#[cfg(test)]
490mod tests {
491 use super::{ParsableXmlDocument, ParsePrintSchemaError};
492 use crate::ticket::document::{
493 PrintCapabilitiesDocument, PrintSchemaDocument, PrintTicketDocument,
494 };
495
496 #[test]
497 fn wrong_type_should_return_error() {
498 let xml = include_bytes!("../../../test_data/print_ticket.xml");
499 let result = PrintCapabilitiesDocument::parse_from_bytes(xml);
500 assert!(matches!(
501 result,
502 Err(ParsePrintSchemaError::WrongDocumentType { .. })
503 ));
504
505 let xml = include_bytes!("../../../test_data/print_capabilities.xml");
506 let result = PrintTicketDocument::parse_from_bytes(xml);
507 assert!(matches!(
508 result,
509 Err(ParsePrintSchemaError::WrongDocumentType { .. })
510 ));
511 }
512
513 #[test]
514 fn parameter_def_should_not_in_print_ticket() {
515 let xml = r#"<psf:PrintTicket version="1"
516 xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
517 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
518 xmlns:psk="http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords">
519 <psf:ParameterDef name="psk:JobCopiesAllDocuments">
520 <psf:Property name="psf:DataType">
521 <psf:Value xsi:type="xsd:QName">xsd:integer</psf:Value>
522 </psf:Property>
523 <psf:Property name="psf:UnitType">
524 <psf:Value xsi:type="xsd:string">copies</psf:Value>
525 </psf:Property>
526 <psf:Property name="psf:Multiple">
527 <psf:Value xsi:type="xsd:integer">1</psf:Value>
528 </psf:Property>
529 <psf:Property name="psf:MaxValue">
530 <psf:Value xsi:type="xsd:integer">9999</psf:Value>
531 </psf:Property>
532 <psf:Property name="psf:MinValue">
533 <psf:Value xsi:type="xsd:integer">1</psf:Value>
534 </psf:Property>
535 <psf:Property name="psf:DefaultValue">
536 <psf:Value xsi:type="xsd:integer">1</psf:Value>
537 </psf:Property>
538 <psf:Property name="psf:Mandatory">
539 <psf:Value xsi:type="xsd:QName">psk:Unconditional</psf:Value>
540 </psf:Property>
541 <psf:Property name="psk:DisplayName">
542 <psf:Value xsi:type="xsd:string">份数</psf:Value>
543 </psf:Property>
544 </psf:ParameterDef>
545</psf:PrintTicket>"#;
546 let result = PrintSchemaDocument::parse_from_bytes(xml);
547 assert!(matches!(
548 result,
549 Err(ParsePrintSchemaError::InvalidPrintSchema { .. })
550 ));
551 }
552
553 #[test]
554 fn parameter_init_should_not_in_print_capabilities() {
555 let xml = r#"<psf:PrintCapabilities version="1"
556 xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
557 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
558 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
559 xmlns:psk="http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords">
560 <psf:ParameterInit name="psk:PageMediaSizeMediaSizeWidth">
561 <psf:Value xsi:type="xsd:integer">2540</psf:Value>
562 </psf:ParameterInit>
563</psf:PrintCapabilities>"#;
564 let result = PrintSchemaDocument::parse_from_bytes(xml);
565 assert!(matches!(
566 result,
567 Err(ParsePrintSchemaError::InvalidPrintSchema { .. })
568 ));
569 }
570
571 #[test]
572 fn parse_print_ticket() {
573 let xml = include_bytes!("../../../test_data/print_ticket.xml");
574 let _document = PrintTicketDocument::parse_from_bytes(xml).unwrap();
575 }
576
577 #[test]
578 fn parse_print_capabilities() {
579 let xml = include_bytes!("../../../test_data/print_capabilities.xml");
580 let _document = PrintCapabilitiesDocument::parse_from_bytes(xml).unwrap();
581 }
582}