1use std::{
5 fmt,
6 io::{self, Read, Write},
7};
8
9use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
10use bytes::Bytes;
11use enum_as_inner::EnumAsInner;
12use num_traits::FromPrimitive;
13
14use crate::{ipp::ValueTag, IppReadExt, IppWriter};
15
16#[derive(Clone, Debug, PartialEq, EnumAsInner)]
18pub enum IppValue {
19 Integer(i32),
20 Enum(i32),
21 OctetString(String),
22 TextWithoutLanguage(String),
23 NameWithoutLanguage(String),
24 Charset(String),
25 NaturalLanguage(String),
26 Uri(String),
27 RangeOfInteger {
28 min: i32,
29 max: i32,
30 },
31 Boolean(bool),
32 Keyword(String),
33 ListOf(Vec<IppValue>),
34 Collection(Vec<IppValue>),
35 MimeMediaType(String),
36 DateTime {
37 year: u16,
38 month: u8,
39 day: u8,
40 hour: u8,
41 minutes: u8,
42 seconds: u8,
43 deciseconds: u8,
44 utcdir: char,
45 utchours: u8,
46 utcmins: u8,
47 },
48 MemberAttrName(String),
49 Resolution {
50 crossfeed: i32,
51 feed: i32,
52 units: i8,
53 },
54 Other {
55 tag: u8,
56 data: Bytes,
57 },
58}
59
60impl IppValue {
61 pub fn to_tag(&self) -> ValueTag {
63 match *self {
64 IppValue::Integer(_) => ValueTag::Integer,
65 IppValue::Enum(_) => ValueTag::Enum,
66 IppValue::RangeOfInteger { .. } => ValueTag::RangeOfInteger,
67 IppValue::Boolean(_) => ValueTag::Boolean,
68 IppValue::Keyword(_) => ValueTag::Keyword,
69 IppValue::OctetString(_) => ValueTag::OctetStringUnspecified,
70 IppValue::TextWithoutLanguage(_) => ValueTag::TextWithoutLanguage,
71 IppValue::NameWithoutLanguage(_) => ValueTag::NameWithoutLanguage,
72 IppValue::Charset(_) => ValueTag::Charset,
73 IppValue::NaturalLanguage(_) => ValueTag::NaturalLanguage,
74 IppValue::Uri(_) => ValueTag::Uri,
75 IppValue::MimeMediaType(_) => ValueTag::MimeMediaType,
76 IppValue::ListOf(ref list) => list[0].to_tag(),
77 IppValue::Collection(_) => ValueTag::BegCollection,
78 IppValue::DateTime { .. } => ValueTag::DateTime,
79 IppValue::MemberAttrName(_) => ValueTag::MemberAttrName,
80 IppValue::Resolution { .. } => ValueTag::Resolution,
81 IppValue::Other { .. } => ValueTag::Unknown,
82 }
83 }
84
85 pub fn read(vtag: u8, reader: &mut Read) -> io::Result<IppValue> {
87 let vsize = reader.read_u16::<BigEndian>()?;
88
89 let ipptag = match ValueTag::from_u8(vtag) {
90 Some(x) => x,
91 None => {
92 return Ok(IppValue::Other {
93 tag: vtag,
94 data: reader.read_bytes(vsize as usize)?,
95 });
96 }
97 };
98
99 match ipptag {
100 ValueTag::Integer => {
101 debug_assert_eq!(vsize, 4);
102 Ok(IppValue::Integer(reader.read_i32::<BigEndian>()?))
103 }
104 ValueTag::Enum => {
105 debug_assert_eq!(vsize, 4);
106 Ok(IppValue::Enum(reader.read_i32::<BigEndian>()?))
107 }
108 ValueTag::OctetStringUnspecified => Ok(IppValue::OctetString(reader.read_string(vsize as usize)?)),
109 ValueTag::TextWithoutLanguage => Ok(IppValue::TextWithoutLanguage(reader.read_string(vsize as usize)?)),
110 ValueTag::NameWithoutLanguage => Ok(IppValue::NameWithoutLanguage(reader.read_string(vsize as usize)?)),
111 ValueTag::Charset => Ok(IppValue::Charset(reader.read_string(vsize as usize)?)),
112 ValueTag::NaturalLanguage => Ok(IppValue::NaturalLanguage(reader.read_string(vsize as usize)?)),
113 ValueTag::Uri => Ok(IppValue::Uri(reader.read_string(vsize as usize)?)),
114 ValueTag::RangeOfInteger => {
115 debug_assert_eq!(vsize, 8);
116 Ok(IppValue::RangeOfInteger {
117 min: reader.read_i32::<BigEndian>()?,
118 max: reader.read_i32::<BigEndian>()?,
119 })
120 }
121 ValueTag::Boolean => {
122 debug_assert_eq!(vsize, 1);
123 Ok(IppValue::Boolean(reader.read_u8()? != 0))
124 }
125 ValueTag::Keyword => Ok(IppValue::Keyword(reader.read_string(vsize as usize)?)),
126 ValueTag::MimeMediaType => Ok(IppValue::MimeMediaType(reader.read_string(vsize as usize)?)),
127 ValueTag::DateTime => Ok(IppValue::DateTime {
128 year: reader.read_u16::<BigEndian>()?,
129 month: reader.read_u8()?,
130 day: reader.read_u8()?,
131 hour: reader.read_u8()?,
132 minutes: reader.read_u8()?,
133 seconds: reader.read_u8()?,
134 deciseconds: reader.read_u8()?,
135 utcdir: reader.read_u8()? as char,
136 utchours: reader.read_u8()?,
137 utcmins: reader.read_u8()?,
138 }),
139 ValueTag::MemberAttrName => Ok(IppValue::MemberAttrName(reader.read_string(vsize as usize)?)),
140 ValueTag::Resolution => Ok(IppValue::Resolution {
141 crossfeed: reader.read_i32::<BigEndian>()?,
142 feed: reader.read_i32::<BigEndian>()?,
143 units: reader.read_i8()?,
144 }),
145 _ => Ok(IppValue::Other {
146 tag: vtag,
147 data: reader.read_bytes(vsize as usize)?,
148 }),
149 }
150 }
151}
152
153impl IppWriter for IppValue {
154 fn write(&self, writer: &mut Write) -> io::Result<usize> {
156 match *self {
157 IppValue::Integer(i) | IppValue::Enum(i) => {
158 writer.write_u16::<BigEndian>(4)?;
159 writer.write_i32::<BigEndian>(i)?;
160 Ok(6)
161 }
162 IppValue::RangeOfInteger { min, max } => {
163 writer.write_u16::<BigEndian>(8)?;
164 writer.write_i32::<BigEndian>(min)?;
165 writer.write_i32::<BigEndian>(max)?;
166 Ok(10)
167 }
168 IppValue::Boolean(b) => {
169 writer.write_u16::<BigEndian>(1)?;
170 writer.write_u8(if b { 1 } else { 0 })?;
171 Ok(3)
172 }
173 IppValue::Keyword(ref s)
174 | IppValue::OctetString(ref s)
175 | IppValue::TextWithoutLanguage(ref s)
176 | IppValue::NameWithoutLanguage(ref s)
177 | IppValue::Charset(ref s)
178 | IppValue::NaturalLanguage(ref s)
179 | IppValue::Uri(ref s)
180 | IppValue::MimeMediaType(ref s)
181 | IppValue::MemberAttrName(ref s) => {
182 writer.write_u16::<BigEndian>(s.len() as u16)?;
183 writer.write_all(s.as_bytes())?;
184 Ok(2 + s.len())
185 }
186 IppValue::ListOf(ref list) => {
187 let mut retval = 0;
188 for (i, item) in list.iter().enumerate() {
189 retval += item.write(writer)?;
190 if i < list.len() - 1 {
191 writer.write_u8(self.to_tag() as u8)?;
192 writer.write_u16::<BigEndian>(0)?;
193 retval += 3;
194 }
195 }
196 Ok(retval)
197 }
198 IppValue::Collection(ref list) => {
199 let mut retval = 0;
200
201 writer.write_u16::<BigEndian>(0)?;
203 retval += 2;
204
205 for item in list.iter() {
206 writer.write_u8(item.to_tag() as u8)?;
208 writer.write_u16::<BigEndian>(0)?;
210 retval += 3 + item.write(writer)?;
212 }
213 writer.write_u8(ValueTag::EndCollection as u8)?;
215 writer.write_u32::<BigEndian>(0)?;
216 retval += 5;
217
218 Ok(retval)
219 }
220 IppValue::DateTime {
221 year,
222 month,
223 day,
224 hour,
225 minutes,
226 seconds,
227 deciseconds,
228 utcdir,
229 utchours,
230 utcmins,
231 } => {
232 writer.write_u16::<BigEndian>(11)?;
233
234 writer.write_u16::<BigEndian>(year)?;
235 writer.write_u8(month)?;
236 writer.write_u8(day)?;
237 writer.write_u8(hour)?;
238 writer.write_u8(minutes)?;
239 writer.write_u8(seconds)?;
240 writer.write_u8(deciseconds)?;
241 writer.write_u8(utcdir as u8)?;
242 writer.write_u8(utchours)?;
243 writer.write_u8(utcmins)?;
244
245 Ok(13)
246 }
247 IppValue::Resolution { crossfeed, feed, units } => {
248 writer.write_u16::<BigEndian>(9)?;
249 writer.write_i32::<BigEndian>(crossfeed)?;
250 writer.write_i32::<BigEndian>(feed)?;
251 writer.write_i8(units)?;
252 Ok(9)
253 }
254 IppValue::Other { ref data, .. } => {
255 writer.write_u16::<BigEndian>(data.len() as u16)?;
256 writer.write_all(data)?;
257 Ok(2 + data.len())
258 }
259 }
260 }
261}
262
263impl fmt::Display for IppValue {
265 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266 match *self {
267 IppValue::Integer(i) | IppValue::Enum(i) => write!(f, "{}", i),
268 IppValue::RangeOfInteger { min, max } => write!(f, "{}..{}", min, max),
269 IppValue::Boolean(b) => write!(f, "{}", if b { "true" } else { "false" }),
270 IppValue::Keyword(ref s)
271 | IppValue::OctetString(ref s)
272 | IppValue::TextWithoutLanguage(ref s)
273 | IppValue::NameWithoutLanguage(ref s)
274 | IppValue::Charset(ref s)
275 | IppValue::NaturalLanguage(ref s)
276 | IppValue::Uri(ref s)
277 | IppValue::MimeMediaType(ref s)
278 | IppValue::MemberAttrName(ref s) => write!(f, "{}", s),
279 IppValue::ListOf(ref list) => {
280 let s: Vec<String> = list.iter().map(|v| format!("{}", v)).collect();
281 write!(f, "[{}]", s.join(", "))
282 }
283 IppValue::Collection(ref list) => {
284 let s: Vec<String> = list.iter().map(|v| format!("{}", v)).collect();
285 write!(f, "<{}>", s.join(", "))
286 }
287 IppValue::DateTime {
288 year,
289 month,
290 day,
291 hour,
292 minutes,
293 seconds,
294 deciseconds,
295 utcdir,
296 utchours,
297 ..
298 } => write!(
299 f,
300 "{}-{}-{},{}:{}:{}.{},{}{}utc",
301 year, month, day, hour, minutes, seconds, deciseconds, utcdir as char, utchours
302 ),
303 IppValue::Resolution { crossfeed, feed, units } => {
304 write!(f, "{}x{}{}", crossfeed, feed, if units == 3 { "in" } else { "cm" })
305 }
306
307 IppValue::Other { tag, ref data } => write!(f, "{:0x}: {:?}", tag, data),
308 }
309 }
310}
311
312impl<'a> IntoIterator for &'a IppValue {
313 type Item = &'a IppValue;
314 type IntoIter = IppValueIterator<'a>;
315
316 fn into_iter(self) -> Self::IntoIter {
317 IppValueIterator { value: self, index: 0 }
318 }
319}
320
321pub struct IppValueIterator<'a> {
322 value: &'a IppValue,
323 index: usize,
324}
325
326impl<'a> Iterator for IppValueIterator<'a> {
327 type Item = &'a IppValue;
328
329 fn next(&mut self) -> Option<Self::Item> {
330 match self.value {
331 IppValue::ListOf(ref list) | IppValue::Collection(ref list) => {
332 if self.index < list.len() {
333 self.index += 1;
334 Some(&list[self.index - 1])
335 } else {
336 None
337 }
338 }
339 _ => {
340 if self.index == 0 {
341 self.index += 1;
342 Some(self.value)
343 } else {
344 None
345 }
346 }
347 }
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use crate::{ipp::DelimiterTag, IppAttribute};
354
355 use super::*;
356
357 #[test]
358 fn test_value_iterator_single() {
359 let val = IppValue::Integer(1234);
360
361 for v in &val {
362 assert_eq!(*v, val);
363 }
364 }
365
366 #[test]
367 fn test_value_iterator_multiple() {
368 let list = vec![IppValue::Integer(1234), IppValue::Integer(5678)];
369 let val = IppValue::ListOf(list.clone());
370
371 for v in val.into_iter().enumerate() {
372 assert_eq!(*v.1, list[v.0]);
373 }
374 }
375
376 #[test]
377 fn test_collection_de_serialize() {
378 let attr = IppAttribute::new(
379 "coll",
380 IppValue::Collection(vec![IppValue::Integer(0x11111111), IppValue::Integer(0x22222222)]),
381 );
382 let mut buf = Vec::new();
383 assert!(attr.write(&mut io::Cursor::new(&mut buf)).is_ok());
384
385 assert_eq!(
386 vec![
387 0x34, 0, 4, b'c', b'o', b'l', b'l', 0, 0, 0x21, 0, 0, 0, 4, 0x11, 0x11, 0x11, 0x11, 0x21, 0, 0, 0, 4,
388 0x22, 0x22, 0x22, 0x22, 0x37, 0, 0, 0, 0,
389 ],
390 buf
391 );
392
393 let mut data = vec![1, 1, 0, 0, 0, 0, 0, 0, 4];
394 data.extend(buf);
395 data.extend(vec![3]);
396
397 let result = crate::parser::IppParser::new(&mut io::Cursor::new(data)).parse();
398 assert!(result.is_ok());
399
400 let res = result.ok().unwrap();
401 let attrs = res.attributes.groups_of(DelimiterTag::PrinterAttributes)[0].attributes();
402 let attr = attrs.get("coll").unwrap();
403 assert_eq!(
404 attr.value().as_collection(),
405 Some(&vec![IppValue::Integer(0x11111111), IppValue::Integer(0x22222222)])
406 );
407 }
408}