Skip to main content

winprint_ext/ticket/document/
print_schema.rs

1use std::fmt;
2use xml::name::OwnedName;
3
4#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
5/// Represents a Print Schema document.
6pub enum PrintSchemaDocument {
7    /// Documents that typed as [`PrintCapabilitiesDocument`].
8    PrintCapabilities(PrintCapabilitiesDocument),
9    /// Documents that typed as [`PrintTicketDocument`].
10    PrintTicket(PrintTicketDocument),
11}
12
13#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
14/// Represents a PrintCapabilities document.
15pub struct PrintCapabilitiesDocument {
16    /// Properties of the document
17    pub properties: Vec<Property>,
18    /// Parameter definitions
19    pub parameter_defs: Vec<ParameterDef>,
20    /// Features
21    pub features: Vec<PrintFeature>,
22}
23
24#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
25/// Represents a PrintTicket document.
26pub struct PrintTicketDocument {
27    /// Properties of the document
28    pub properties: Vec<Property>,
29    /// Parameter initializations
30    pub parameter_inits: Vec<ParameterInit>,
31    /// Features
32    pub features: Vec<PrintFeature>,
33}
34
35#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
36/// Represents a Print Feature.
37pub struct PrintFeature {
38    /// The name of the feature.
39    #[fmt("{}", self.name)]
40    pub name: OwnedName,
41    /// Properties of the feature
42    pub properties: Vec<Property>,
43    /// Available options
44    pub options: Vec<PrintFeatureOption>,
45    /// Sub-features of the feature
46    pub features: Vec<PrintFeature>,
47}
48
49#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
50/// Represents a parameter initialization used in a [`PrintTicketDocument`].
51pub struct ParameterInit {
52    /// The name of the parameter.
53    #[fmt("{}", self.name)]
54    pub name: OwnedName,
55    /// The value of the parameter.
56    #[fmt("{:?}", self.value)]
57    pub value: PropertyValue,
58}
59
60#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
61/// Represents a parameter definition used in a [`PrintCapabilitiesDocument`].
62pub struct ParameterDef {
63    /// The name of the parameter.
64    #[fmt("{}", self.name)]
65    pub name: OwnedName,
66    /// Properties of the parameter
67    pub properties: Vec<Property>,
68}
69
70#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
71/// Represents a possible option for a [`PrintFeature`].
72pub struct PrintFeatureOption {
73    /// The name of the option.
74    #[fmt("{}", self.name.as_ref().map(|x| x.to_string()).unwrap_or("<unnamed>".to_string()))]
75    pub name: Option<OwnedName>,
76    /// Scored-properties of the option
77    pub scored_properties: Vec<ScoredProperty>,
78    /// Properties of the option
79    pub properties: Vec<Property>,
80}
81
82#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
83/// Represents a scored-property.
84/// A [`ScoredProperty`] declares a property that is intrinsic to an [Option](PrintFeatureOption).
85/// Such properties should be compared when evaluating how closely a requested Option matches a device-supported Option.
86pub struct ScoredProperty {
87    /// The name of the scored-property.
88    #[fmt("{}", self.name.as_ref().map(|x| x.to_string()).unwrap_or("<unnamed>".to_string()))]
89    pub name: Option<OwnedName>,
90    /// The parameter that this scored-property depends on.
91    #[fmt("{}", self.parameter_ref.as_ref().map(|x| x.to_string()).unwrap_or("<unnamed>".to_string()))]
92    pub parameter_ref: Option<OwnedName>,
93    /// The value of the scored-property.
94    #[fmt("{}", self.value.as_ref().map(|x| format!("{:?}", x)).unwrap_or("<none>".to_string()))]
95    pub value: Option<PropertyValue>,
96    /// Sub-scored-properties of the scored-property
97    pub scored_properties: Vec<ScoredProperty>,
98    /// Properties of the scored-property
99    pub properties: Vec<Property>,
100}
101
102#[derive(Clone, PartialEq, Eq, Hash, fmt_derive::Debug)]
103/// Represents a property.
104pub struct Property {
105    /// The name of the property.
106    #[fmt("{}", self.name)]
107    pub name: OwnedName,
108    /// The value of the property.
109    #[fmt("{}", self.value.as_ref().map(|x| format!("{:?}", x)).unwrap_or("<none>".to_string()))]
110    pub value: Option<PropertyValue>,
111    /// Sub-properties of the property
112    pub properties: Vec<Property>,
113}
114
115#[derive(Clone, PartialEq, Eq, Hash)]
116/// Represents a property value or a scored-property value.
117pub enum PropertyValue {
118    /// A string value.
119    String(String),
120    /// An integer value.
121    Integer(i32),
122    /// A qualified name value.
123    QName(OwnedName),
124    /// An unknown-typed value.
125    Unknown(OwnedName, String),
126}
127
128impl fmt::Debug for PropertyValue {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        match self {
131            PropertyValue::String(s) => write!(f, "String({:?})", s),
132            PropertyValue::Integer(i) => write!(f, "Integer({})", i),
133            PropertyValue::QName(q) => write!(f, "QName({})", q),
134            PropertyValue::Unknown(n, s) => write!(f, "Unknown({}, {:?})", n, s),
135        }
136    }
137}
138
139impl ParameterDef {
140    /// Get the default value of this parameter.
141    pub fn default_value(&self) -> Option<&PropertyValue> {
142        self.properties
143            .iter()
144            .find(|x| {
145                x.name.local_name == "DefaultValue" && x.name.namespace_ref() == Some(super::NS_PSF)
146            })
147            .and_then(|x| x.value.as_ref())
148    }
149}
150
151impl PropertyValue {
152    /// Get the `xsi:type` of this value.
153    pub fn xsi_type(&self) -> OwnedName {
154        match self {
155            PropertyValue::String(_) => OwnedName::qualified("string", super::NS_XSD, Some("xsd")),
156            PropertyValue::Integer(_) => {
157                OwnedName::qualified("integer", super::NS_XSD, Some("xsd"))
158            }
159            PropertyValue::QName(_) => OwnedName::qualified("QName", super::NS_XSD, Some("xsd")),
160            PropertyValue::Unknown(n, _) => n.clone(),
161        }
162    }
163    /// Try as [`PropertyValue::String`] value.
164    pub fn string(&self) -> Option<&str> {
165        match self {
166            PropertyValue::String(s) => Some(s),
167            _ => None,
168        }
169    }
170    /// Try as [`PropertyValue::Integer`] value.
171    pub fn integer(&self) -> Option<i32> {
172        match self {
173            PropertyValue::Integer(i) => Some(*i),
174            _ => None,
175        }
176    }
177    /// Try as [`PropertyValue::QName`] value.
178    pub fn qualified_name(&self) -> Option<&OwnedName> {
179        match self {
180            PropertyValue::QName(q) => Some(q),
181            _ => None,
182        }
183    }
184}
185
186impl PrintFeatureOption {
187    /// Collect all parameters that this option depends on.
188    pub fn parameters_dependent(&self) -> Vec<OwnedName> {
189        let mut result = vec![];
190        for scored_property in &self.scored_properties {
191            result.extend(scored_property.parameters_dependent());
192        }
193        result
194    }
195}
196
197impl ScoredProperty {
198    /// Collect all parameters that this scored-property depends on.
199    pub fn parameters_dependent(&self) -> Vec<OwnedName> {
200        let mut result = vec![];
201        if let Some(ref parameter_ref) = self.parameter_ref {
202            result.push(parameter_ref.clone());
203        }
204        for scored_property in &self.scored_properties {
205            result.extend(scored_property.parameters_dependent());
206        }
207        result
208    }
209
210    /// Get the value of this scored-property, or the value of the parameter it references.
211    pub fn value_with<'a>(&'a self, parameters: &'a [ParameterInit]) -> Option<&'a PropertyValue> {
212        if let Some(ref parameter_ref) = self.parameter_ref {
213            parameters
214                .iter()
215                .find(|x| x.name == *parameter_ref)
216                .map(|x| &x.value)
217        } else {
218            self.value.as_ref()
219        }
220    }
221}
222
223impl From<PrintTicketDocument> for PrintSchemaDocument {
224    fn from(value: PrintTicketDocument) -> Self {
225        PrintSchemaDocument::PrintTicket(value)
226    }
227}
228
229impl From<PrintCapabilitiesDocument> for PrintSchemaDocument {
230    fn from(value: PrintCapabilitiesDocument) -> Self {
231        PrintSchemaDocument::PrintCapabilities(value)
232    }
233}
234
235/// A trait for types that have scored-properties.
236pub trait WithScoredProperties {
237    /// Get the scored properties.
238    fn scored_properties(&self) -> &[ScoredProperty];
239
240    /// Get the scored-property with the given name and namespace.
241    fn get_scored_property(&self, name: &str, namespace: Option<&str>) -> Option<&ScoredProperty> {
242        self.scored_properties().iter().find(|x| {
243            x.name.as_ref().map_or(false, |x| {
244                x.local_name == name && x.namespace_ref() == namespace
245            })
246        })
247    }
248}
249
250impl WithScoredProperties for PrintFeatureOption {
251    fn scored_properties(&self) -> &[ScoredProperty] {
252        &self.scored_properties
253    }
254}
255
256impl WithScoredProperties for ScoredProperty {
257    fn scored_properties(&self) -> &[ScoredProperty] {
258        &self.scored_properties
259    }
260}
261
262/// A trait for types that have properties.
263pub trait WithProperties {
264    /// Get the properties.
265    fn properties(&self) -> &[Property];
266
267    /// Get the property with the given name and namespace.
268    fn get_property(&self, name: &str, namespace: Option<&str>) -> Option<&Property> {
269        self.properties()
270            .iter()
271            .find(|x| x.name.local_name == name && x.name.namespace_ref() == namespace)
272    }
273}
274
275impl WithProperties for PrintSchemaDocument {
276    fn properties(&self) -> &[Property] {
277        match self {
278            PrintSchemaDocument::PrintCapabilities(x) => x.properties(),
279            PrintSchemaDocument::PrintTicket(x) => x.properties(),
280        }
281    }
282}
283
284impl WithProperties for PrintCapabilitiesDocument {
285    fn properties(&self) -> &[Property] {
286        &self.properties
287    }
288}
289
290impl WithProperties for PrintTicketDocument {
291    fn properties(&self) -> &[Property] {
292        &self.properties
293    }
294}
295
296impl WithProperties for ParameterDef {
297    fn properties(&self) -> &[Property] {
298        &self.properties
299    }
300}
301
302impl WithProperties for PrintFeature {
303    fn properties(&self) -> &[Property] {
304        &self.properties
305    }
306}
307
308impl WithProperties for PrintFeatureOption {
309    fn properties(&self) -> &[Property] {
310        &self.properties
311    }
312}
313
314impl WithProperties for ScoredProperty {
315    fn properties(&self) -> &[Property] {
316        &self.properties
317    }
318}
319
320impl WithProperties for Property {
321    fn properties(&self) -> &[Property] {
322        &self.properties
323    }
324}
325
326#[cfg(test)]
327mod tests {
328    use super::{
329        PrintCapabilitiesDocument, PrintSchemaDocument, PrintTicketDocument, Property,
330        PropertyValue, WithProperties,
331    };
332    use xml::name::OwnedName;
333
334    fn new_test_properties() -> Vec<Property> {
335        vec![
336            Property {
337                name: OwnedName::local("Property1"),
338                value: Some(PropertyValue::String("Value1".to_string())),
339                properties: vec![],
340            },
341            Property {
342                name: OwnedName::qualified("Property2", "http://test.namespace/", Some("test")),
343                value: Some(PropertyValue::Integer(2)),
344                properties: vec![],
345            },
346        ]
347    }
348
349    fn check_test_properties(w: &impl WithProperties) {
350        assert_eq!(w.properties().len(), 2);
351
352        // ensure we can get properties
353        assert_eq!(
354            w.get_property("Property1", None)
355                .and_then(|p| p.value.as_ref())
356                .and_then(|v| v.string()),
357            Some("Value1")
358        );
359        assert_eq!(
360            w.get_property("Property2", Some("http://test.namespace/"))
361                .and_then(|p| p.value.as_ref())
362                .and_then(|v| v.integer()),
363            Some(2)
364        );
365
366        // namespace is handled
367        assert!(w
368            .get_property("Property1", Some("http://wrong.namespace/"))
369            .is_none());
370        assert!(w
371            .get_property("Property2", Some("http://wrong.namespace/"))
372            .is_none());
373        assert!(w.get_property("Property2", None).is_none());
374
375        // ensure we can't get properties that don't exist
376        assert!(w.get_property("PROPERTY_NOT_EXIST", None).is_none());
377    }
378
379    #[test]
380    fn get_properties_from_ticket() {
381        let document1: PrintSchemaDocument = PrintTicketDocument {
382            properties: new_test_properties(),
383            parameter_inits: vec![],
384            features: vec![],
385        }
386        .into();
387        check_test_properties(&document1);
388    }
389
390    #[test]
391    fn get_properties_from_capabilities() {
392        let document1: PrintSchemaDocument = PrintCapabilitiesDocument {
393            properties: new_test_properties(),
394            parameter_defs: vec![],
395            features: vec![],
396        }
397        .into();
398        check_test_properties(&document1);
399    }
400
401    #[test]
402    fn get_properties_from_parameter_def() {
403        let parameter_def = super::ParameterDef {
404            name: OwnedName::local("Test"),
405            properties: new_test_properties(),
406        };
407        check_test_properties(&parameter_def);
408    }
409
410    #[test]
411    fn get_properties_from_option() {
412        let option = super::PrintFeatureOption {
413            name: None,
414            scored_properties: vec![],
415            properties: new_test_properties(),
416        };
417        check_test_properties(&option);
418    }
419
420    #[test]
421    fn get_properties_from_feature() {
422        let feature = super::PrintFeature {
423            name: OwnedName::local("Test"),
424            properties: new_test_properties(),
425            options: vec![],
426            features: vec![],
427        };
428        check_test_properties(&feature);
429    }
430
431    #[test]
432    fn get_properties_from_scored_property() {
433        let scored_property = super::ScoredProperty {
434            name: None,
435            parameter_ref: None,
436            value: None,
437            scored_properties: vec![],
438            properties: new_test_properties(),
439        };
440        check_test_properties(&scored_property);
441    }
442
443    #[test]
444    fn get_properties_from_property() {
445        let property = Property {
446            name: OwnedName::local("Test"),
447            value: None,
448            properties: new_test_properties(),
449        };
450        check_test_properties(&property);
451    }
452}