1use 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#[derive(Clone, Debug)]
79pub struct IppAttribute {
80 name: String,
82 value: IppValue,
84}
85
86impl IppAttribute {
87 pub fn new(name: &str, value: IppValue) -> IppAttribute {
92 IppAttribute {
93 name: name.to_string(),
94 value,
95 }
96 }
97
98 pub fn name(&self) -> &str {
100 &self.name
101 }
102
103 pub fn value(&self) -> &IppValue {
105 &self.value
106 }
107}
108
109impl IppWriter for IppAttribute {
110 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#[derive(Clone, Debug)]
131pub struct IppAttributeGroup {
132 tag: DelimiterTag,
133 attributes: HashMap<String, IppAttribute>,
134}
135
136impl IppAttributeGroup {
137 pub fn new(tag: DelimiterTag) -> IppAttributeGroup {
139 IppAttributeGroup {
140 tag,
141 attributes: HashMap::new(),
142 }
143 }
144
145 pub fn tag(&self) -> DelimiterTag {
147 self.tag
148 }
149
150 pub fn attributes(&self) -> &HashMap<String, IppAttribute> {
152 &self.attributes
153 }
154
155 pub fn attributes_mut(&mut self) -> &mut HashMap<String, IppAttribute> {
157 &mut self.attributes
158 }
159}
160
161#[derive(Clone, Debug)]
163pub struct IppAttributes {
164 groups: Vec<IppAttributeGroup>,
165}
166
167impl IppAttributes {
168 pub fn new() -> IppAttributes {
170 IppAttributes { groups: Vec::new() }
171 }
172
173 pub fn groups(&self) -> &Vec<IppAttributeGroup> {
175 &self.groups
176 }
177
178 pub fn groups_mut(&mut self) -> &mut Vec<IppAttributeGroup> {
180 &mut self.groups
181 }
182
183 pub fn groups_of(&self, tag: DelimiterTag) -> Vec<&IppAttributeGroup> {
185 self.groups.iter().filter(|g| g.tag == tag).collect()
186 }
187
188 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 fn write(&self, writer: &mut Write) -> io::Result<usize> {
206 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 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}