ipp_proto/
attribute.rs

1//!
2//! Attribute-related structs
3//!
4use std::{
5    collections::HashMap,
6    io::{self, Write},
7};
8
9use byteorder::{BigEndian, WriteBytesExt};
10
11use crate::{ipp::*, IppValue, IppWriter};
12
13pub const ATTRIBUTES_CHARSET: &str = "attributes-charset";
14pub const ATTRIBUTES_NATURAL_LANGUAGE: &str = "attributes-natural-language";
15pub const CHARSET_CONFIGURED: &str = "charset-configured";
16pub const CHARSET_SUPPORTED: &str = "charset-supported";
17pub const COMPRESSION_SUPPORTED: &str = "compression-supported";
18pub const DOCUMENT_FORMAT_DEFAULT: &str = "document-format-default";
19pub const DOCUMENT_FORMAT_SUPPORTED: &str = "document-format-supported";
20pub const GENERATED_NATURAL_LANGUAGE_SUPPORTED: &str = "generated-natural-language-supported";
21pub const IPP_VERSIONS_SUPPORTED: &str = "ipp-versions-supported";
22pub const NATURAL_LANGUAGE_CONFIGURED: &str = "natural-language-configured";
23pub const OPERATIONS_SUPPORTED: &str = "operations-supported";
24pub const PDL_OVERRIDE_SUPPORTED: &str = "pdl-override-supported";
25pub const PRINTER_IS_ACCEPTING_JOBS: &str = "printer-is-accepting-jobs";
26pub const PRINTER_MAKE_AND_MODEL: &str = "printer-make-and-model";
27pub const PRINTER_NAME: &str = "printer-name";
28pub const PRINTER_STATE: &str = "printer-state";
29pub const PRINTER_STATE_MESSAGE: &str = "printer-state-message";
30pub const PRINTER_STATE_REASONS: &str = "printer-state-reasons";
31pub const PRINTER_UP_TIME: &str = "printer-up-time";
32pub const PRINTER_URI: &str = "printer-uri";
33pub const PRINTER_URI_SUPPORTED: &str = "printer-uri-supported";
34pub const QUEUED_JOB_COUNT: &str = "queued-job-count";
35pub const URI_AUTHENTICATION_SUPPORTED: &str = "uri-authentication-supported";
36pub const URI_SECURITY_SUPPORTED: &str = "uri-security-supported";
37pub const JOB_ID: &str = "job-id";
38pub const JOB_NAME: &str = "job-name";
39pub const JOB_STATE: &str = "job-state";
40pub const JOB_STATE_REASONS: &str = "job-state-reasons";
41pub const JOB_URI: &str = "job-uri";
42pub const LAST_DOCUMENT: &str = "last-document";
43pub const REQUESTING_USER_NAME: &str = "requesting-user-name";
44pub const STATUS_MESSAGE: &str = "status-message";
45pub const REQUESTED_ATTRIBUTES: &str = "requested-attributes";
46pub const SIDES_SUPPORTED: &str = "sides-supported";
47pub const OUTPUT_MODE_SUPPORTED: &str = "output-mode-supported";
48pub const COLOR_SUPPORTED: &str = "color-supported";
49pub const PRINTER_INFO: &str = "printer-info";
50pub const PRINTER_LOCATION: &str = "printer-location";
51pub const PRINTER_MORE_INFO: &str = "printer-more-info";
52pub const PRINTER_RESOLUTION_DEFAULT: &str = "printer-resolution-default";
53pub const PRINTER_RESOLUTION_SUPPORTED: &str = "printer-resolution-supported";
54pub const COPIES_SUPPORTED: &str = "copies-supported";
55pub const COPIES_DEFAULT: &str = "copies-default";
56pub const SIDES_DEFAULT: &str = "sides-default";
57pub const PRINT_QUALITY_DEFAULT: &str = "print-quality-default";
58pub const PRINT_QUALITY_SUPPORTED: &str = "print-quality-supported";
59pub const FINISHINGS_DEFAULT: &str = "finishings-default";
60pub const FINISHINGS_SUPPORTED: &str = "finishings-supported";
61pub const OUTPUT_BIN_DEFAULT: &str = "output-bin-default";
62pub const OUTPUT_BIN_SUPPORTED: &str = "output-bin-supported";
63pub const ORIENTATION_REQUESTED_DEFAULT: &str = "orientation-requested-default";
64pub const ORIENTATION_REQUESTED_SUPPORTED: &str = "orientation-requested-supported";
65pub const MEDIA_DEFAULT: &str = "media-default";
66pub const MEDIA_SUPPORTED: &str = "media-supported";
67pub const PAGES_PER_MINUTE: &str = "pages-per-minute";
68pub const COLOR_MODE_SUPPORTED: &str = "color-mode-supported";
69pub const PRINT_COLOR_MODE_SUPPORTED: &str = "print-color-mode-supported";
70
71const HEADER_ATTRS: [&str; 3] = [ATTRIBUTES_CHARSET, ATTRIBUTES_NATURAL_LANGUAGE, PRINTER_URI];
72
73fn is_header_attr(attr: &str) -> bool {
74    HEADER_ATTRS.iter().any(|&at| at == attr)
75}
76
77/// `IppAttribute` represents an IPP attribute
78#[derive(Clone, Debug)]
79pub struct IppAttribute {
80    /// Attribute name
81    name: String,
82    /// Attribute value
83    value: IppValue,
84}
85
86impl IppAttribute {
87    /// Create new instance of the attribute
88    ///
89    /// * `name` - Attribute name<br/>
90    /// * `value` - Attribute value<br/>
91    pub fn new(name: &str, value: IppValue) -> IppAttribute {
92        IppAttribute {
93            name: name.to_string(),
94            value,
95        }
96    }
97
98    /// Return attribute name
99    pub fn name(&self) -> &str {
100        &self.name
101    }
102
103    /// Return attribute value
104    pub fn value(&self) -> &IppValue {
105        &self.value
106    }
107}
108
109impl IppWriter for IppAttribute {
110    /// Serialize attribute into binary stream
111    fn write(&self, writer: &mut Write) -> io::Result<usize> {
112        let mut retval = 0;
113
114        writer.write_u8(self.value.to_tag() as u8)?;
115        retval += 1;
116
117        writer.write_u16::<BigEndian>(self.name.len() as u16)?;
118        retval += 2;
119
120        writer.write_all(self.name.as_bytes())?;
121        retval += self.name.len();
122
123        retval += self.value.write(writer)?;
124
125        Ok(retval)
126    }
127}
128
129/// Attribute group
130#[derive(Clone, Debug)]
131pub struct IppAttributeGroup {
132    tag: DelimiterTag,
133    attributes: HashMap<String, IppAttribute>,
134}
135
136impl IppAttributeGroup {
137    /// Create new attribute group of a given type
138    pub fn new(tag: DelimiterTag) -> IppAttributeGroup {
139        IppAttributeGroup {
140            tag,
141            attributes: HashMap::new(),
142        }
143    }
144
145    /// Return group type tag
146    pub fn tag(&self) -> DelimiterTag {
147        self.tag
148    }
149
150    /// Return read-only attributes
151    pub fn attributes(&self) -> &HashMap<String, IppAttribute> {
152        &self.attributes
153    }
154
155    /// Return mutable attributes
156    pub fn attributes_mut(&mut self) -> &mut HashMap<String, IppAttribute> {
157        &mut self.attributes
158    }
159}
160
161/// Attribute list
162#[derive(Clone, Debug)]
163pub struct IppAttributes {
164    groups: Vec<IppAttributeGroup>,
165}
166
167impl IppAttributes {
168    /// Create attribute list
169    pub fn new() -> IppAttributes {
170        IppAttributes { groups: Vec::new() }
171    }
172
173    /// Get all groups
174    pub fn groups(&self) -> &Vec<IppAttributeGroup> {
175        &self.groups
176    }
177
178    /// Get all mutable groups
179    pub fn groups_mut(&mut self) -> &mut Vec<IppAttributeGroup> {
180        &mut self.groups
181    }
182
183    /// Get a list of attribute groups matching a given delimiter tag
184    pub fn groups_of(&self, tag: DelimiterTag) -> Vec<&IppAttributeGroup> {
185        self.groups.iter().filter(|g| g.tag == tag).collect()
186    }
187
188    /// Add attribute to a given group
189    pub fn add(&mut self, tag: DelimiterTag, attribute: IppAttribute) {
190        let mut group = self.groups_mut().iter_mut().find(|g| g.tag() == tag);
191        if let Some(ref mut group) = group {
192            group.attributes_mut().insert(attribute.name().to_owned(), attribute);
193        } else {
194            let mut new_group = IppAttributeGroup::new(tag);
195            new_group
196                .attributes_mut()
197                .insert(attribute.name().to_owned(), attribute);
198            self.groups_mut().push(new_group);
199        }
200    }
201}
202
203impl IppWriter for IppAttributes {
204    /// Serialize attribute list into binary stream
205    fn write(&self, writer: &mut Write) -> io::Result<usize> {
206        // first send the header attributes
207        writer.write_u8(DelimiterTag::OperationAttributes as u8)?;
208
209        let mut retval = 1;
210
211        if let Some(group) = self.groups_of(DelimiterTag::OperationAttributes).get(0) {
212            for hdr in &HEADER_ATTRS {
213                if let Some(attr) = group.attributes().get(*hdr) {
214                    retval += attr.write(writer)?
215                }
216            }
217        }
218
219        // now the rest
220        for hdr in &[
221            DelimiterTag::OperationAttributes,
222            DelimiterTag::JobAttributes,
223            DelimiterTag::PrinterAttributes,
224        ] {
225            if let Some(group) = self.groups_of(*hdr).get(0) {
226                if group.tag() != DelimiterTag::OperationAttributes {
227                    writer.write_u8(group.tag() as u8)?;
228                    retval += 1;
229                }
230                for (_, attr) in group
231                    .attributes()
232                    .iter()
233                    .filter(|&(_, v)| group.tag() != DelimiterTag::OperationAttributes || !is_header_attr(v.name()))
234                {
235                    retval += attr.write(writer)?;
236                }
237            }
238        }
239        writer.write_u8(DelimiterTag::EndOfAttributes as u8)?;
240        retval += 1;
241
242        Ok(retval)
243    }
244}