This allows QObject to be converted to other formats, for example JSON via serde_json.
This is not too useful, since QObjects are consumed by C code or deserialized into structs, but it can be used for testing and it is part of the full implementation of a serde format. Co-authored-by: Marc-André Lureau <[email protected]> Signed-off-by: Marc-André Lureau <[email protected]> Reviewed-by: Zhao Liu <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]> --- rust/Cargo.lock | 1 + rust/util/Cargo.toml | 1 + rust/util/meson.build | 2 +- rust/util/src/qobject/mod.rs | 4 +-- rust/util/src/qobject/serialize.rs | 57 ++++++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 rust/util/src/qobject/serialize.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index cbb3ca15f77..b9e8636b8bc 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -513,6 +513,7 @@ dependencies = [ "foreign", "glib-sys", "libc", + "serde", "util-sys", ] diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml index 2ad5940daca..0a0400278f3 100644 --- a/rust/util/Cargo.toml +++ b/rust/util/Cargo.toml @@ -17,6 +17,7 @@ anyhow = { workspace = true } foreign = { workspace = true } glib-sys = { workspace = true } libc = { workspace = true } +serde = { workspace = true } common = { path = "../common" } util-sys = { path = "../bindings/util-sys" } diff --git a/rust/util/meson.build b/rust/util/meson.build index 6d175ae0b0f..a8d9978e1a9 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -1,7 +1,7 @@ _util_rs = static_library( 'util', 'src/lib.rs', - dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, util_sys_rs], + dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, serde_rs, util_sys_rs], ) util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs index 210078ea23e..c42b75f3199 100644 --- a/rust/util/src/qobject/mod.rs +++ b/rust/util/src/qobject/mod.rs @@ -6,6 +6,8 @@ #![deny(clippy::unwrap_used)] +mod serialize; + use std::{ cell::UnsafeCell, ffi::{c_char, CString}, @@ -246,7 +248,6 @@ fn drop(&mut self) { } } -#[allow(unused)] macro_rules! match_qobject { (@internal ($qobj:expr) => $(() => $unit:expr,)? @@ -329,5 +330,4 @@ macro_rules! match_qobject { $($type $(($val))? => $code,)+) }; } -#[allow(unused_imports)] use match_qobject; diff --git a/rust/util/src/qobject/serialize.rs b/rust/util/src/qobject/serialize.rs new file mode 100644 index 00000000000..97250602cc3 --- /dev/null +++ b/rust/util/src/qobject/serialize.rs @@ -0,0 +1,57 @@ +//! `QObject` serialization +//! +//! This module implements the [`Serialize`] trait for `QObject`, +//! allowing it to be converted to other formats, for example +//! JSON. + +use std::{ffi::CStr, mem::ManuallyDrop, ptr::addr_of}; + +use serde::ser::{self, Serialize, SerializeMap, SerializeSeq}; + +use super::{match_qobject, QObject}; +use crate::bindings; + +impl Serialize for QObject { + #[inline] + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ::serde::Serializer, + { + match_qobject! { (self) => + () => serializer.serialize_unit(), + bool(b) => serializer.serialize_bool(b), + i64(i) => serializer.serialize_i64(i), + u64(u) => serializer.serialize_u64(u), + f64(f) => serializer.serialize_f64(f), + CStr(cstr) => { + let s = cstr.to_str().map_err(ser::Error::custom)?; + serializer.serialize_str(s) + }, + QList(l) => { + let mut node_ptr = unsafe { l.head.tqh_first }; + let mut state = serializer.serialize_seq(None)?; + while !node_ptr.is_null() { + let node = unsafe { &*node_ptr }; + let elem = unsafe { ManuallyDrop::new(QObject::from_raw(addr_of!(*node.value))) }; + state.serialize_element(&*elem)?; + node_ptr = unsafe { node.next.tqe_next }; + } + state.end() + }, + QDict(d) => { + let mut state = serializer.serialize_map(Some(d.size))?; + let mut e_ptr = unsafe { bindings::qdict_first(d) }; + while !e_ptr.is_null() { + let e = unsafe { &*e_ptr }; + let key = unsafe { CStr::from_ptr(e.key) }; + let key = key.to_str().map_err(ser::Error::custom)?; + state.serialize_key(key)?; + let value = unsafe { ManuallyDrop::new(QObject::from_raw(addr_of!(*e.value))) }; + state.serialize_value(&*value)?; + e_ptr = unsafe { bindings::qdict_next(d, e) }; + } + state.end() + } + } + } +} -- 2.54.0
