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