1 //! Types and traits for easily getting a message's arguments, or appening a message with arguments.
2 //!
3 //! Also see the arguments guide (in the examples directory).
4 //!
5 //! A message has `read1`, `read2` etc, and `append1`, `append2` etc, which is your
6 //! starting point into this module's types.
7 //!
8 //! **Append a**:
9 //!
10 //! `bool, u8, u16, u32, u64, i16, i32, i64, f64` - the corresponding D-Bus basic type
11 //!
12 //! `&str` - a D-Bus string. D-Bus strings do not allow null characters, so
13 //! if the string contains null characters, it will be cropped
14 //! to only include the data before the null character. (Tip: This allows for skipping an
15 //! allocation by writing a string literal which ends with a null character.)
16 //!
17 //! `&[T] where T: Append` - a D-Bus array. Note: can use an efficient fast-path in case of
18 //! T being an FixedArray type.
19 //!
20 //! `Array<T, I> where T: Append, I: Iterator<Item=T>` - a D-Bus array, maximum flexibility.
21 //!
22 //! `Variant<T> where T: Append` - a D-Bus variant.
23 //!
24 //! `(T1, T2) where T1: Append, T2: Append` - tuples are D-Bus structs. Implemented up to 12.
25 //!
26 //! `Dict<K, V, I> where K: Append + DictKey, V: Append, I: Iterator<Item=(&K, &V)>` - A D-Bus dict (array of dict entries).
27 //!
28 //! `Path` - a D-Bus object path.
29 //!
30 //! `Signature` - a D-Bus signature.
31 //!
32 //! `OwnedFd` - shares the file descriptor with the remote side.
33 //!
34 //! **Get / read a**:
35 //!
36 //! `bool, u8, u16, u32, u64, i16, i32, i64, f64` - the corresponding D-Bus basic type
37 //!
38 //! `&str`, `&CStr` - a D-Bus string. D-Bus strings are always UTF-8 and do not contain null characters.
39 //!
40 //! `&[T] where T: FixedArray` - a D-Bus array of integers or f64.
41 //!
42 //! `Array<T, Iter> where T: Get` - a D-Bus array, maximum flexibility. Implements Iterator so you can easily
43 //! collect it into, e g, a `Vec`.
44 //!
45 //! `Variant<T> where T: Get` - a D-Bus variant. Use this type of Variant if you know the inner type.
46 //!
47 //! `Variant<Iter>` - a D-Bus variant. This type of Variant allows you to examine the inner type.
48 //!
49 //! `(T1, T2) where T1: Get, T2: Get` - tuples are D-Bus structs. Implemented up to 12.
50 //!
51 //! `Dict<K, V, Iter> where K: Get + DictKey, V: Get` - A D-Bus dict (array of dict entries). Implements Iterator so you can easily
52 //! collect it into, e g, a `HashMap`.
53 //!
54 //! `Path` - a D-Bus object path.
55 //!
56 //! `Signature` - a D-Bus signature.
57 //!
58 //! `OwnedFd` - a file descriptor sent from the remote side.
59 //!
60 
61 mod msgarg;
62 mod basic_impl;
63 mod variantstruct_impl;
64 mod array_impl;
65 
66 pub mod messageitem;
67 
68 pub use self::msgarg::{Arg, FixedArray, Get, DictKey, Append, RefArg, AppendAll, ReadAll, ArgAll,
69     cast, cast_mut, prop_cast, PropMap};
70 pub use self::array_impl::{Array, Dict};
71 pub use self::variantstruct_impl::Variant;
72 
73 use std::{fmt, mem, ptr, error};
74 use crate::{ffi, Message, Signature, Path};
75 use std::ffi::{CStr, CString};
76 use std::os::raw::{c_void, c_int};
77 #[cfg(unix)]
78 use std::os::unix::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd};
79 use std::collections::VecDeque;
80 
check(f: &str, i: u32)81 fn check(f: &str, i: u32) { if i == 0 { panic!("D-Bus error: '{}' failed", f) }}
82 
ffi_iter() -> ffi::DBusMessageIter83 fn ffi_iter() -> ffi::DBusMessageIter {
84     // Safe because DBusMessageIter contains only fields that are allowed to be zeroed (i e no references or similar)
85     unsafe { mem::zeroed() }
86 }
87 
88 /// An RAII wrapper around Fd to ensure that file descriptor is closed
89 /// when the scope ends.
90 #[derive(Debug, PartialEq, PartialOrd)]
91 pub struct OwnedFd {
92     #[cfg(unix)]
93     fd: RawFd
94 }
95 
96 #[cfg(unix)]
97 impl OwnedFd {
98     /// Create a new OwnedFd from a RawFd.
99     ///
100     /// This function is unsafe, because you could potentially send in an invalid file descriptor,
101     /// or close it during the lifetime of this struct. This could potentially be unsound.
new(fd: RawFd) -> OwnedFd102     pub unsafe fn new(fd: RawFd) -> OwnedFd {
103         OwnedFd { fd: fd }
104     }
105 
106     /// Convert an OwnedFD back into a RawFd.
into_fd(self) -> RawFd107     pub fn into_fd(self) -> RawFd {
108         let s = self.fd;
109         ::std::mem::forget(self);
110         s
111     }
112 }
113 
114 #[cfg(unix)]
115 impl Drop for OwnedFd {
drop(&mut self)116     fn drop(&mut self) {
117         unsafe { libc::close(self.fd); }
118     }
119 }
120 
121 impl Clone for OwnedFd {
122     #[cfg(unix)]
clone(&self) -> OwnedFd123     fn clone(&self) -> OwnedFd {
124         let x = unsafe { libc::dup(self.fd) };
125         if x == -1 { panic!("Duplicating file descriptor failed") }
126         unsafe { OwnedFd::new(x) }
127     }
128 
129     #[cfg(windows)]
clone(&self) -> OwnedFd130     fn clone(&self) -> OwnedFd {
131         OwnedFd {}
132     }
133 }
134 
135 #[cfg(unix)]
136 impl AsRawFd for OwnedFd {
as_raw_fd(&self) -> RawFd137     fn as_raw_fd(&self) -> RawFd {
138         self.fd
139     }
140 }
141 
142 #[cfg(unix)]
143 impl IntoRawFd for OwnedFd {
into_raw_fd(self) -> RawFd144     fn into_raw_fd(self) -> RawFd {
145         self.into_fd()
146     }
147 }
148 
149 #[cfg(unix)]
150 impl FromRawFd for OwnedFd {
from_raw_fd(fd: RawFd) -> Self151     unsafe fn from_raw_fd(fd: RawFd) -> Self { OwnedFd::new(fd) }
152 }
153 
154 #[derive(Clone, Copy)]
155 /// Helper struct for appending one or more arguments to a Message.
156 pub struct IterAppend<'a>(ffi::DBusMessageIter, &'a Message);
157 
158 impl<'a> IterAppend<'a> {
159     /// Creates a new IterAppend struct.
new(m: &'a mut Message) -> IterAppend<'a>160     pub fn new(m: &'a mut Message) -> IterAppend<'a> {
161         let mut i = ffi_iter();
162         unsafe { ffi::dbus_message_iter_init_append(m.ptr(), &mut i) };
163         IterAppend(i, m)
164     }
165 
166     /// Appends the argument.
append<T: Append>(&mut self, a: T)167     pub fn append<T: Append>(&mut self, a: T) { a.append(self) }
168 
append_container<F: FnOnce(&mut IterAppend<'a>)>(&mut self, arg_type: ArgType, sig: Option<&CStr>, f: F)169     fn append_container<F: FnOnce(&mut IterAppend<'a>)>(&mut self, arg_type: ArgType, sig: Option<&CStr>, f: F) {
170         let mut s = IterAppend(ffi_iter(), self.1);
171         let p = sig.map(|s| s.as_ptr()).unwrap_or(ptr::null());
172         check("dbus_message_iter_open_container",
173             unsafe { ffi::dbus_message_iter_open_container(&mut self.0, arg_type as c_int, p, &mut s.0) });
174         f(&mut s);
175         check("dbus_message_iter_close_container",
176             unsafe { ffi::dbus_message_iter_close_container(&mut self.0, &mut s.0) });
177     }
178 
179     /// Low-level function to append a variant.
180     ///
181     /// Use in case the `Variant` struct is not flexible enough -
182     /// the easier way is to just call e g "append1" on a message and supply a `Variant` parameter.
183     ///
184     /// In order not to get D-Bus errors: during the call to "f" you need to call "append" on
185     /// the supplied `IterAppend` exactly once,
186     /// and with a value which has the same signature as inner_sig.
append_variant<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F)187     pub fn append_variant<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F) {
188         self.append_container(ArgType::Variant, Some(inner_sig.as_cstr()), f)
189     }
190 
191     /// Low-level function to append an array.
192     ///
193     /// Use in case the `Array` struct is not flexible enough -
194     /// the easier way is to just call e g "append1" on a message and supply an `Array` parameter.
195     ///
196     /// In order not to get D-Bus errors: during the call to "f", you should only call "append" on
197     /// the supplied `IterAppend` with values which has the same signature as inner_sig.
append_array<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F)198     pub fn append_array<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F) {
199         self.append_container(ArgType::Array, Some(inner_sig.as_cstr()), f)
200     }
201 
202     /// Low-level function to append a struct.
203     ///
204     /// Use in case tuples are not flexible enough -
205     /// the easier way is to just call e g "append1" on a message and supply a tuple parameter.
append_struct<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F)206     pub fn append_struct<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) {
207         self.append_container(ArgType::Struct, None, f)
208     }
209 
210     /// Low-level function to append a dict entry.
211     ///
212     /// Use in case the `Dict` struct is not flexible enough -
213     /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter.
214     ///
215     /// In order not to get D-Bus errors: during the call to "f", you should call "append" once
216     /// for the key, then once for the value. You should only call this function for a subiterator
217     /// you got from calling "append_dict", and signatures need to match what you specified in "append_dict".
append_dict_entry<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F)218     pub fn append_dict_entry<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) {
219         self.append_container(ArgType::DictEntry, None, f)
220     }
221 
222     /// Low-level function to append a dict.
223     ///
224     /// Use in case the `Dict` struct is not flexible enough -
225     /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter.
226     ///
227     /// In order not to get D-Bus errors: during the call to "f", you should only call "append_dict_entry"
228     /// for the subiterator - do this as many times as the number of dict entries.
append_dict<F: FnOnce(&mut IterAppend<'a>)>(&mut self, key_sig: &Signature, value_sig: &Signature, f: F)229     pub fn append_dict<F: FnOnce(&mut IterAppend<'a>)>(&mut self, key_sig: &Signature, value_sig: &Signature, f: F) {
230         let sig = format!("{{{}{}}}", key_sig, value_sig);
231         self.append_container(Array::<bool,()>::ARG_TYPE, Some(&CString::new(sig).unwrap()), f);
232     }
233 }
234 
235 
236 
237 #[derive(Clone, Copy)]
238 /// Helper struct for retrieve one or more arguments from a Message.
239 pub struct Iter<'a>(ffi::DBusMessageIter, &'a Message, u32);
240 
241 impl<'a> Iter<'a> {
242     /// Creates a new struct for iterating over the arguments of a message, starting with the first argument.
new(m: &'a Message) -> Iter<'a>243     pub fn new(m: &'a Message) -> Iter<'a> {
244         let mut i = ffi_iter();
245         unsafe { ffi::dbus_message_iter_init(m.ptr(), &mut i) };
246         Iter(i, m, 0)
247     }
248 
249     /// Returns the current argument, if T is the argument type. Otherwise returns None.
get<T: Get<'a>>(&mut self) -> Option<T>250     pub fn get<T: Get<'a>>(&mut self) -> Option<T> {
251         T::get(self)
252     }
253 
254     /// Returns the current argument as a trait object.
255     ///
256     /// Note: For the more complex arguments (arrays / dicts / structs, and especially
257     /// combinations thereof), their internal representations are still a bit in flux.
258     /// Instead, use as_iter() to read the values of those.
259     ///
260     /// The rest are unlikely to change - Variants are `Variant<Box<dyn RefArg>>`, strings are `String`,
261     /// paths are `Path<'static>`, signatures are `Signature<'static>`, Int32 are `i32s` and so on.
get_refarg(&mut self) -> Option<Box<dyn RefArg + 'static>>262     pub fn get_refarg(&mut self) -> Option<Box<dyn RefArg + 'static>> {
263         Some(match self.arg_type() {
264             ArgType::Array => array_impl::get_array_refarg(self),
265             ArgType::Variant => Box::new(Variant::new_refarg(self).unwrap()),
266             ArgType::Boolean => Box::new(self.get::<bool>().unwrap()),
267             ArgType::Invalid => return None,
268             ArgType::String => Box::new(self.get::<String>().unwrap()),
269             ArgType::DictEntry => unimplemented!(),
270             ArgType::Byte => Box::new(self.get::<u8>().unwrap()),
271             ArgType::Int16 => Box::new(self.get::<i16>().unwrap()),
272             ArgType::UInt16 => Box::new(self.get::<u16>().unwrap()),
273             ArgType::Int32 => Box::new(self.get::<i32>().unwrap()),
274             ArgType::UInt32 => Box::new(self.get::<u32>().unwrap()),
275             ArgType::Int64 => Box::new(self.get::<i64>().unwrap()),
276             ArgType::UInt64 => Box::new(self.get::<u64>().unwrap()),
277             ArgType::Double => Box::new(self.get::<f64>().unwrap()),
278             ArgType::UnixFd => Box::new(self.get::<std::fs::File>().unwrap()),
279             ArgType::Struct => Box::new(self.recurse(ArgType::Struct).unwrap().collect::<VecDeque<_>>()),
280             ArgType::ObjectPath => Box::new(self.get::<Path>().unwrap().into_static()),
281             ArgType::Signature => Box::new(self.get::<Signature>().unwrap().into_static()),
282         })
283     }
284 
285     /// Returns the type signature for the current argument.
signature(&mut self) -> Signature<'static>286     pub fn signature(&mut self) -> Signature<'static> {
287         unsafe {
288             let c = ffi::dbus_message_iter_get_signature(&mut self.0);
289             assert!(c != ptr::null_mut());
290             let cc = CStr::from_ptr(c);
291             let r = Signature::new(std::str::from_utf8(cc.to_bytes()).unwrap());
292             ffi::dbus_free(c as *mut c_void);
293             r.unwrap()
294         }
295     }
296 
297     /// The raw arg_type for the current item.
298     ///
299     /// Unlike Arg::arg_type, this requires access to self and is not a static method.
300     /// You can match this against Arg::arg_type for different types to understand what type the current item is.
301     /// In case you're past the last argument, this function will return 0.
arg_type(&mut self) -> ArgType302     pub fn arg_type(&mut self) -> ArgType {
303         let s = unsafe { ffi::dbus_message_iter_get_arg_type(&mut self.0) };
304         ArgType::from_i32(s as i32).unwrap()
305     }
306 
307     /// Returns false if there are no more items.
next(&mut self) -> bool308     pub fn next(&mut self) -> bool {
309         self.2 += 1;
310         unsafe { ffi::dbus_message_iter_next(&mut self.0) != 0 }
311     }
312 
313     /// Wrapper around `get` and `next`. Calls `get`, and then `next` if `get` succeeded.
314     ///
315     /// Also returns a `Result` rather than an `Option` to give an error if successful.
316     ///
317     /// # Example
318     /// ```ignore
319     /// struct ServiceBrowserItemNew {
320     ///     interface: i32,
321     ///     protocol: i32,
322     ///     name: String,
323     ///     item_type: String,
324     ///     domain: String,
325     ///     flags: u32,
326     /// }
327     ///
328     /// fn service_browser_item_new_msg(m: &Message) -> Result<ServiceBrowserItemNew, TypeMismatchError> {
329     ///     let mut iter = m.iter_init();
330     ///     Ok(ServiceBrowserItemNew {
331     ///         interface: iter.read()?,
332     ///         protocol: iter.read()?,
333     ///         name: iter.read()?,
334     ///         item_type: iter.read()?,
335     ///         domain: iter.read()?,
336     ///         flags: iter.read()?,
337     ///     })
338     /// }
339     /// ```
read<T: Arg + Get<'a>>(&mut self) -> Result<T, TypeMismatchError>340     pub fn read<T: Arg + Get<'a>>(&mut self) -> Result<T, TypeMismatchError> {
341         let r = self.get().ok_or_else(||
342              TypeMismatchError { expected: T::ARG_TYPE, found: self.arg_type(), position: self.2 })?;
343         self.next();
344         Ok(r)
345     }
346 
347     /// If the current argument is a container of the specified arg_type, then a new
348     /// Iter is returned which is for iterating over the contents inside the container.
349     ///
350     /// Primarily for internal use (the "get" function is more ergonomic), but could be
351     /// useful for recursing into containers with unknown types.
recurse(&mut self, arg_type: ArgType) -> Option<Iter<'a>>352     pub fn recurse(&mut self, arg_type: ArgType) -> Option<Iter<'a>> {
353         let containers = [ArgType::Array, ArgType::DictEntry, ArgType::Struct, ArgType::Variant];
354         if !containers.iter().any(|&t| t == arg_type) { return None; }
355 
356         let mut subiter = ffi_iter();
357         unsafe {
358             if ffi::dbus_message_iter_get_arg_type(&mut self.0) != arg_type as c_int { return None };
359             ffi::dbus_message_iter_recurse(&mut self.0, &mut subiter)
360         }
361         Some(Iter(subiter, self.1, 0))
362     }
363 }
364 
365 impl<'a> fmt::Debug for Iter<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result366     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
367         let mut z = self.clone();
368         let mut t = f.debug_tuple("Iter");
369         loop {
370             t.field(&z.arg_type());
371             if !z.next() { break }
372         }
373         t.finish()
374     }
375 }
376 
377 impl<'a> Iterator for Iter<'a> {
378     type Item = Box<dyn RefArg + 'static>;
next(&mut self) -> Option<Self::Item>379     fn next(&mut self) -> Option<Self::Item> {
380         let r = self.get_refarg();
381         if r.is_some() { self.next(); }
382         r
383     }
384 }
385 
386 /// Type of Argument
387 ///
388 /// use this to figure out, e g, which type of argument is at the current position of Iter.
389 #[repr(u8)]
390 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
391 pub enum ArgType {
392     /// Dicts are Arrays of dict entries, so Dict types will have Array as ArgType.
393     Array = ffi::DBUS_TYPE_ARRAY as u8,
394     /// Variant
395     Variant = ffi::DBUS_TYPE_VARIANT as u8,
396     /// bool
397     Boolean = ffi::DBUS_TYPE_BOOLEAN as u8,
398     /// Invalid arg type - this is also the ArgType returned when there are no more arguments available.
399     Invalid = ffi::DBUS_TYPE_INVALID as u8,
400     /// String
401     String = ffi::DBUS_TYPE_STRING as u8,
402     /// Dict entry; you'll usually not encounter this one as dicts are arrays of dict entries.
403     DictEntry = ffi::DBUS_TYPE_DICT_ENTRY as u8,
404     /// u8
405     Byte = ffi::DBUS_TYPE_BYTE as u8,
406     /// i16
407     Int16 = ffi::DBUS_TYPE_INT16 as u8,
408     /// u16
409     UInt16 = ffi::DBUS_TYPE_UINT16 as u8,
410     /// i32
411     Int32 = ffi::DBUS_TYPE_INT32 as u8,
412     /// u32
413     UInt32 = ffi::DBUS_TYPE_UINT32 as u8,
414     /// i64
415     Int64 = ffi::DBUS_TYPE_INT64 as u8,
416     /// u64
417     UInt64 = ffi::DBUS_TYPE_UINT64 as u8,
418     /// f64
419     Double = ffi::DBUS_TYPE_DOUBLE as u8,
420     /// File
421     UnixFd = ffi::DBUS_TYPE_UNIX_FD as u8,
422     /// Use tuples or Vec<Box<dyn RefArg>> to read/write structs.
423     Struct = ffi::DBUS_TYPE_STRUCT as u8,
424     /// Path
425     ObjectPath = ffi::DBUS_TYPE_OBJECT_PATH as u8,
426     /// Signature
427     Signature = ffi::DBUS_TYPE_SIGNATURE as u8,
428 }
429 
430 const ALL_ARG_TYPES: [(ArgType, &str); 18] =
431     [(ArgType::Variant, "Variant"),
432     (ArgType::Array, "Array/Dict"),
433     (ArgType::Struct, "Struct"),
434     (ArgType::String, "String"),
435     (ArgType::DictEntry, "Dict entry"),
436     (ArgType::ObjectPath, "Path"),
437     (ArgType::Signature, "Signature"),
438     (ArgType::UnixFd, "File"),
439     (ArgType::Boolean, "bool"),
440     (ArgType::Byte, "u8"),
441     (ArgType::Int16, "i16"),
442     (ArgType::Int32, "i32"),
443     (ArgType::Int64, "i64"),
444     (ArgType::UInt16, "u16"),
445     (ArgType::UInt32, "u32"),
446     (ArgType::UInt64, "u64"),
447     (ArgType::Double, "f64"),
448     (ArgType::Invalid, "nothing")];
449 
450 impl ArgType {
451     /// A str corresponding to the name of a Rust type.
as_str(self) -> &'static str452     pub fn as_str(self) -> &'static str {
453         ALL_ARG_TYPES.iter().skip_while(|a| a.0 != self).next().unwrap().1
454     }
455 
456     /// Returns a Vec of all possible argtypes.
all() -> Vec<Self>457     pub fn all() -> Vec<Self> {
458         ALL_ARG_TYPES.iter().map(|x| x.0).collect()
459     }
460 
461     /// Converts an i32 to an ArgType (or an error).
from_i32(i: i32) -> Result<ArgType, String>462     pub fn from_i32(i: i32) -> Result<ArgType, String> {
463         for &(a, _) in &ALL_ARG_TYPES {
464             if a as i32 == i { return Ok(a); }
465         }
466         Err(format!("Invalid ArgType {} ({})", i, i as u8 as char))
467     }
468 }
469 
470 
471 /// Error struct to indicate a D-Bus argument type mismatch.
472 ///
473 /// Might be returned from `iter::read()`.
474 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
475 pub struct TypeMismatchError {
476     expected: ArgType,
477     found: ArgType,
478     position: u32,
479 }
480 
481 impl TypeMismatchError {
482     /// The ArgType we were trying to read, but failed
expected_arg_type(&self) -> ArgType483     pub fn expected_arg_type(&self) -> ArgType { self.expected }
484 
485     /// The ArgType we should have been trying to read, if we wanted the read to succeed
found_arg_type(&self) -> ArgType486     pub fn found_arg_type(&self) -> ArgType { self.found }
487 
488     /// At what argument was the error found?
489     ///
490     /// Returns 0 for first argument, 1 for second argument, etc.
pos(&self) -> u32491     pub fn pos(&self) -> u32 { self.position }
492 }
493 
494 impl error::Error for TypeMismatchError {
description(&self) -> &str495     fn description(&self) -> &str { "D-Bus argument type mismatch" }
cause(&self) -> Option<&dyn error::Error>496     fn cause(&self) -> Option<&dyn error::Error> { None }
497 }
498 
499 impl fmt::Display for TypeMismatchError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result500     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
501         write!(f, "D-Bus argument type mismatch at position {}: expected {}, found {}",
502             self.position, self.expected.as_str(),
503             if self.expected == self.found { "same but still different somehow" } else { self.found.as_str() }
504         )
505     }
506 }
507 
508 
509 #[allow(dead_code)]
test_compile()510 fn test_compile() {
511     let mut msg = Message::new_signal("/", "a.b", "C").unwrap();
512     let mut q = IterAppend::new(&mut msg);
513 
514     q.append(5u8);
515     q.append(Array::new(&[5u8, 6, 7]));
516     q.append((8u8, &[9u8, 6, 7][..]));
517     q.append(Variant((6u8, 7u8)));
518 }
519