From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877958; cv=none; d=zohomail.com; s=zohoarc; b=QQIWyvXxWFGCMzWDL0EOkx+olquUkCh2AXXFAaf5xCjOWHlEKXydjZVTUNHF/stB4tLe8ttoh597bT6yC8z24kaAjE5uUHfNlD5Zv8TlnXn5LrGk+P0YK6bEcmQF9/mta19END13NCPQjwiaZ4laGuWwhOZ7CuzxMBVsTMPnn1s= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877958; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=1+ou4jVNnFiCxKe4bwC9g/4o2bH3WOIu5RMilZ8uR2g=; b=SOjtTwMb03+How9GbdYdzj8o6ZdoZjuEVSnI2yAnbUbRRL7jxu5vkdDUS6IaBmFyGZwSqS2gei4acPh+TZ7tuD+4el+4wDhSXQ6PUfJ4SR2WRQsOheYClst2aaAOURenvJLLT+GzRUJCdPAoP4501djGztnNYw8Cf+4eTp2MrzE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877958563957.1459117352597; Thu, 8 Jan 2026 05:12:38 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpn0-0007Vh-0b; Thu, 08 Jan 2026 08:10:58 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpmx-0007Uh-Pk for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:10:55 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpmv-0007xG-3h for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:10:55 -0500 Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-319-6ERNsTlgPXOWs0YqHA5LEg-1; Thu, 08 Jan 2026 08:10:51 -0500 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-4325ddc5babso1596403f8f.0 for ; Thu, 08 Jan 2026 05:10:51 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432bd5ede7esm16232759f8f.32.2026.01.08.05.10.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:10:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877852; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1+ou4jVNnFiCxKe4bwC9g/4o2bH3WOIu5RMilZ8uR2g=; b=CkR1FZ+D7kkDXOywz4jsMJED0Ml/wwJPsa/gNt0MjFxB2vI1btYeL80R7+3XfGbS4RDzag mkPbTg5AS0/Abfha6f6R1ADTUUKtIZYWvVfI7Yk6wez749Zi/6zoGIU0Gzx6E/Y//sNowu S3/um4dgiK1bajgJsbx4ZaqqbhBzzqA= X-MC-Unique: 6ERNsTlgPXOWs0YqHA5LEg-1 X-Mimecast-MFC-AGG-ID: 6ERNsTlgPXOWs0YqHA5LEg_1767877850 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877849; x=1768482649; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=1+ou4jVNnFiCxKe4bwC9g/4o2bH3WOIu5RMilZ8uR2g=; b=j3PixCLnBQRQiGlYu8sWSNXKndMkQgj+UaYS08Bq7OAUduEyaW15/p1hCr6Oj84TFB G6XO434KxJoFg5y6ujBucRzPwDNpM1OX88ptVnOlA+VV5nFbshO2XopcuQHjza8wmwOz 9rEzMBHmFwiNGvI7rNUTaRBouoF4CdmnmHxwgQhY3j2rPJ8cKuMZgyEUYfYjmTN9sXIZ q7aw+OXZQSRqEiqA5EqLGQVwFcemABxhg2mSGl+irjxl5eCW+YdkdlrHbLe2jjNhG/U/ tmipVT7Bq7KgQg87rqY7k542l3yZFI8Xlnuh3AmB+6IH7eLTT9ZF9E/384oS8um56q3Q XA9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877849; x=1768482649; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=1+ou4jVNnFiCxKe4bwC9g/4o2bH3WOIu5RMilZ8uR2g=; b=amtOY6/Gi/bu0kGyE5QfHQhlM41pJqYInvhsltWQ5X6RhxTBNKYrAhn/w5egxod3Gh cn+WGoPhj4ci0zEJqXRc1cfaq3dj56qVuKt0H13+/ZADT8NSwDBYw+jqTt6jjCqiWKXy yV1i8r0gKz4h78DjR7Y0a3QDg2keYzvSO2NveKSnd2oL2zb/6Q1CvrQ863JGMEiFbQEj n3ttW/86O5pVOB2sJ8R7AY7VlUo03a00ShpT6Z0+uJ2RYaLjienVHQ6j4heUJgzGFsmu qoVxJ4vw7/xHR0Fs733Qu1UHS3r6+7ISzKnvqPcfwE3p3ajhcuYujOxxc4VrxvYuXt6y yTWg== X-Gm-Message-State: AOJu0YwSnVnH9su89z5Ue/R7boFH5NGzEe355E6M1Q9tjBKGdHDRb5X8 GX5hlpb36Df31o6Wv+xiEneXzMYKRLwIl/dKq+tvJS1thQH4VwCWBxGIPbH6PYIZWLJEqayOJsV RcwfYG3E2UJjLDpgRFEEhOfJmyUptQ/GK50o3iJ9OFrtgffez4wQIogJQrBwAH+D6AE118GKX2S Mk4tBxiHuEnmCn/XfJpLfqUhaTBNTEVxuOcNyQdmaI X-Gm-Gg: AY/fxX6E0gLSblK7AxKxp8E2CfZiIeoUU5Ypf5JUtxhTKE0BM4+JcW3lzHj8AN5TySg 8QLz1m9shNVHe9R8gExDwUgPBEYZB212u1uVTunWblNDY0P2NlvLP75/3ox0x4hptMKudUMyDxF Zl7ak/kMAkXXZrBqf4etNavDyMMF+st3ThFKp/W0cjtoEy9YbE0Yr5mi73aU/QFbtTydyt76sBd PCklKVrCmViLSFAmRO4tTpLtIrT4Y0cYO1X5JdeApfUkjUsdj05tA5wbTlxKSDlwLCkbq/AlE7g YM/6YfX3qLK7q9ooBIZ42jeZBrEChM+NTVnibAPs+3R4C7LwdIvKxlRiifR3i7Y5hxLh4WQt4kV ufEPnt+jR12OMeqo3aNgEBMMm9O4Eg+tuS/PlZIyYSIgXeaiz6UW2BFqFtgVMUt6cdvxzbqZP79 lbwPITUA7b1K8i8Q== X-Received: by 2002:a05:6000:40cd:b0:432:84ee:186f with SMTP id ffacd0b85a97d-432c379dc44mr8044186f8f.33.1767877849050; Thu, 08 Jan 2026 05:10:49 -0800 (PST) X-Google-Smtp-Source: AGHT+IFIKLIBrcrivLwbcqPHqxVQPEABek1scIeDMkQkNnrrDtJlYUm+q55Sr568msCqY6JugMjzXA== X-Received: by 2002:a05:6000:40cd:b0:432:84ee:186f with SMTP id ffacd0b85a97d-432c379dc44mr8044143f8f.33.1767877848448; Thu, 08 Jan 2026 05:10:48 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org, Zhao Liu Subject: [PATCH v2 01/16] rust/qobject: add basic bindings Date: Thu, 8 Jan 2026 14:10:28 +0100 Message-ID: <20260108131043.490084-2-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877960498158500 This is only a basic API, intended to be used by the serde traits. Co-authored-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- include/qobject/qobject.h | 5 +- rust/util/wrapper.h | 7 + rust/util/meson.build | 6 +- rust/util/src/lib.rs | 4 + rust/util/src/qobject/mod.rs | 331 +++++++++++++++++++++++++++++++++++ 5 files changed, 350 insertions(+), 3 deletions(-) create mode 100644 rust/util/src/qobject/mod.rs diff --git a/include/qobject/qobject.h b/include/qobject/qobject.h index 02f4c6a6eb2..567da7b6c51 100644 --- a/include/qobject/qobject.h +++ b/include/qobject/qobject.h @@ -35,7 +35,10 @@ #include "qemu/atomic.h" #include "qapi/qapi-builtin-types.h" =20 -/* Not for use outside include/qobject/ */ +/* + * Not for use outside include/qobject/ (and Rust bindings, when they + * have to redo inline functions here). + */ struct QObjectBase_ { QType type; size_t refcnt; diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h index b9ed68a01d8..0907dd59142 100644 --- a/rust/util/wrapper.h +++ b/rust/util/wrapper.h @@ -30,3 +30,10 @@ typedef enum memory_order { #include "qemu/log.h" #include "qemu/module.h" #include "qemu/timer.h" +#include "qobject/qnull.h" +#include "qobject/qbool.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" +#include "qobject/qobject.h" +#include "qobject/qlist.h" +#include "qobject/qdict.h" diff --git a/rust/util/meson.build b/rust/util/meson.build index 98629394afb..28593286134 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -37,8 +37,10 @@ _util_rs =3D static_library( 'src/prelude.rs', 'src/timer.rs', ], - {'.': _util_bindings_inc_rs} - ), + {'.': _util_bindings_inc_rs, + 'qobject': [ + 'src/qobject/mod.rs', + ]}), dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs], ) =20 diff --git a/rust/util/src/lib.rs b/rust/util/src/lib.rs index 7d2de3ed811..96334466895 100644 --- a/rust/util/src/lib.rs +++ b/rust/util/src/lib.rs @@ -9,6 +9,10 @@ // for prelude-like modules #[rustfmt::skip] pub mod prelude; + +#[macro_use] +pub mod qobject; + pub mod timer; =20 pub use error::{Error, Result, ResultExt}; diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs new file mode 100644 index 00000000000..5744870ad1c --- /dev/null +++ b/rust/util/src/qobject/mod.rs @@ -0,0 +1,331 @@ +//! `QObject` bindings +//! +//! This module implements bindings for QEMU's `QObject` data structure. +//! The bindings integrate with `serde`, which take the role of visitors +//! in Rust code. + +#![deny(clippy::unwrap_used)] + +use std::{ + cell::UnsafeCell, + ffi::{c_char, CString}, + mem::ManuallyDrop, + ptr::{addr_of, addr_of_mut}, + sync::atomic::{AtomicUsize, Ordering}, +}; + +use common::assert_field_type; + +use crate::bindings; + +/// A wrapper for a C `QObject`. +/// +/// Because `QObject` is not thread-safe, the safety of these bindings +/// right now hinges on treating them as immutable. It is part of the +/// contract with the `QObject` constructors that the Rust struct is +/// only built after the contents are stable. +/// +/// Only a bare bones API is public; production and consumption of `QObjec= t` +/// generally goes through `serde`. +pub struct QObject(&'static UnsafeCell); + +// SAFETY: the QObject API are not thread-safe other than reference counti= ng; +// but the Rust struct is only created once the contents are stable, and +// therefore it obeys the aliased XOR mutable invariant. +unsafe impl Send for QObject {} +unsafe impl Sync for QObject {} + +// Since a QObject can be a floating-point value, and potentially a NaN, +// do not implement Eq +impl PartialEq for QObject { + fn eq(&self, other: &Self) -> bool { + unsafe { bindings::qobject_is_equal(self.0.get(), other.0.get()) } + } +} + +impl QObject { + /// Construct a [`QObject`] from a C `QObjectBase` pointer. + /// The caller cedes its reference to the returned struct. + /// + /// # Safety + /// + /// The `QObjectBase` must not be changed from C code while + /// the Rust `QObject` lives + const unsafe fn from_base(p: *const bindings::QObjectBase_) -> Self { + QObject(unsafe { &*p.cast() }) + } + + /// Construct a [`QObject`] from a C `QObject` pointer. + /// The caller cedes its reference to the returned struct. + /// + /// # Safety + /// + /// The `QObject` must not be changed from C code while + /// the Rust `QObject` lives + pub const unsafe fn from_raw(p: *const bindings::QObject) -> Self { + QObject(unsafe { &*p.cast() }) + } + + /// Obtain a raw C pointer from a reference. `self` is consumed + /// without decreasing the reference count; therefore, the reference + /// is transferred to the `*mut bindings::QObject`. + pub fn into_raw(self) -> *mut bindings::QObject { + let src =3D ManuallyDrop::new(self); + src.0.get() + } + + /// Construct a [`QObject`] from a C `QObjectBase` pointer. + /// The caller *does not* cede its reference to the returned struct. + /// + /// # Safety + /// + /// The `QObjectBase` must not be changed from C code while + /// the Rust `QObject` lives + unsafe fn cloned_from_base(p: *const bindings::QObjectBase_) -> Self { + let orig =3D unsafe { ManuallyDrop::new(QObject::from_base(p)) }; + (*orig).clone() + } + + /// Construct a [`QObject`] from a C `QObject` pointer. + /// The caller *does not* cede its reference to the returned struct. + /// + /// # Safety + /// + /// The `QObject` must not be changed from C code while + /// the Rust `QObject` lives + pub unsafe fn cloned_from_raw(p: *const bindings::QObject) -> Self { + let orig =3D unsafe { ManuallyDrop::new(QObject::from_raw(p)) }; + (*orig).clone() + } + + fn refcnt(&self) -> &AtomicUsize { + assert_field_type!(bindings::QObjectBase_, refcnt, usize); + let qobj =3D self.0.get(); + unsafe { AtomicUsize::from_ptr(addr_of_mut!((*qobj).base.refcnt)) } + } +} + +/// Rust equivalent of the C `QOBJECT` macro; for internal use only, becau= se +/// all access should go through `From` (which already returns [`QObject`] +/// or serde. +macro_rules! qobject { + ($qobj:expr) =3D> {{ + let qobj: &bindings::QObjectBase_ =3D &$qobj.base; + // SAFETY: this `let` guarantees that either $qobj is a reference + // (not a raw pointer), or we're in an outer unsafe block + unsafe { QObject::from_base(qobj) } + }}; +} + +impl From<()> for QObject { + fn from(_null: ()) -> Self { + // Conversion of the C inline `qnull` function + unsafe { QObject::cloned_from_base(addr_of!(bindings::qnull_.base)= ) } + } +} + +impl From> for QObject +where + QObject: From, +{ + fn from(o: Option) -> Self { + o.map_or_else(|| ().into(), Into::into) + } +} + +impl From for QObject { + fn from(b: bool) -> Self { + let qobj =3D unsafe { &*bindings::qbool_from_bool(b) }; + qobject!(qobj) + } +} + +macro_rules! impl_from_return_qnum_int { + ($t:ty) =3D> { + impl From<$t> for QObject { + fn from(n: $t) -> Self { + let qobj =3D unsafe { &*bindings::qnum_from_int(n.into()) = }; + qobject!(qobj) + } + } + }; +} + +impl_from_return_qnum_int!(i8); +impl_from_return_qnum_int!(i16); +impl_from_return_qnum_int!(i32); +impl_from_return_qnum_int!(i64); + +macro_rules! impl_from_return_qnum_uint { + ($t:ty) =3D> { + impl From<$t> for QObject { + fn from(n: $t) -> Self { + let qobj =3D unsafe { &*bindings::qnum_from_uint(n.into())= }; + qobject!(qobj) + } + } + }; +} + +impl_from_return_qnum_uint!(u8); +impl_from_return_qnum_uint!(u16); +impl_from_return_qnum_uint!(u32); +impl_from_return_qnum_uint!(u64); + +macro_rules! impl_from_return_qnum_double { + ($t:ty) =3D> { + impl From<$t> for QObject { + fn from(n: $t) -> Self { + let qobj =3D unsafe { &*bindings::qnum_from_double(n.into(= )) }; + qobject!(qobj) + } + } + }; +} + +impl_from_return_qnum_double!(f32); +impl_from_return_qnum_double!(f64); + +impl From for QObject { + fn from(s: CString) -> Self { + let qobj =3D unsafe { &*bindings::qstring_from_str(s.as_ptr()) }; + qobject!(qobj) + } +} + +impl FromIterator for QObject +where + Self: From, +{ + fn from_iter>(it: I) -> Self { + let qlist =3D unsafe { &mut *bindings::qlist_new() }; + for elem in it { + let elem: QObject =3D elem.into(); + let elem =3D elem.into_raw(); + unsafe { + bindings::qlist_append_obj(qlist, elem); + } + } + qobject!(qlist) + } +} + +impl FromIterator<(CString, A)> for QObject +where + Self: From, +{ + fn from_iter>(it: I) -> Self { + let qdict =3D unsafe { &mut *bindings::qdict_new() }; + for (key, val) in it { + let val: QObject =3D val.into(); + let val =3D val.into_raw(); + unsafe { + bindings::qdict_put_obj(qdict, key.as_ptr().cast::= (), val); + } + } + qobject!(qdict) + } +} + +impl Clone for QObject { + fn clone(&self) -> Self { + self.refcnt().fetch_add(1, Ordering::Acquire); + QObject(self.0) + } +} + +impl Drop for QObject { + fn drop(&mut self) { + if self.refcnt().fetch_sub(1, Ordering::Release) =3D=3D 1 { + unsafe { + bindings::qobject_destroy(self.0.get()); + } + } + } +} + +#[allow(unused)] +macro_rules! match_qobject { + (@internal ($qobj:expr) =3D> + $(() =3D> $unit:expr,)? + $(bool($boolvar:tt) =3D> $bool:expr,)? + $(i64($i64var:tt) =3D> $i64:expr,)? + $(u64($u64var:tt) =3D> $u64:expr,)? + $(f64($f64var:tt) =3D> $f64:expr,)? + $(CStr($cstrvar:tt) =3D> $cstr:expr,)? + $(QList($qlistvar:tt) =3D> $qlist:expr,)? + $(QDict($qdictvar:tt) =3D> $qdict:expr,)? + $(_ =3D> $other:expr,)? + ) =3D> { + loop { + let qobj_ =3D $qobj.0.get(); + match unsafe { &* qobj_ }.base.type_ { + $($crate::bindings::QTYPE_QNULL =3D> break $unit,)? + $($crate::bindings::QTYPE_QBOOL =3D> break { + let qbool__: *mut $crate::bindings::QBool =3D qobj_.ca= st(); + let $boolvar =3D unsafe { (&*qbool__).value }; + $bool + },)? + $crate::bindings::QTYPE_QNUM =3D> { + let qnum__: *mut $crate::bindings::QNum =3D qobj_.cast= (); + let qnum__ =3D unsafe { &*qnum__ }; + match qnum__.kind { + $crate::bindings::QNUM_I64 | + $crate::bindings::QNUM_U64 | + $crate::bindings::QNUM_DOUBLE =3D> {} + _ =3D> { + panic!("unreachable"); + } + } + + match qnum__.kind { + $($crate::bindings::QNUM_I64 =3D> break { + let $i64var =3D unsafe { qnum__.u.i64_ }; + $i64 + },)? + $($crate::bindings::QNUM_U64 =3D> break { + let $u64var =3D unsafe { qnum__.u.u64_ }; + $u64 + },)? + $($crate::bindings::QNUM_DOUBLE =3D> break { + let $f64var =3D unsafe { qnum__.u.dbl }; + $f64 + },)? + _ =3D> {} // evaluate $other + } + }, + $($crate::bindings::QTYPE_QSTRING =3D> break { + let qstring__: *mut $crate::bindings::QString =3D qobj= _.cast(); + let $cstrvar =3D unsafe { ::core::ffi::CStr::from_ptr(= (&*qstring__).string) }; + $cstr + },)? + $($crate::bindings::QTYPE_QLIST =3D> break { + let qlist__: *mut $crate::bindings::QList =3D qobj_.ca= st(); + let $qlistvar =3D unsafe { &*qlist__ }; + $qlist + },)? + $($crate::bindings::QTYPE_QDICT =3D> break { + let qdict__: *mut $crate::bindings::QDict =3D qobj_.ca= st(); + let $qdictvar =3D unsafe { &*qdict__ }; + $qdict + },)? + _ =3D> () + }; + $(break $other;)? + #[allow(unreachable_code)] + { + panic!("unreachable"); + } + } + }; + + // first cleanup the syntax a bit, checking that there's at least + // one pattern and always adding a trailing comma + (($qobj:expr) =3D> + $($type:tt$(($val:tt))? =3D> $code:expr ),+ $(,)?) =3D> { + match_qobject!(@internal ($qobj) =3D> + $($type $(($val))? =3D> $code,)+) + }; +} +#[allow(unused_imports)] +use match_qobject; --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877899; cv=none; d=zohomail.com; s=zohoarc; b=QL1MIBm+VyyLP7F4FVxg1EUBwXNJnmJyWUEARNyWKYnufWqzjbR2NOqbdOm73p33MEORahBuipK0TN2W52nO99Q0EH+8PYIgbD2Uo7uusIVbX4qAiNSxHCK+d6XilOZYuLGAIpsHm1xhz5IZK5u4lANb99QPQBp1XBTEBo75Izs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877899; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=YAEznkDRO4+UCmKzsUCKS74cgfmdVqQv4A3+zQl8jFo=; b=n7oFocZfLvTu4mIFWW2PXZUiVY3IoDxa5bxgrq/Y7qEWJbBWAB5Rc4gaTJg3QKhXdwuXVOnwcgXDPkX+pKT7RKGkvp/3GSjQA8J0Ni4LKMNXK5NXtiHG6mbEVZpCp8Fxkg5akp4UWd/aOswy9jdKx3J5lxSQntf54cH3FmbZZAs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877899531526.1772619947428; Thu, 8 Jan 2026 05:11:39 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpn1-0007Ws-Ra; Thu, 08 Jan 2026 08:10:59 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpn0-0007Vx-Eg for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:10:58 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpmy-0007y5-82 for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:10:58 -0500 Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-649-bH1Q87k-PbOe_1q-CP-TlA-1; Thu, 08 Jan 2026 08:10:53 -0500 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-4775e00b16fso21652115e9.2 for ; Thu, 08 Jan 2026 05:10:53 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d7f653c78sm160551165e9.11.2026.01.08.05.10.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:10:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877855; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=YAEznkDRO4+UCmKzsUCKS74cgfmdVqQv4A3+zQl8jFo=; b=ArpuZLN0Jisd+mxDvBSUvlCLwYc1fLClzQApwmvd6QCs594EzJYWYbv6id+57LoNFVETHV sXCGHZnbT59le/XciSK5D9+l7qPGWK2bWbb1MrqhFhFpDMptg6++sTiLMfIveNZxb9nd7/ 0w14ebDZLz5OuGMmTb8sw5Sns9bol10= X-MC-Unique: bH1Q87k-PbOe_1q-CP-TlA-1 X-Mimecast-MFC-AGG-ID: bH1Q87k-PbOe_1q-CP-TlA_1767877853 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877852; x=1768482652; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=YAEznkDRO4+UCmKzsUCKS74cgfmdVqQv4A3+zQl8jFo=; b=fTQNpgvX1D/KGpTE/7XfUM/7mL/4dqn1OwL4UlzUlARHHZfKLLIK/BbWIiyDt8eRyx yiEzadyCsZGQZ92GpWq3xCr7hNeGRf2LHNt4UV5B7foh+JUykwHqe2YCLRjLErZmPPFN evzozyhVR9zllsj3HcFHDij2Q2OLirtcGjR/UExLNNg1SMMprpOkgT+5hg92IDcaVXHk pZ13WzawZMG2fMfPb6lb+kovrvX6cqsz15qH6KBaTxgjYshGODx7/YfnRq45j6eQp4fv 3nTXp0iJ2FoS8snDbaOdJ4U2yA90dswO1DoJNTEXqiQTw57LlrKN2BAo+hsdYNQmNymJ s2Tw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877852; x=1768482652; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=YAEznkDRO4+UCmKzsUCKS74cgfmdVqQv4A3+zQl8jFo=; b=huoSXLeDXslefHKoCst5p42UFm2Czgv+qGKgeuZS8dScp86JRyjKgN5qnoCxxuGQk6 y2Xn6Ewr3Ep2M7UutHKLSoDv5QgEtDhDUyBqPXWSWEVIvWSXFWebEIxIW4SfCCdt3obo Rem6sAb5RbMMgAk0napho+R7uUF15eF62Waqf/ei9/t1jMC3pfLSR9IhUwPvUGOXWm9H FKfX7OeN0plwSWTFmhj81KHRmgv9XqD4YUflmujZuDjE8rUQ2XAv4b+UABj65wfqC4Jc pbz8VkJUi6hek8Nm7CljXUhlBmEr/A8Qs/rADEbBrSfLn1NT3tYM4+HXM2Idc0lsQAEp 0K/Q== X-Gm-Message-State: AOJu0YzI/HSg5wuxKxLBJHwk2vIIetWRXO8wQQL0tJTGcqdXLYc9bDkf GI6Rs+IRN0+Ro7JuX0pN/esFRZBio10dgsZfTnyLMadI754Cy3wrkMkkwCI2FQgPPAYHRbBoDrj 9yINu8WdusfR7SsgkS8ZxJJYHr5omL1bH8eZRcSpgia4i0K43xgfG7RtHUJSRfSOXpTk3NMTJFL 7OD38oE53X8dBp9cDhijDsH2lOi0LmMHGDRdMH+wgZ X-Gm-Gg: AY/fxX75pO18gqpuen9EllClD/CFtt/oPmQ7okdnFUhNRs5cokQpSupohoaO+6fmc2g 9wxt+I1Bq+5ZBO9iGOQnDODD3azu97A8hAK1eI9gbHYc7XVvujsW7Zk+l5WeF3Ez5Go+LvLX+YG k5+7Gvh9Wj6PBSIvuOcMgBbgVKy7lA3rihS6E9H962Y4Pw3PvXtRRdv59YK2OxAZEisWAZElyB8 WPwv+x6DekNzic+OLa4DU1cqKPA3MGxPkYpDU/2UBqDGN6+6Lb+YYBkKXks1sQZziJit3QiUwRw GEz6O9V7+qFRwLQONS90bB2JDS/8Q3f1L4YOsOann25ESsuEvWxDZs/Xf1WKOTtMDtGISOkNA1w thf9Fu5r2IxKWTosBezYObDUSv5rtwx0a2TpF7zOpMqKLMIRpuLt5otevOTuass7WpfAl4S4tq/ 1yIxDxCZ7G24AwlQ== X-Received: by 2002:a05:600c:4ed4:b0:471:d2f:7987 with SMTP id 5b1f17b1804b1-47d84b40ae5mr76149255e9.26.1767877851635; Thu, 08 Jan 2026 05:10:51 -0800 (PST) X-Google-Smtp-Source: AGHT+IGYOicKeSLeYefTCpWT9Z2NpiRDd1XevMjr6Fzfg1sOcJ/hvgudOOyThEjDc9TERUfGj6LuGw== X-Received: by 2002:a05:600c:4ed4:b0:471:d2f:7987 with SMTP id 5b1f17b1804b1-47d84b40ae5mr76148835e9.26.1767877851090; Thu, 08 Jan 2026 05:10:51 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org, Zhao Liu Subject: [PATCH v2 02/16] subprojects: add serde Date: Thu, 8 Jan 2026 14:10:29 +0100 Message-ID: <20260108131043.490084-3-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877900155158500 Content-Type: text/plain; charset="utf-8" Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 2 ++ rust/meson.build | 4 +++ scripts/archive-source.sh | 3 ++ scripts/make-release | 2 +- subprojects/.gitignore | 3 ++ .../packagefiles/serde-1-rs/meson.build | 36 +++++++++++++++++++ .../packagefiles/serde-1.0.226-include.patch | 16 +++++++++ .../packagefiles/serde_core-1-rs/meson.build | 25 +++++++++++++ .../serde_core-1.0.226-include.patch | 15 ++++++++ .../serde_derive-1-rs/meson.build | 35 ++++++++++++++++++ .../serde_derive-1.0.226-include.patch | 11 ++++++ subprojects/serde-1-rs.wrap | 11 ++++++ subprojects/serde_core-1-rs.wrap | 11 ++++++ subprojects/serde_derive-1-rs.wrap | 11 ++++++ 14 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 subprojects/packagefiles/serde-1-rs/meson.build create mode 100644 subprojects/packagefiles/serde-1.0.226-include.patch create mode 100644 subprojects/packagefiles/serde_core-1-rs/meson.build create mode 100644 subprojects/packagefiles/serde_core-1.0.226-include.pat= ch create mode 100644 subprojects/packagefiles/serde_derive-1-rs/meson.build create mode 100644 subprojects/packagefiles/serde_derive-1.0.226-include.p= atch create mode 100644 subprojects/serde-1-rs.wrap create mode 100644 subprojects/serde_core-1-rs.wrap create mode 100644 subprojects/serde_derive-1-rs.wrap diff --git a/rust/Cargo.toml b/rust/Cargo.toml index ace0baf9bd7..45ebfa693a4 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -20,6 +20,8 @@ anyhow =3D "~1.0" foreign =3D "~0.3.1" libc =3D "0.2.162" glib-sys =3D { version =3D "0.21.2", features =3D ["v2_66"] } +serde =3D "1.0.226" +serde_derive =3D "1.0.226" =20 [workspace.lints.rust] unexpected_cfgs =3D { level =3D "deny", check-cfg =3D ['cfg(MESON)'] } diff --git a/rust/meson.build b/rust/meson.build index bacb7879102..bd995e2b955 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -11,6 +11,8 @@ subproject('foreign-0.3-rs', required: true) subproject('glib-sys-0.21-rs', required: true) subproject('libc-0.2-rs', required: true) subproject('probe-0.5-rs', required: true) +subproject('serde-1-rs', required: true) +subproject('serde_derive-1-rs', required: true) =20 anyhow_rs =3D dependency('anyhow-1-rs') bilge_rs =3D dependency('bilge-0.2-rs') @@ -19,6 +21,8 @@ foreign_rs =3D dependency('foreign-0.3-rs') glib_sys_rs =3D dependency('glib-sys-0.21-rs') libc_rs =3D dependency('libc-0.2-rs') probe_rs =3D dependency('probe-0.5-rs') +serde_rs =3D dependency('serde-1-rs') +serde_derive_rs =3D dependency('serde_derive-1-rs', native: true) =20 subproject('proc-macro2-1-rs', required: true) subproject('quote-1-rs', required: true) diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index a37acab524e..ae768cf99f6 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -46,6 +46,9 @@ subprojects=3D( proc-macro-error-attr-1-rs proc-macro2-1-rs quote-1-rs + serde-1-rs + serde_core-1-rs + serde_derive-1-rs syn-2-rs unicode-ident-1-rs ) diff --git a/scripts/make-release b/scripts/make-release index 5f54b0e7939..23fef08bcf3 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -44,7 +44,7 @@ SUBPROJECTS=3D"libvfio-user keycodemapdb berkeley-softflo= at-3 bilge-impl-0.2-rs either-1-rs foreign-0.3-rs itertools-0.11-rs libc-0.2-rs probe-0.5-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs - syn-2-rs unicode-ident-1-rs" + serde-1-rs serde_core-1-rs serde_derive-1-rs syn-2-rs unicode-ident-1-rs" =20 src=3D"$1" version=3D"$2" diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 011ce4dc3b7..2cdb0a6a910 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -21,6 +21,9 @@ /proc-macro-error-attr-* /proc-macro* /quote-* +/serde-* +/serde_core-* +/serde_derive-* /syn-* /unicode-ident-* =20 diff --git a/subprojects/packagefiles/serde-1-rs/meson.build b/subprojects/= packagefiles/serde-1-rs/meson.build new file mode 100644 index 00000000000..775e0120f24 --- /dev/null +++ b/subprojects/packagefiles/serde-1-rs/meson.build @@ -0,0 +1,36 @@ +project('serde-1-rs', 'rust', + meson_version: '>=3D1.5.0', + version: '1.0.226', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('serde_core-1-rs', required: true) +subproject('serde_derive-1-rs', required: true) + +serde_core_dep =3D dependency('serde_core-1-rs') +serde_derive_dep =3D dependency('serde_derive-1-rs') + +_serde_rs =3D static_library( + 'serde', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + '--cfg', 'feature=3D"alloc"', + '--cfg', 'feature=3D"std"', + '--cfg', 'feature=3D"derive"', + ], + dependencies: [ + serde_core_dep, + serde_derive_dep, + ] +) + +serde_dep =3D declare_dependency( + link_with: _serde_rs, + dependencies: serde_derive_dep, +) + +meson.override_dependency('serde-1-rs', serde_dep) diff --git a/subprojects/packagefiles/serde-1.0.226-include.patch b/subproj= ects/packagefiles/serde-1.0.226-include.patch new file mode 100644 index 00000000000..92878136878 --- /dev/null +++ b/subprojects/packagefiles/serde-1.0.226-include.patch @@ -0,0 +1,16 @@ +--- a/src/lib.rs 2025-09-23 13:41:09.327582205 +0200 ++++ b/src/lib.rs 2025-09-23 13:41:23.043271856 +0200 +@@ -241,7 +241,12 @@ + #[doc(hidden)] + mod private; +=20 +-include!(concat!(env!("OUT_DIR"), "/private.rs")); ++#[doc(hidden)] ++pub mod __private_MESON { ++ #[doc(hidden)] ++ pub use crate::private::*; ++} ++use serde_core::__private_MESON as serde_core_private; +=20 + // Re-export #[derive(Serialize, Deserialize)]. + // diff --git a/subprojects/packagefiles/serde_core-1-rs/meson.build b/subproj= ects/packagefiles/serde_core-1-rs/meson.build new file mode 100644 index 00000000000..79c36f6b70e --- /dev/null +++ b/subprojects/packagefiles/serde_core-1-rs/meson.build @@ -0,0 +1,25 @@ +project('serde_core-1-rs', 'rust', + meson_version: '>=3D1.5.0', + version: '1.0.226', + license: 'MIT OR Apache-2.0', + default_options: []) + +_serde_core_rs =3D static_library( + 'serde_core', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + '--cfg', 'feature=3D"alloc"', + '--cfg', 'feature=3D"result"', + '--cfg', 'feature=3D"std"', + ], +) + +serde_core_dep =3D declare_dependency( + link_with: _serde_core_rs, +) + +meson.override_dependency('serde_core-1-rs', serde_core_dep) diff --git a/subprojects/packagefiles/serde_core-1.0.226-include.patch b/su= bprojects/packagefiles/serde_core-1.0.226-include.patch new file mode 100644 index 00000000000..d1321dfe272 --- /dev/null +++ b/subprojects/packagefiles/serde_core-1.0.226-include.patch @@ -0,0 +1,15 @@ +--- a/src/lib.rs 2025-09-23 13:32:40.872421170 +0200 ++++ b/src/lib.rs 2025-09-23 13:32:52.181098856 +0200 +@@ -263,7 +263,11 @@ + pub use core::result::Result; + } +=20 +-include!(concat!(env!("OUT_DIR"), "/private.rs")); ++#[doc(hidden)] ++pub mod __private_MESON { ++ #[doc(hidden)] ++ pub use crate::private::*; ++} +=20 + #[cfg(all(not(feature =3D "std"), no_core_error))] + mod std_error; diff --git a/subprojects/packagefiles/serde_derive-1-rs/meson.build b/subpr= ojects/packagefiles/serde_derive-1-rs/meson.build new file mode 100644 index 00000000000..6c1001a844a --- /dev/null +++ b/subprojects/packagefiles/serde_derive-1-rs/meson.build @@ -0,0 +1,35 @@ +project('serde_derive-1-rs', 'rust', + meson_version: '>=3D1.5.0', + version: '1.0.226', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('quote-1-rs', required: true) +subproject('syn-2-rs', required: true) +subproject('proc-macro2-1-rs', required: true) + +quote_dep =3D dependency('quote-1-rs', native: true) +syn_dep =3D dependency('syn-2-rs', native: true) +proc_macro2_dep =3D dependency('proc-macro2-1-rs', native: true) + +rust =3D import('rust') + +_serde_derive_rs =3D rust.proc_macro( + 'serde_derive', + files('src/lib.rs'), + override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], + rust_args: [ + '--cap-lints', 'allow', + ], + dependencies: [ + quote_dep, + syn_dep, + proc_macro2_dep, + ], +) + +serde_derive_dep =3D declare_dependency( + link_with: _serde_derive_rs, +) + +meson.override_dependency('serde_derive-1-rs', serde_derive_dep) diff --git a/subprojects/packagefiles/serde_derive-1.0.226-include.patch b/= subprojects/packagefiles/serde_derive-1.0.226-include.patch new file mode 100644 index 00000000000..81d65564d29 --- /dev/null +++ b/subprojects/packagefiles/serde_derive-1.0.226-include.patch @@ -0,0 +1,11 @@ +--- a/src/lib.rs 2025-09-23 13:51:51.540191923 +0200 ++++ b/src/lib.rs 2025-09-23 13:52:07.690060195 +0200 +@@ -98,7 +98,7 @@ + impl private { + fn ident(&self) -> Ident { + Ident::new( +- concat!("__private", env!("CARGO_PKG_VERSION_PATCH")), ++ "__private_MESON", + Span::call_site(), + ) + } diff --git a/subprojects/serde-1-rs.wrap b/subprojects/serde-1-rs.wrap new file mode 100644 index 00000000000..56746dd0f43 --- /dev/null +++ b/subprojects/serde-1-rs.wrap @@ -0,0 +1,11 @@ +[wrap-file] +directory =3D serde-1.0.226 +source_url =3D https://crates.io/api/v1/crates/serde/1.0.226/download +source_filename =3D serde-1.0.226.0.tar.gz +source_hash =3D 0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b5= 1295fd +#method =3D cargo +diff_files =3D serde-1.0.226-include.patch +patch_directory =3D serde-1-rs + +# bump this version number on every change to meson.build or the patches: +# v1 diff --git a/subprojects/serde_core-1-rs.wrap b/subprojects/serde_core-1-rs= .wrap new file mode 100644 index 00000000000..3692e754935 --- /dev/null +++ b/subprojects/serde_core-1-rs.wrap @@ -0,0 +1,11 @@ +[wrap-file] +directory =3D serde_core-1.0.226 +source_url =3D https://crates.io/api/v1/crates/serde_core/1.0.226/download +source_filename =3D serde_core-1.0.226.0.tar.gz +source_hash =3D ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99= e966a4 +#method =3D cargo +diff_files =3D serde_core-1.0.226-include.patch +patch_directory =3D serde_core-1-rs + +# bump this version number on every change to meson.build or the patches: +# v1 diff --git a/subprojects/serde_derive-1-rs.wrap b/subprojects/serde_derive-= 1-rs.wrap new file mode 100644 index 00000000000..00c92dc79cf --- /dev/null +++ b/subprojects/serde_derive-1-rs.wrap @@ -0,0 +1,11 @@ +[wrap-file] +directory =3D serde_derive-1.0.226 +source_url =3D https://crates.io/api/v1/crates/serde_derive/1.0.226/downlo= ad +source_filename =3D serde_derive-1.0.226.0.tar.gz +source_hash =3D 8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b56917169= 9cdb33 +#method =3D cargo +diff_files =3D serde_derive-1.0.226-include.patch +patch_directory =3D serde_derive-1-rs + +# bump this version number on every change to meson.build or the patches: +# v1 --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877937; cv=none; d=zohomail.com; s=zohoarc; b=JUk7vf71S/k84guhdvY3x97gdgf5LWZtV8kGKnDxObKBHATbHlUl1fTB9Vec2HboBmTpIjphit0fNsce13HraoSWaeAeZgKliTLL6xxqVxbU1FCrGhGpQhuQnRSAUcHsDA8MXytkTyoMFo4yUXxyxRPZFmXhTsaQRCau0ppu8Ic= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877937; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=UqCrVvUX1EXb2s0loBz213vZBI1pR55uJOq0h9TgEQM=; b=oLhp3K4/JaIunfGPRhMI/5yvTSHjZeNuMj6uYOrPFhIH/nTiwqrk/b1uujkMABvRgn6ePbALNt4xEPhCxus9OrMXHH07IZuAIdfaIfnFefLdwzoR7XqiYbHSCT+paaMyoQ+tl3RIE93M8uTMNHET5bjTG49s7aBMAD7micELaYw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877937827481.2213970717995; Thu, 8 Jan 2026 05:12:17 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpn3-0007XX-C6; Thu, 08 Jan 2026 08:11:01 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpn1-0007We-N0 for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:10:59 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpmz-0007yR-Rk for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:10:59 -0500 Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-205-q3OO3Vm4OfqoFsZxHZ42Hw-1; Thu, 08 Jan 2026 08:10:56 -0500 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-47d4029340aso30791515e9.3 for ; Thu, 08 Jan 2026 05:10:55 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d8384646fsm118655955e9.15.2026.01.08.05.10.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:10:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877857; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UqCrVvUX1EXb2s0loBz213vZBI1pR55uJOq0h9TgEQM=; b=iqyG4m8lKobk+XdPwf0oqgGBS65sSLIlD4CGxcBxbrD7ToigjF/1r5uX8DP5QHtiMZ9Fei T9VF3lRZBfeYt6PAQmp9X8Rgbrncx0KwN0fVYAN7QvrHTjWoxPOJx7m2GCYuEafJ8RiBL+ nRnd87JNhqF1S4aqmvrOeyEfc+0pVyA= X-MC-Unique: q3OO3Vm4OfqoFsZxHZ42Hw-1 X-Mimecast-MFC-AGG-ID: q3OO3Vm4OfqoFsZxHZ42Hw_1767877855 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877854; x=1768482654; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UqCrVvUX1EXb2s0loBz213vZBI1pR55uJOq0h9TgEQM=; b=nAhVPVEviqooGCZabp5RWYFOZaeEF+Sr5+4zFTEhn/Dcey1hWHlxJC74H9icwrmcxL FAliysCfG98PMHIOZL3XmEt5Qm0oMC8FtcSpfNqjZe+qF/ewfPRqELiVQiujKbVRpuHX SYXAkge+gn/haMA2ry+gKnCckJzwfVgI6HEe0OPuDkLtcoGB48WaO8EoA0zonFazrDcO 6gPE9Vf9DGmHIpMESAFZ75jZC95F8WeS2wdG7ftacpxcjVdRCgkoifFhmjEpSJWvTlhH +ndvjM7kYVswHPoHlxPrbcznNBSe/6S0IN9qLyV6Aci6PQbeEiVyolikbzH+/6nsHnOG oxKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877854; x=1768482654; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=UqCrVvUX1EXb2s0loBz213vZBI1pR55uJOq0h9TgEQM=; b=h0RiWByuhtgZFe1BpEXorvQsWw8N3UhOizmDVf/Z/iUa6CZ1SS8gb8WppLxps7swZ6 NWt3paD1eN1DwDJH3Nrt97xCrHDDpDXrN3x8P6B+K/ovfBODWS4tBlSUaJMzQwvFikjR Kv/wHxYnzATL8OJHZYt0TKPFMvby8byz7ZFt+4V8hFEIfZ72SqkjnaVLZQdUSa84+ut9 ai/H5i7j8MrmQi7Cj99UGb5e2V+JCuYmUeU3ZbK5Zhtjqzxr5Dy/dyNdsEgjWY9zEMGR GPIjsElQoVk/9BJ1j3xhu7Ara+i8tATNQ4hxVzkXKOqHcS5WHDlbu06tZEM8q5Yzfbq8 q6Bw== X-Gm-Message-State: AOJu0Yzfs9E0gQ+zvjb7HK10oH6F1ZZHTt4V9X7Sx1TW5vonN3NBxPcW 1j5kcN6YV+31tsyycyFOxycbgu0ifMG2r1IBJ6fsUaVjO1p+rsTk51Ytk9YmCiSPut2+dp3tYKc +nnUmMPwTNVRY87+NlZmmBmLXzc+uCT2dcOszLrSAR9ZyaVSYe3kEazoiyRQTiDHAuQSUV/kqOM PoKRzvzVfPQX1szZZh2d+6U1o5eHS+UwVY0G5cV8lC X-Gm-Gg: AY/fxX4ORlcSC2ylCRJUeOJX9GM7iKWYbkhFlufamUcxzikrEu7XOLkMiaunQxU5qli W5xfih/WI2JUhXj6ZcGNpBYXG0wIoZVDkAAeLpxK7FpP902sEvbQaeyptc/+cSPujOvyrp3T/Z1 4tKZnJAn0NW9b8e4g0526tPUgkSvlu4CRy3ia7rbfswsuFsofuXj98ijJUKLXUNiFyh6o+FeWIG bbt+1i928MDqFwjbsA7axTTd8xp2zwPe/1w06k1Rv4IThtgNasbRyhwTYyGVfr0GQv38BQVByxd 3hsj8FtYl8iZPtFyq/5VyE6sQXibzXH8BKZUN/RwfPuFrKbG6SrmD0Uo4csjgTqEkLd8Drs0mfC A3XO0w+Md3uiV2VnmYagRO0ivP1dwr5PrSus+MoozXx/wsYKj1vX8e+fjJSwqtVUdwKVSQRo0Io GgviRLyIoY9Uj+WA== X-Received: by 2002:a05:600c:4e49:b0:476:4efc:8ed4 with SMTP id 5b1f17b1804b1-47d84b172bcmr73726855e9.11.1767877854085; Thu, 08 Jan 2026 05:10:54 -0800 (PST) X-Google-Smtp-Source: AGHT+IGw4iQ0MovB5pjL581zvBOb6KCYC+7Qdo6ESflVbfDkVmmWAPAC8XTWbr5jesFGfAiDcesXQg== X-Received: by 2002:a05:600c:4e49:b0:476:4efc:8ed4 with SMTP id 5b1f17b1804b1-47d84b172bcmr73726575e9.11.1767877853571; Thu, 08 Jan 2026 05:10:53 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org, Zhao Liu Subject: [PATCH v2 03/16] rust/qobject: add Serialize implementation Date: Thu, 8 Jan 2026 14:10:30 +0100 Message-ID: <20260108131043.490084-4-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877938204158500 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=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 1 + rust/util/Cargo.toml | 1 + rust/util/meson.build | 3 +- rust/util/src/qobject/mod.rs | 4 +- rust/util/src/qobject/serialize.rs | 59 ++++++++++++++++++++++++++++++ 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 rust/util/src/qobject/serialize.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 42ae857fe53..b6c9f934140 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -453,6 +453,7 @@ dependencies =3D [ "foreign", "glib-sys", "libc", + "serde", ] =20 [[package]] diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml index 85f91436545..554004816eb 100644 --- a/rust/util/Cargo.toml +++ b/rust/util/Cargo.toml @@ -17,6 +17,7 @@ anyhow =3D { workspace =3D true } foreign =3D { workspace =3D true } glib-sys =3D { workspace =3D true } libc =3D { workspace =3D true } +serde =3D { workspace =3D true } common =3D { path =3D "../common" } =20 [lints] diff --git a/rust/util/meson.build b/rust/util/meson.build index 28593286134..166a792bce7 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -40,8 +40,9 @@ _util_rs =3D static_library( {'.': _util_bindings_inc_rs, 'qobject': [ 'src/qobject/mod.rs', + 'src/qobject/serialize.rs', ]}), - dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs], + dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, s= erde_rs], ) =20 util_rs =3D declare_dependency(link_with: [_util_rs], dependencies: [qemuu= til, qom]) diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs index 5744870ad1c..7b9f0516fe1 100644 --- a/rust/util/src/qobject/mod.rs +++ b/rust/util/src/qobject/mod.rs @@ -6,6 +6,8 @@ =20 #![deny(clippy::unwrap_used)] =20 +mod serialize; + use std::{ cell::UnsafeCell, ffi::{c_char, CString}, @@ -244,7 +246,6 @@ fn drop(&mut self) { } } =20 -#[allow(unused)] macro_rules! match_qobject { (@internal ($qobj:expr) =3D> $(() =3D> $unit:expr,)? @@ -327,5 +328,4 @@ macro_rules! match_qobject { $($type $(($val))? =3D> $code,)+) }; } -#[allow(unused_imports)] use match_qobject; diff --git a/rust/util/src/qobject/serialize.rs b/rust/util/src/qobject/ser= ialize.rs new file mode 100644 index 00000000000..34ec3847c1d --- /dev/null +++ b/rust/util/src/qobject/serialize.rs @@ -0,0 +1,59 @@ +//! `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(&self, serializer: S) -> Result + where + S: ::serde::Serializer, + { + match_qobject! { (self) =3D> + () =3D> serializer.serialize_unit(), + bool(b) =3D> serializer.serialize_bool(b), + i64(i) =3D> serializer.serialize_i64(i), + u64(u) =3D> serializer.serialize_u64(u), + f64(f) =3D> serializer.serialize_f64(f), + CStr(cstr) =3D> cstr.to_str().map_or_else( + |_| Err(ser::Error::custom("invalid UTF-8 in QString")), + |s| serializer.serialize_str(s), + ), + QList(l) =3D> { + let mut node_ptr =3D unsafe { l.head.tqh_first }; + let mut state =3D serializer.serialize_seq(None)?; + while !node_ptr.is_null() { + let node =3D unsafe { &*node_ptr }; + let elem =3D unsafe { ManuallyDrop::new(QObject::from_= raw(addr_of!(*node.value))) }; + state.serialize_element(&*elem)?; + node_ptr =3D unsafe { node.next.tqe_next }; + } + state.end() + }, + QDict(d) =3D> { + let mut state =3D serializer.serialize_map(Some(d.size))?; + let mut e_ptr =3D unsafe { bindings::qdict_first(d) }; + while !e_ptr.is_null() { + let e =3D unsafe { &*e_ptr }; + let key =3D unsafe { CStr::from_ptr(e.key) }; + key.to_str().map_or_else( + |_| Err(ser::Error::custom("invalid UTF-8 in key")= ), + |k| state.serialize_key(k), + )?; + let value =3D unsafe { ManuallyDrop::new(QObject::from= _raw(addr_of!(*e.value))) }; + state.serialize_value(&*value)?; + e_ptr =3D unsafe { bindings::qdict_next(d, e) }; + } + state.end() + } + } + } +} --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877989; cv=none; d=zohomail.com; s=zohoarc; b=kF8ExHw3RH6J3jHiq4Ooika3pRGpAtqvkUi7/NDrXeHkyQJBtwjcKqFsGf1xXgsve2ViGbh9eWZRmgaWKoDsH5oRsESz7IDqtWGhWblCd3SQOnkuyLsXlAtkyJRUQUUaVCMGA13UzusucmUnpNFtp9dhyNGDfTPlAGGbD6Yx020= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877989; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=YpHKlKmUC0n2lAzUf3AvxLx4w3ae82IHyL9uR8odyos=; b=iVSTvnlQ6s8UXe0t8GCp59n3dbPSSe33NqUTUJsB3kSNy5Y/zcpto+dGGxBRKsfo57xsEvCiNH2W2F8PvndqF17oc4MgZv4v2k7wYFBmAmfB0asiKy2fFj6yuJEWO2mSQXUla3D5HmGwzS4R2FH5NwreevZ8u1843sL4L6qs7pE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877989463986.3075403747486; Thu, 8 Jan 2026 05:13:09 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpn8-0007Yx-4N; Thu, 08 Jan 2026 08:11:06 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpn6-0007Y6-3i for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:04 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpn3-0007zB-Js for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:03 -0500 Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-96-YePMl8PCO7yQ6pEIvv_PhQ-1; Thu, 08 Jan 2026 08:10:59 -0500 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-477a1e2b372so29183885e9.2 for ; Thu, 08 Jan 2026 05:10:59 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d7f410c6csm163464275e9.1.2026.01.08.05.10.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:10:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877860; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=YpHKlKmUC0n2lAzUf3AvxLx4w3ae82IHyL9uR8odyos=; b=V5R3Z45otGNWKXw+N58NLoXoBmlLbZY+yHi2fkrGMDFWFehLjLyD3i9GFUbjQy4Ux6F18V LVk71rhDZ3GzRGG1OkcR/ihpmV769ArSE51zr+R77NiWquNQnqjqTeypqFwXBtkm2QVAUm n74SwMYqMzgiJn4Ym+m1T3O0Vm65PeY= X-MC-Unique: YePMl8PCO7yQ6pEIvv_PhQ-1 X-Mimecast-MFC-AGG-ID: YePMl8PCO7yQ6pEIvv_PhQ_1767877858 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877857; x=1768482657; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=YpHKlKmUC0n2lAzUf3AvxLx4w3ae82IHyL9uR8odyos=; b=YRhDWZmfK1oniJSqctnjw7oDLQQlFxCYe6+4/V5EuebKq+M2cUuOdZM0R9KtSgYSF/ 9rfzLtMundHx4yIyIvV54BOj92gpWx4QNA3Qg1QU43xsn8IdhnTG+1D/CYhJD51ZRv+O SnmLH0GpPrh7T/7BbjSAj426NT1htmynyN8R06arIxkFg2qDixdcPJfBTyisvKR9Q7om zQXFKM3dHvrVwiX2c+Ie7S6AlLKJnoSUQd9T0+fqw2MQx80kJ/bSkDJG75MggsX6obYS 6pSpP3yuOt+gsfZkFYFcGBFryTkBiMfZJlC/MBQhWcR/CE1YkvAphEJq8iigSWgDiTeP 2r0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877857; x=1768482657; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=YpHKlKmUC0n2lAzUf3AvxLx4w3ae82IHyL9uR8odyos=; b=mRE8R9/D1RDfgIywS/9OFMBK+baLrmz9VOxr2OKgqEyLJh3FV8WaUs37RQcgLVgLAd 5YhzbYSC//b/X8G8H++vI6ktZjI9xdvBZHBgxQZ0HT/m0+veZy7J/1JN9U9kjNBilXWW Q5EdUkHZlSEMxiag+6QU/pXQU41N6ybknVnk4XDgKZnyg1lFXFH1fGwQdbQntdX5lOSa BpXN1OJng10F7JU78P7pEzViW6m2EjfBtwUGRhFB1sSDAuV1azizZ3VRPUZNZNXayVnh I49VoUCnVAAIyGkxIhJnTlIrRc1PfCDwexJ6Wh9984mgFqfm3DMSagj6nFTNCPyMWJRq Zf9Q== X-Gm-Message-State: AOJu0Ywqd1HGibTOuNMyyG3dsMNJzt6+kMm3GCiqDPDN+5Niq6zdT1K+ 3f6SjVPhH6EJFUg43zFy/VBNmKdmEuP8bv+w5DTaRV8bSx31sDS2Jv21GJp+LxhdAVvRMXe03If 00k2DYlJcDpd4nx3clhtLk96GStGlWb/PfKj+Jccr92MOMqsoNkK540U+n1SlUnNOtrakKvU5Wr +SXSVCikDpk6Spxm5+frqkTklhUrrROkckqJ0xiG+z X-Gm-Gg: AY/fxX4HUjgPPJyfZuCvBQ0WlqSoT/D65WLOM2sjlpF4/Laj6KYDCtMidNDC5fG9rMm 7s+adcJqb1ZNY2P7C5J9ofNjVKhGJMyUF2sQ9Au7X0enbVR/SLh/S1hf2kvYV4kbdYrYDVBjgcw h1QLr1ywIfaKseO9igFMnAJKddr3tZiTVxy3fWHxU8/E3Peizk2rXh6oKeq4Lm6bkGsdFkU/0Wu AippfnJSBA+X30sD3afSgF4uxv7qzAvywOmi4rDboIibJirtmswOABvpcsfTpFfzVWIy7O1zgf1 i/IioJYJFTk84I8AQM6eokzj7cfX+pOrquufCSXqipmNDOuk+X6TyNyileyGOlWQ081ciXnbygD kWolIvzV4DcNn1t6jQ+F3GOhXu9eTfm5wDLiKOJxvLpxnHY7IMYYxOL9UQCBB/jjWPbrzXUQw36 ATapOK9ZAT8ADZrQ== X-Received: by 2002:a05:600c:4f87:b0:475:da1a:5418 with SMTP id 5b1f17b1804b1-47d84b0a9a4mr71528835e9.1.1767877856858; Thu, 08 Jan 2026 05:10:56 -0800 (PST) X-Google-Smtp-Source: AGHT+IHB7RVwDZiZiXLewpJi/QauvNkDnOFhGX2dW8lPTZWh6hjZ4I9zouh5VsD6nUrJziLeiTvDjQ== X-Received: by 2002:a05:600c:4f87:b0:475:da1a:5418 with SMTP id 5b1f17b1804b1-47d84b0a9a4mr71528295e9.1.1767877856268; Thu, 08 Jan 2026 05:10:56 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org, Zhao Liu Subject: [PATCH v2 04/16] rust/qobject: add Serializer (to_qobject) implementation Date: Thu, 8 Jan 2026 14:10:31 +0100 Message-ID: <20260108131043.490084-5-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877990407158500 This allows creating QObject from any serializable data structure. Co-authored-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/util/meson.build | 2 + rust/util/src/qobject/error.rs | 52 +++ rust/util/src/qobject/mod.rs | 4 + rust/util/src/qobject/serializer.rs | 585 ++++++++++++++++++++++++++++ 4 files changed, 643 insertions(+) create mode 100644 rust/util/src/qobject/error.rs create mode 100644 rust/util/src/qobject/serializer.rs diff --git a/rust/util/meson.build b/rust/util/meson.build index 166a792bce7..7829364c09a 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -40,6 +40,8 @@ _util_rs =3D static_library( {'.': _util_bindings_inc_rs, 'qobject': [ 'src/qobject/mod.rs', + 'src/qobject/error.rs', + 'src/qobject/serializer.rs', 'src/qobject/serialize.rs', ]}), dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, s= erde_rs], diff --git a/rust/util/src/qobject/error.rs b/rust/util/src/qobject/error.rs new file mode 100644 index 00000000000..5212e65c4f7 --- /dev/null +++ b/rust/util/src/qobject/error.rs @@ -0,0 +1,52 @@ +//! Error data type for `QObject`'s `serde` integration + +use std::{ + ffi::NulError, + fmt::{self, Display}, + str::Utf8Error, +}; + +use serde::ser; + +#[derive(Debug)] +pub enum Error { + Custom(String), + KeyMustBeAString, + InvalidUtf8, + NulEncountered, + NumberOutOfRange, +} + +impl ser::Error for Error { + fn custom(msg: T) -> Self { + Error::Custom(msg.to_string()) + } +} + +impl From for Error { + fn from(_: NulError) -> Self { + Error::NulEncountered + } +} + +impl From for Error { + fn from(_: Utf8Error) -> Self { + Error::InvalidUtf8 + } +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Custom(msg) =3D> formatter.write_str(msg), + Error::KeyMustBeAString =3D> formatter.write_str("key must be = a string"), + Error::InvalidUtf8 =3D> formatter.write_str("invalid UTF-8 in = string"), + Error::NulEncountered =3D> formatter.write_str("NUL character = in string"), + Error::NumberOutOfRange =3D> formatter.write_str("number out o= f range"), + } + } +} + +impl std::error::Error for Error {} + +pub type Result =3D std::result::Result; diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs index 7b9f0516fe1..bfa3e9e34bb 100644 --- a/rust/util/src/qobject/mod.rs +++ b/rust/util/src/qobject/mod.rs @@ -6,7 +6,9 @@ =20 #![deny(clippy::unwrap_used)] =20 +mod error; mod serialize; +mod serializer; =20 use std::{ cell::UnsafeCell, @@ -17,6 +19,8 @@ }; =20 use common::assert_field_type; +pub use error::{Error, Result}; +pub use serializer::to_qobject; =20 use crate::bindings; =20 diff --git a/rust/util/src/qobject/serializer.rs b/rust/util/src/qobject/se= rializer.rs new file mode 100644 index 00000000000..08730855731 --- /dev/null +++ b/rust/util/src/qobject/serializer.rs @@ -0,0 +1,585 @@ +//! `QObject` serializer +//! +//! This module implements a [`Serializer`](serde::ser::Serializer) that +//! produces `QObject`s, allowing them to be created from serializable data +//! structures (such as primitive data types, or structs that implement +//! `Serialize`). + +use std::ffi::CString; + +use serde::ser::{Impossible, Serialize}; + +use super::{ + error::{Error, Result}, + QObject, +}; + +pub struct SerializeTupleVariant { + name: CString, + vec: Vec, +} + +impl serde::ser::SerializeTupleVariant for SerializeTupleVariant { + type Ok =3D QObject; + type Error =3D Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + self.vec.push(to_qobject(value)?); + Ok(()) + } + + fn end(self) -> Result { + let SerializeTupleVariant { name, vec, .. } =3D self; + + // TODO: insert elements one at a time + let list =3D QObject::from_iter(vec); + + // serde by default represents enums as a single-entry object, with + // the variant stored in the key ("external tagging"). Internal t= agging + // is implemented by the struct that requests it, not by the seria= lizer. + let map =3D [(name, list)]; + Ok(QObject::from_iter(map)) + } +} + +pub struct SerializeStructVariant { + name: CString, + vec: Vec<(CString, QObject)>, +} + +impl serde::ser::SerializeStructVariant for SerializeStructVariant { + type Ok =3D QObject; + type Error =3D Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Resu= lt<()> + where + T: ?Sized + Serialize, + { + self.vec.push((CString::new(key)?, to_qobject(value)?)); + Ok(()) + } + + fn end(self) -> Result { + // TODO: insert keys one at a time + let SerializeStructVariant { name, vec, .. } =3D self; + let list =3D QObject::from_iter(vec); + + // serde by default represents enums as a single-entry object, with + // the variant stored in the key ("external tagging"). Internal t= agging + // is implemented by the struct that requests it, not by the seria= lizer. + let map =3D [(name, list)]; + Ok(QObject::from_iter(map)) + } +} + +pub struct SerializeVec { + vec: Vec, +} + +impl serde::ser::SerializeSeq for SerializeVec { + type Ok =3D QObject; + type Error =3D Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + self.vec.push(to_qobject(value)?); + Ok(()) + } + + fn end(self) -> Result { + // TODO: insert elements one at a time + let SerializeVec { vec, .. } =3D self; + let list =3D QObject::from_iter(vec); + Ok(list) + } +} + +impl serde::ser::SerializeTuple for SerializeVec { + type Ok =3D QObject; + type Error =3D Error; + + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + serde::ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + serde::ser::SerializeSeq::end(self) + } +} + +impl serde::ser::SerializeTupleStruct for SerializeVec { + type Ok =3D QObject; + type Error =3D Error; + + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + serde::ser::SerializeSeq::serialize_element(self, value) + } + + fn end(self) -> Result { + serde::ser::SerializeSeq::end(self) + } +} + +struct MapKeySerializer; + +impl serde::Serializer for MapKeySerializer { + type Ok =3D CString; + type Error =3D Error; + + type SerializeSeq =3D Impossible; + type SerializeTuple =3D Impossible; + type SerializeTupleStruct =3D Impossible; + type SerializeTupleVariant =3D Impossible; + type SerializeMap =3D Impossible; + type SerializeStruct =3D Impossible; + type SerializeStructVariant =3D Impossible; + + #[inline] + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + Ok(CString::new(variant)?) + } + + #[inline] + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -= > Result + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + fn serialize_bool(self, _value: bool) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_i8(self, _value: i8) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_i16(self, _value: i16) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_i32(self, _value: i32) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_i64(self, _value: i64) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_i128(self, _value: i128) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_u8(self, _value: u8) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_u16(self, _value: u16) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_u32(self, _value: u32) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_u64(self, _value: u64) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_u128(self, _value: u128) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_f32(self, _value: f32) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_f64(self, _value: f64) -> Result { + Err(Error::KeyMustBeAString) + } + + #[inline] + fn serialize_char(self, _value: char) -> Result { + Err(Error::KeyMustBeAString) + } + + #[inline] + fn serialize_str(self, value: &str) -> Result { + Ok(CString::new(value)?) + } + + fn serialize_bytes(self, value: &[u8]) -> Result { + Ok(CString::new(value)?) + } + + fn serialize_unit(self) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result= { + Err(Error::KeyMustBeAString) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + Err(Error::KeyMustBeAString) + } + + fn serialize_none(self) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_some(self, _value: &T) -> Result + where + T: ?Sized + Serialize, + { + Err(Error::KeyMustBeAString) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(Error::KeyMustBeAString) + } + + fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<= Self::SerializeStruct> { + Err(Error::KeyMustBeAString) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(Error::KeyMustBeAString) + } +} + +pub struct SerializeMap { + vec: Vec<(CString, QObject)>, + next_key: Option, +} + +impl serde::ser::SerializeMap for SerializeMap { + type Ok =3D QObject; + type Error =3D Error; + + fn serialize_key(&mut self, key: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + self.next_key =3D Some(key.serialize(MapKeySerializer)?); + Ok(()) + } + + fn serialize_value(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + let key =3D self.next_key.take(); + // Panic because this indicates a bug in the program rather than an + // expected failure. + let key =3D key.expect("serialize_value called before serialize_ke= y"); + self.vec.push((key, to_qobject(value)?)); + Ok(()) + } + + fn end(self) -> Result { + // TODO: insert keys one at a time + let SerializeMap { vec, .. } =3D self; + Ok(QObject::from_iter(vec)) + } +} + +impl serde::ser::SerializeStruct for SerializeMap { + type Ok =3D QObject; + type Error =3D Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Resu= lt<()> + where + T: ?Sized + Serialize, + { + serde::ser::SerializeMap::serialize_entry(self, key, value) + } + + fn end(self) -> Result { + serde::ser::SerializeMap::end(self) + } +} + +/// Serializer whose output is a `QObject`. +/// +/// This is the serializer that backs [`to_qobject`]. +pub struct Serializer; + +impl serde::Serializer for Serializer { + type Ok =3D QObject; + type Error =3D Error; + type SerializeSeq =3D SerializeVec; + type SerializeTuple =3D SerializeVec; + type SerializeTupleStruct =3D SerializeVec; + type SerializeTupleVariant =3D SerializeTupleVariant; + type SerializeMap =3D SerializeMap; + type SerializeStruct =3D SerializeMap; + type SerializeStructVariant =3D SerializeStructVariant; + + #[inline] + fn serialize_bool(self, value: bool) -> Result { + Ok(value.into()) + } + + #[inline] + fn serialize_i8(self, value: i8) -> Result { + Ok(value.into()) + } + + #[inline] + fn serialize_i16(self, value: i16) -> Result { + Ok(value.into()) + } + + #[inline] + fn serialize_i32(self, value: i32) -> Result { + Ok(value.into()) + } + + fn serialize_i64(self, value: i64) -> Result { + Ok(value.into()) + } + + fn serialize_i128(self, value: i128) -> Result { + if let Ok(value) =3D u64::try_from(value) { + Ok(value.into()) + } else if let Ok(value) =3D i64::try_from(value) { + Ok(value.into()) + } else { + Err(Error::NumberOutOfRange) + } + } + + #[inline] + fn serialize_u8(self, value: u8) -> Result { + Ok(value.into()) + } + + #[inline] + fn serialize_u16(self, value: u16) -> Result { + Ok(value.into()) + } + + #[inline] + fn serialize_u32(self, value: u32) -> Result { + Ok(value.into()) + } + + #[inline] + fn serialize_u64(self, value: u64) -> Result { + Ok(value.into()) + } + + fn serialize_u128(self, value: u128) -> Result { + if let Ok(value) =3D u64::try_from(value) { + Ok(value.into()) + } else { + Err(Error::NumberOutOfRange) + } + } + + #[inline] + fn serialize_f32(self, float: f32) -> Result { + Ok(float.into()) + } + + #[inline] + fn serialize_f64(self, float: f64) -> Result { + Ok(float.into()) + } + + #[inline] + fn serialize_char(self, value: char) -> Result { + let mut s =3D String::new(); + s.push(value); + Ok(CString::new(s)?.into()) + } + + #[inline] + fn serialize_str(self, value: &str) -> Result { + Ok(CString::new(value)?.into()) + } + + fn serialize_bytes(self, value: &[u8]) -> Result { + // Serialize into a vector of numeric QObjects + let it =3D value.iter().copied(); + Ok(QObject::from_iter(it)) + } + + #[inline] + fn serialize_unit(self) -> Result { + Ok(().into()) + } + + #[inline] + fn serialize_unit_struct(self, _name: &'static str) -> Result= { + self.serialize_unit() + } + + #[inline] + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + self.serialize_str(variant) + } + + #[inline] + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -= > Result + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + // serde by default represents enums as a single-entry object, with + // the variant stored in the key ("external tagging"). Internal t= agging + // is implemented by the struct that requests it, not by the seria= lizer. + let map =3D [(CString::new(variant)?, to_qobject(value)?)]; + Ok(QObject::from_iter(map)) + } + + #[inline] + fn serialize_none(self) -> Result { + self.serialize_unit() + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeVec { + vec: Vec::with_capacity(len.unwrap_or(0)), + }) + } + + fn serialize_tuple(self, len: usize) -> Result { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_struct( + self, + _name: &'static str, + len: usize, + ) -> Result { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + Ok(SerializeTupleVariant { + name: CString::new(variant)?, + vec: Vec::with_capacity(len), + }) + } + + fn serialize_map(self, _len: Option) -> Result { + Ok(SerializeMap { + vec: Vec::new(), + next_key: None, + }) + } + fn serialize_struct(self, _name: &'static str, len: usize) -> Result { + self.serialize_map(Some(len)) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + Ok(SerializeStructVariant { + name: CString::new(variant)?, + vec: Vec::new(), + }) + } +} + +pub fn to_qobject(input: T) -> Result +where + T: Serialize, +{ + input.serialize(Serializer) +} --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877914; cv=none; d=zohomail.com; s=zohoarc; b=K1u17fIL1wQNFvaFNKOHOuj3daILDh/yM7Z7pY/iKsMuW5vJVIb4lKkBuWGhtQpVle7XrFPABjV3uwbzTwTetuBO3LstusQN2HxfOqWiEQtAAS6Bj9zbg4Z/gnODpMHAI9P4RsERW6U9+AqIsg1MuSLylm/oUf2Pqsj6U7oK0cc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877914; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=JUVnHGR4WXXh17z+7zB0yfXoO4945f0W6/gfPFC9GIg=; b=Wc3FhwYGk/X3kC2i4OCJANBTpeJcasPRXOtuz0iSuQ9eVqGJ/BqGEywkBJ7dP8cGG8QPcF3Q7/x+cDM3pWiwkVt7H+1kQdzpKJUGtUgOuUkyIfX0QOHLnPX8xLWoqqoUWbJuFlZcMWtIceOsN23FV31Yr7VgAyG4v0zCNWxo5uE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877914127688.9921753244766; Thu, 8 Jan 2026 05:11:54 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnB-0007Zm-4S; Thu, 08 Jan 2026 08:11:09 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpn8-0007Z4-TO for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:06 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpn7-0007zc-20 for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:06 -0500 Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-347-Nr89NWDLP4WbF_o-uDewLA-1; Thu, 08 Jan 2026 08:11:02 -0500 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-477b8a667bcso35632185e9.2 for ; Thu, 08 Jan 2026 05:11:02 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432bd5dfa07sm16274775f8f.25.2026.01.08.05.10.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:10:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877864; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=JUVnHGR4WXXh17z+7zB0yfXoO4945f0W6/gfPFC9GIg=; b=EyeHLM6EhiUJQeJmOc0eXKW8nHYa/zK6EOpese3V2+oqggeK6FBw3MYt7Lp8kAoXcszXky yKEUd19RHQqWYw/haA74DBVsDDiDTuN8WrVnzWXal+Lzepv/Ulwyd5sIBAf1uDkXP2WKYk ze6s4Anb0CpygGltPEuHrY2qn55+27g= X-MC-Unique: Nr89NWDLP4WbF_o-uDewLA-1 X-Mimecast-MFC-AGG-ID: Nr89NWDLP4WbF_o-uDewLA_1767877861 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877860; x=1768482660; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=JUVnHGR4WXXh17z+7zB0yfXoO4945f0W6/gfPFC9GIg=; b=G7IR4AzEb1a7DHVWhCKBjOTQOa8o4UN++u9y4C4EK9GDng/cL3zHfq7+VBukrKBfm5 hNhONEM/8Bl0IM5cDuylDJMhzDM+WAJTGgLEZHFaYCoEMhJbIENU26z8jvGk3olqBFOg zHA+RSoDE/64pWhgyEa3nrQUmQoksRaY73QW4ibEcTFJfuDg2Zgifb5jkf1xktgKNm0e +lxNggd2mO+FxRuxNyaRdHfWuG48OiKhKjCaY34ay9c2h+qy5UujyzbXQqcyvttJVEHa ssxI3I7Zozrwa90oMgbq9/kBGJvjTSdeNhn7qVglVa6JVvaMrM515JWqMGCqRK1NHoWN hwoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877860; x=1768482660; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=JUVnHGR4WXXh17z+7zB0yfXoO4945f0W6/gfPFC9GIg=; b=h/bGPB/cv5fYPHDxhv4qro2aFHbqC6t4EPU6huw/YIe0xIVrY0wJ+EuSYaucp8PfaX gZhh6gAGfjMqUOLZq3wRin/3KSfAWjL58AGWnTxil/Cq1SmKnJ+rQyRrxBSrV7j29tYg NZTMMBIJVMkTXSTrRRbMQygcDKcHlHS2DC3Lm2U1uFRICWW+X6LLWkJTbERVTCG+RU9c 33VMqHGJ457mmNIsmZU7sQ6yAoZIGHsYvRtfuxbeGhdRr+sb89N8r+bh8l/uRLRRK4Za xggaD+RkrrRmI2JBbCEtsL0fEPIjzoHmS1Zl6PuEYfO0Saul/XFomd9+W8gXzQmIi2wE SHsw== X-Gm-Message-State: AOJu0YyCgDmnDLLs/HwfyS9NKogzJBovyLEpBEuMhOX5tTY1uFzKV1HI 5N/j1+4CGnyBQrRgopjdsBLzyv4E7Fg2KXVb1qELyygXITp4IApNfVzgMrmgtKBueZvph5uNq0H +gAFX5ZpipEmAToyuqV0vuXVjxk7Ga0VeWJ+ZK1hzc1YgXBhAb8/WLlxcbzCtwmGltqiLte6uKl 8fHjOCjBlN5It+g10HYMGIODRAwuQex17sOG4/X7cH X-Gm-Gg: AY/fxX7OQFf/PFghyDlr55216x8K1ynATXIFSm+O4WH0nfN5n4Ci9J8ZaCAB9gAUvS5 QNPypCdGYUTGsQY98Uy/sspqGlntW58yg/2XEFqJ2MX6ZRLvjmfdOw53jdhSZIJPxlc78ZbuYpQ 56yWrFogtaXetFVqdmPi66Xpe3VKV2ywR6rqRogI0aLvAaXA1bYUBJhGLgJ3Utk0eYSw2K24js/ JWRSqSFYDM7z5Il4ZLPGVzU6UVre8yl7k8IdGsWReLBEL8nUiirW4QKyv2pTTAl6Gv2xelA9WUz Rled5V/2vjkU4rR2Ii/qk+ZVipDLnl/mX7AgeTbSV3NpGycD5DfV+t8HeHiM/rnL0t4B6ddxPzo YDVw9uP9hf3+C8iJnyIHpK5LIjfgzVbbRuGOm/AjQuYA8u3oE4/vGjIxW3KwA0eSCFs620ab+iU 25XbX48stvUGgr2A== X-Received: by 2002:a05:6000:2311:b0:42f:b707:56dd with SMTP id ffacd0b85a97d-432c37c878bmr8054237f8f.33.1767877860440; Thu, 08 Jan 2026 05:11:00 -0800 (PST) X-Google-Smtp-Source: AGHT+IHuepepjI/T31qaZs/s9jAakQ0H5Cxp4VxpWUG1QPLKG0DFtNRa1vnCXidolxpt29rZB0GTeQ== X-Received: by 2002:a05:6000:2311:b0:42f:b707:56dd with SMTP id ffacd0b85a97d-432c37c878bmr8054210f8f.33.1767877859960; Thu, 08 Jan 2026 05:10:59 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org, Zhao Liu Subject: [PATCH v2 05/16] rust/qobject: add Deserialize implementation Date: Thu, 8 Jan 2026 14:10:32 +0100 Message-ID: <20260108131043.490084-6-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877916067158500 This allows QObject to be created from any serializable format, for example JSON via serde_json. This is not too useful, since QObjects are produced by C code or by serializing structs, but it can be used for testing and it is part of the full implementation of a serde format. It is relatively simple and similar to the Serializer, just with the extra indirection of a Visitor. Co-authored-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/util/meson.build | 1 + rust/util/src/qobject/deserialize.rs | 134 +++++++++++++++++++++++++++ rust/util/src/qobject/mod.rs | 1 + 3 files changed, 136 insertions(+) create mode 100644 rust/util/src/qobject/deserialize.rs diff --git a/rust/util/meson.build b/rust/util/meson.build index 7829364c09a..4621932fdcb 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -40,6 +40,7 @@ _util_rs =3D static_library( {'.': _util_bindings_inc_rs, 'qobject': [ 'src/qobject/mod.rs', + 'src/qobject/deserialize.rs', 'src/qobject/error.rs', 'src/qobject/serializer.rs', 'src/qobject/serialize.rs', diff --git a/rust/util/src/qobject/deserialize.rs b/rust/util/src/qobject/d= eserialize.rs new file mode 100644 index 00000000000..280a577b6be --- /dev/null +++ b/rust/util/src/qobject/deserialize.rs @@ -0,0 +1,134 @@ +//! `QObject` deserialization +//! +//! This module implements the [`Deserialize`] trait for `QObject`, +//! allowing it to be created from any serializable format, for +//! example JSON. + +use core::fmt; +use std::ffi::CString; + +use serde::de::{self, Deserialize, MapAccess, SeqAccess, Visitor}; + +use super::{to_qobject, QObject}; + +impl<'de> Deserialize<'de> for QObject { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct ValueVisitor; + + impl<'de> Visitor<'de> for ValueVisitor { + type Value =3D QObject; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Re= sult { + formatter.write_str("any valid JSON value") + } + + #[inline] + fn visit_bool(self, value: bool) -> Result { + Ok(value.into()) + } + + #[inline] + fn visit_i64(self, value: i64) -> Result { + Ok(value.into()) + } + + fn visit_i128(self, value: i128) -> Result + where + E: serde::de::Error, + { + to_qobject(value).map_err(|_| de::Error::custom("number ou= t of range")) + } + + #[inline] + fn visit_u64(self, value: u64) -> Result { + Ok(value.into()) + } + + fn visit_u128(self, value: u128) -> Result + where + E: serde::de::Error, + { + to_qobject(value).map_err(|_| de::Error::custom("number ou= t of range")) + } + + #[inline] + fn visit_f64(self, value: f64) -> Result { + Ok(value.into()) + } + + #[inline] + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + CString::new(value) + .map_err(|_| de::Error::custom("NUL character in strin= g")) + .map(QObject::from) + } + + #[inline] + fn visit_string(self, value: String) -> Result + where + E: serde::de::Error, + { + CString::new(value) + .map_err(|_| de::Error::custom("NUL character in strin= g")) + .map(QObject::from) + } + + #[inline] + fn visit_none(self) -> Result { + Ok(().into()) + } + + #[inline] + fn visit_some(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Deserialize::deserialize(deserializer) + } + + #[inline] + fn visit_unit(self) -> Result { + Ok(().into()) + } + + #[inline] + fn visit_seq(self, mut visitor: V) -> Result + where + V: SeqAccess<'de>, + { + // TODO: insert elements one at a time + let mut vec =3D Vec::::new(); + + while let Some(elem) =3D visitor.next_element()? { + vec.push(elem); + } + Ok(QObject::from_iter(vec)) + } + + fn visit_map(self, mut visitor: V) -> Result + where + V: MapAccess<'de>, + { + // TODO: insert elements one at a time + let mut vec =3D Vec::<(CString, QObject)>::new(); + + if let Some(first_key) =3D visitor.next_key()? { + vec.push((first_key, visitor.next_value()?)); + while let Some((key, value)) =3D visitor.next_entry()?= { + vec.push((key, value)); + } + } + Ok(QObject::from_iter(vec)) + } + } + + deserializer.deserialize_any(ValueVisitor) + } +} diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs index bfa3e9e34bb..c0c16f28049 100644 --- a/rust/util/src/qobject/mod.rs +++ b/rust/util/src/qobject/mod.rs @@ -6,6 +6,7 @@ =20 #![deny(clippy::unwrap_used)] =20 +mod deserialize; mod error; mod serialize; mod serializer; --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877927; cv=none; d=zohomail.com; s=zohoarc; b=Ck7g3dFSjYir7AMeP2OfpdsVfC9IaRgGFrkaLJaqqhIzyldSNWMl5SOS1VGPQ8RNdUUQiyKVFT1WdAtYmoOTDMiPG2RlBXyP+bu4olF4aKLknDV2QzJIyLGvLGjyH00yk1oGoLGGaqVEeAm1YNArBUW1AmUeF25RxY6FusUZOIQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877927; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=IXUBvQcaMsRwI/b+BszJt6nG9jA/4L5XWgG62HdNUNU=; b=Tmzmy1VdtAh37l6cIYc2KdgoQIrbTFSNpDcHGu+gCzUO+IwhXuRwbAV2q9+gWu1X6d2I4FHSXUE2PayJbErxXTQJj43+G68+uc9q2++satFmFOIqBSSuhcVPknjk5k6WmIABP6dd/OTD959REbTLkZtEnDlaFqUTa2heTG+0Dtc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877927246562.3046350785336; Thu, 8 Jan 2026 05:12:07 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnF-0007bL-2k; Thu, 08 Jan 2026 08:11:13 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnC-0007aI-LU for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:10 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpn9-0007zz-1T for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:10 -0500 Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-33-54cSPiGTMjmCCos_NXjlbg-1; Thu, 08 Jan 2026 08:11:05 -0500 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-430fe16b481so1698356f8f.3 for ; Thu, 08 Jan 2026 05:11:04 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432bd5ee870sm16250175f8f.36.2026.01.08.05.11.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877866; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IXUBvQcaMsRwI/b+BszJt6nG9jA/4L5XWgG62HdNUNU=; b=bFFVQf6afoshEiar3hyvTrhGmGoCb333hrFrpYOuiHdkqh2capLzyRdqdOIjDJ4f3fHKYx jCTPUuZhJ4ID9l3T4aUEzOtUTHdr2HLIIMVu2gP4/rwqwz+1TjYCPEKaxZ6Kvhplszga0n L4Lsa41lQxgW5YCCnbS3QUiLA95+V4Q= X-MC-Unique: 54cSPiGTMjmCCos_NXjlbg-1 X-Mimecast-MFC-AGG-ID: 54cSPiGTMjmCCos_NXjlbg_1767877864 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877863; x=1768482663; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=IXUBvQcaMsRwI/b+BszJt6nG9jA/4L5XWgG62HdNUNU=; b=l3VKIsKPQmbLUcXFuvrYvnVM9e66gZcyT9irp7geYxTgKayjiJN/M8ILiyCW3zz3et sfGu/7jdfgbpbF/ID4htaTzm5YLugeg72AqW3z2NJnzFQ693wlyrkkx0Ggc9+zPFWtSE 59yUc6vmDoPIQKwgjoE0OG6GN1ra8NyJ+fF43D4elLlM8Pru3sDwX6PFSFZhJIMjzuQX ehMHdg3NNPb5XgjaN018ZywmDRRLwpy4hhjGKXwEs3nYHinxqnBETcDUILxBS3yL2b4L xv57xUgEjcRvBKlxo5N+9smvyDQ+NZ5/FEdaEf3MhlUccR3d+hPRXUs5Ccaw7X4IAkAw CjXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877863; x=1768482663; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=IXUBvQcaMsRwI/b+BszJt6nG9jA/4L5XWgG62HdNUNU=; b=jeUxM7s93pOfPO1Id1Bnr07dqO+xMZe6XTze5KVhpR9Uo8gbsMTNe8Iw/enk5Xpyos CBXN9vr/UeNmkXfeyKqU1f4pjHdJ/BnnT+9fiIyh2KuhIWwNzHieXr4/aM7omnlT6Myd iTJlZ/yW/yv3Vskcu5CHC47ERK0e5S9p2YWA8UmjLQHYReifMCw4mW8xW8hLWtasctHA HVwHqo/Fj3J2VF1e6f9T1JiPlmPbSZvRXkhgZi80+PszPsiUnEjqF2PrR22Er8+vLX02 xDC2CLgGk4+F3x51sH/F7B4C9JKxEm4lCfVoMZawjGPHjqb8zp1FQiy+S7S9iJCJmT9s B1uw== X-Gm-Message-State: AOJu0YypZzSrCm8cW9jvHYkR4aLsNnEwexvk4ZJ2fZu55GhcqZFTGgoO seg8e0bpNwbgpV/oInsA9hqwkM+jllB8lvxpCSUun2My445PlkOyyCVW2F2SQawcXg9f81xbz3c GuqjD9R0R9OW17of50DZ4kd3icpY+p8RvYlnnDd7NdhdvW3cJynQerxlhKZmaDH86OSyY7ZX1LM ofolOxSW+acrWCgA5VBIsrQi90Y0Ipskz3XOiS0sbK X-Gm-Gg: AY/fxX7/qaJqItoWERJ7FEzQ1lhnpWSMkYj6ZWhP2Gp7mowOWO5hQesFbd8XGSHrn9a eiGx8CBySddCA2eG/PDfT1NW3LfN5NnpdznChvQcGXQglUXfyuVsbd/yhFkmZjLzgfbrAzY57VI 675yVWl7o1nmuZQoJUXH0a1/UwY12dflFxTTVrTHmtvMJ7G+Ip2P/I/sngaFncuRI4+HZwSKsaN IvXpl4bT7GWIaDVi9ufpV0ssg4qBPgOvoflZqDEgV2BrJ465/rz+XI1y8O6Cu/8orWu6eEHYDHt yYGhOc4OwfCQ8KjU9QRsjO/WPn/M6ycvEEdrP3O+js3zSwtbwB2wqbSvsPITsiNva7A1guWD0Sx E7UmbV+m1NtfrXpZSDS3sYjCdaXS/p6kC0k5H3dsTo7+yoGEKcR5GJJS3o3AzxwUp8aaxXTphJ2 vAFkFr15HXHgo6AA== X-Received: by 2002:a05:6000:240f:b0:431:864:d4a9 with SMTP id ffacd0b85a97d-432c3772035mr6376945f8f.8.1767877862939; Thu, 08 Jan 2026 05:11:02 -0800 (PST) X-Google-Smtp-Source: AGHT+IHMG9a3naAy2kMCjipFCfNO9dOnznFl+BkKm3U0MQO1tS54K1Cs53sXUMyf9MSr/updpapoLA== X-Received: by 2002:a05:6000:240f:b0:431:864:d4a9 with SMTP id ffacd0b85a97d-432c3772035mr6376911f8f.8.1767877862360; Thu, 08 Jan 2026 05:11:02 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org, Zhao Liu Subject: [PATCH v2 06/16] rust/qobject: add Deserializer (from_qobject) implementation Date: Thu, 8 Jan 2026 14:10:33 +0100 Message-ID: <20260108131043.490084-7-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877930076158500 This allows creating any serializable data structure from QObject. The purpose of all the code is to typecheck each variant in the serde data model and check that it's one of the corresponding QObject data types. Co-authored-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 1 + rust/util/meson.build | 1 + rust/util/src/qobject/deserializer.rs | 371 ++++++++++++++++++++++++++ rust/util/src/qobject/error.rs | 8 +- rust/util/src/qobject/mod.rs | 2 + 5 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 rust/util/src/qobject/deserializer.rs diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 79c26d9d165..c7f3a496a25 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -162,6 +162,7 @@ module status ``util::error`` stable ``util::log`` proof of concept ``util::module`` complete +``util::qobject`` stable ``util::timer`` stable =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 diff --git a/rust/util/meson.build b/rust/util/meson.build index 4621932fdcb..92d27957eda 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -40,6 +40,7 @@ _util_rs =3D static_library( {'.': _util_bindings_inc_rs, 'qobject': [ 'src/qobject/mod.rs', + 'src/qobject/deserializer.rs', 'src/qobject/deserialize.rs', 'src/qobject/error.rs', 'src/qobject/serializer.rs', diff --git a/rust/util/src/qobject/deserializer.rs b/rust/util/src/qobject/= deserializer.rs new file mode 100644 index 00000000000..b0f8aa23773 --- /dev/null +++ b/rust/util/src/qobject/deserializer.rs @@ -0,0 +1,371 @@ +//! `QObject` deserializer +//! +//! This module implements a [`Deserializer`](serde::de::Deserializer) that +//! consumes `QObject`s, allowing them to be turned into deserializable da= ta +//! structures (such as primitive data types, or structs that implement +//! `Deserialize`). + +use std::ffi::CStr; + +use serde::de::{ + self, value::StrDeserializer, DeserializeSeed, Expected, IntoDeseriali= zer, MapAccess, + SeqAccess, Unexpected, Visitor, +}; + +use super::{ + error::{Error, Result}, + match_qobject, QObject, +}; +use crate::bindings; + +impl QObject { + #[cold] + fn invalid_type(&self, exp: &dyn Expected) -> E + where + E: serde::de::Error, + { + serde::de::Error::invalid_type(self.unexpected(), exp) + } + + #[cold] + fn unexpected(&self) -> Unexpected<'_> { + match_qobject! { (self) =3D> + () =3D> Unexpected::Unit, + bool(b) =3D> Unexpected::Bool(b), + i64(n) =3D> Unexpected::Signed(n), + u64(n) =3D> Unexpected::Unsigned(n), + f64(n) =3D> Unexpected::Float(n), + CStr(s) =3D> s.to_str().map_or_else( + |_| Unexpected::Other("string with invalid UTF-8"), + Unexpected::Str), + QList(_) =3D> Unexpected::Seq, + QDict(_) =3D> Unexpected::Map, + } + } +} + +fn visit_qlist_ref<'de, V>(qlist: &'de bindings::QList, visitor: V) -> Res= ult +where + V: Visitor<'de>, +{ + struct QListDeserializer(*mut bindings::QListEntry, usize); + + impl<'de> SeqAccess<'de> for QListDeserializer { + type Error =3D Error; + + fn next_element_seed(&mut self, seed: T) -> Result> + where + T: DeserializeSeed<'de>, + { + if self.0.is_null() { + return Ok(None); + } + + let e =3D unsafe { &*self.0 }; + // increment the reference count because deserialize consumes = `value`. + let value =3D unsafe { QObject::cloned_from_raw(e.value.cast_c= onst()) }; + let result =3D seed.deserialize(value); + self.0 =3D unsafe { e.next.tqe_next }; + self.1 +=3D 1; + result.map(Some) + } + } + + let mut deserializer =3D QListDeserializer(unsafe { qlist.head.tqh_fir= st }, 0); + let seq =3D visitor.visit_seq(&mut deserializer)?; + if deserializer.0.is_null() { + Ok(seq) + } else { + Err(serde::de::Error::invalid_length( + deserializer.1, + &"fewer elements in array", + )) + } +} + +fn visit_qdict_ref<'de, V>(qdict: &'de bindings::QDict, visitor: V) -> Res= ult +where + V: Visitor<'de>, +{ + struct QDictDeserializer(*mut bindings::QDict, *const bindings::QDictE= ntry); + + impl<'de> MapAccess<'de> for QDictDeserializer { + type Error =3D Error; + + fn next_key_seed(&mut self, seed: T) -> Result> + where + T: DeserializeSeed<'de>, + { + if self.1.is_null() { + return Ok(None); + } + + let e =3D unsafe { &*self.1 }; + let key =3D unsafe { CStr::from_ptr(e.key) }; + let key_de =3D StrDeserializer::new(key.to_str()?); + seed.deserialize(key_de).map(Some) + } + + fn next_value_seed(&mut self, seed: T) -> Result + where + T: DeserializeSeed<'de>, + { + if self.1.is_null() { + panic!("next_key must have returned None"); + } + + let e =3D unsafe { &*self.1 }; + // increment the reference count because deserialize consumes = `value`. + let value =3D unsafe { QObject::cloned_from_raw(e.value) }; + let result =3D seed.deserialize(value); + self.1 =3D unsafe { bindings::qdict_next(self.0, self.1) }; + result + } + } + + let qdict =3D (qdict as *const bindings::QDict).cast_mut(); + let e =3D unsafe { bindings::qdict_first(qdict) }; + let mut deserializer =3D QDictDeserializer(qdict, e); + let map =3D visitor.visit_map(&mut deserializer)?; + if deserializer.1.is_null() { + Ok(map) + } else { + Err(serde::de::Error::invalid_length( + unsafe { bindings::qdict_size(qdict) }, + &"fewer elements in map", + )) + } +} + +fn visit_qnum_ref<'de, V>(qnum: QObject, visitor: V) -> Result +where + V: Visitor<'de>, +{ + match_qobject! { (qnum) =3D> + i64(n) =3D> visitor.visit_i64(n), + u64(n) =3D> visitor.visit_u64(n), + f64(n) =3D> visitor.visit_f64(n), + _ =3D> Err(qnum.invalid_type(&"number")), + } +} + +macro_rules! deserialize_number { + ($method:ident) =3D> { + fn $method(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visit_qnum_ref(self, visitor) + } + }; +} + +impl<'de> serde::Deserializer<'de> for QObject { + type Error =3D Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + () =3D> visitor.visit_unit(), + bool(v) =3D> visitor.visit_bool(v), + i64(n) =3D> visitor.visit_i64(n), + u64(n) =3D> visitor.visit_u64(n), + f64(n) =3D> visitor.visit_f64(n), + CStr(cstr) =3D> visitor.visit_str(cstr.to_str()?), + QList(qlist) =3D> visit_qlist_ref(qlist, visitor), + QDict(qdict) =3D> visit_qdict_ref(qdict, visitor), + } + } + + deserialize_number!(deserialize_i8); + deserialize_number!(deserialize_i16); + deserialize_number!(deserialize_i32); + deserialize_number!(deserialize_i64); + deserialize_number!(deserialize_i128); + deserialize_number!(deserialize_u8); + deserialize_number!(deserialize_u16); + deserialize_number!(deserialize_u32); + deserialize_number!(deserialize_u64); + deserialize_number!(deserialize_u128); + deserialize_number!(deserialize_f32); + deserialize_number!(deserialize_f64); + + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + () =3D> visitor.visit_none(), + _ =3D> visitor.visit_some(self), + } + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + CStr(cstr) =3D> visitor.visit_enum(cstr.to_str()?.into_deseria= lizer()), + _ =3D> Err(self.invalid_type(&"string")), + } + } + + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V= ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + bool(v) =3D> visitor.visit_bool(v), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + CStr(cstr) =3D> visitor.visit_str(cstr.to_str()?), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + CStr(cstr) =3D> visitor.visit_str(cstr.to_str()?), + QList(qlist) =3D> visit_qlist_ref(qlist, visitor), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_byte_buf(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_bytes(visitor) + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + () =3D> visitor.visit_unit(), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -= > Result + where + V: Visitor<'de>, + { + self.deserialize_unit(visitor) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + QList(qlist) =3D> visit_qlist_ref(qlist, visitor), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_map(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + QDict(qdict) =3D> visit_qdict_ref(qdict, visitor), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_struct( + self, + _name: &'static str, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + QList(qlist) =3D> visit_qlist_ref(qlist, visitor), + QDict(qdict) =3D> visit_qdict_ref(qdict, visitor), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_unit() + } +} + +pub fn from_qobject(value: QObject) -> Result +where + T: de::DeserializeOwned, +{ + T::deserialize(value) +} diff --git a/rust/util/src/qobject/error.rs b/rust/util/src/qobject/error.rs index 5212e65c4f7..2d7c180187a 100644 --- a/rust/util/src/qobject/error.rs +++ b/rust/util/src/qobject/error.rs @@ -6,7 +6,7 @@ str::Utf8Error, }; =20 -use serde::ser; +use serde::{de, ser}; =20 #[derive(Debug)] pub enum Error { @@ -23,6 +23,12 @@ fn custom(msg: T) -> Self { } } =20 +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error::Custom(msg.to_string()) + } +} + impl From for Error { fn from(_: NulError) -> Self { Error::NulEncountered diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs index c0c16f28049..b95dddddedc 100644 --- a/rust/util/src/qobject/mod.rs +++ b/rust/util/src/qobject/mod.rs @@ -7,6 +7,7 @@ #![deny(clippy::unwrap_used)] =20 mod deserialize; +mod deserializer; mod error; mod serialize; mod serializer; @@ -20,6 +21,7 @@ }; =20 use common::assert_field_type; +pub use deserializer::from_qobject; pub use error::{Error, Result}; pub use serializer::to_qobject; =20 --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767878008; cv=none; d=zohomail.com; s=zohoarc; b=aIJwgz0leGYzwCtKhTzwXZ++ZXyG5A35cFC4D2ifMizj7oAQkjQHF2lZY8mb+mxQ9loBSewpTjn0iixUtw8TOPAApZvUbJSs2YlZPt2QItnlX5xxcdaE0moVMsqRVre7AOTBXEAqfjX+njc61L7e2tHKW5X6Vm0rGGSkzEwIrsc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767878008; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=24uu20SH0A5RmWohW/685UZSc7h5TVZr0Shnp1cHoEY=; b=O0kg7FzmxoP2dbpHCMy99J2A7MGAzPAx0VlG66XBSHrPhRH4wwa54Y2qo7hm+ZbNCazWTylps2VWS6xCZsHo80K7HOisSJLfQVo9IgMcdtjUQ+MOw4EkIZ6VoVP0wYDNO2eHMo8G+ZTRYRYWy29zkwZGooZvfvKJzjx5b9YDklQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767878008919631.6354826879565; Thu, 8 Jan 2026 05:13:28 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnI-0007cv-An; Thu, 08 Jan 2026 08:11:16 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnG-0007bt-67 for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:14 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnD-00080h-8D for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:13 -0500 Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-613-bARp6NvaOYmmnuwfE-roWQ-1; Thu, 08 Jan 2026 08:11:07 -0500 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-4325aa61c6bso2068210f8f.0 for ; Thu, 08 Jan 2026 05:11:07 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432d286cdecsm2534047f8f.7.2026.01.08.05.11.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877870; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=24uu20SH0A5RmWohW/685UZSc7h5TVZr0Shnp1cHoEY=; b=LSkOWSoUfrVj4Xc59Tg9x5jp//4rxr/XNRzILhLJbF5HbHtxgNi9dO/QwaN7BbUJlHa6Ny OAKkxNZ10fkmq2nRwrzi7iw4pLdHFOggd6PMp/nWR5OpgmVgqmWVluK0R6SU/YFn+AxTmr 8bFdVAcfozcc7jjWVufohXaHDlNyBb4= X-MC-Unique: bARp6NvaOYmmnuwfE-roWQ-1 X-Mimecast-MFC-AGG-ID: bARp6NvaOYmmnuwfE-roWQ_1767877867 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877865; x=1768482665; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=24uu20SH0A5RmWohW/685UZSc7h5TVZr0Shnp1cHoEY=; b=ao1Pnfb501s1ruzHhNzZLdZfU3J9ggAgvsVSGNFE4BuZkjpb4BOegpjmjvRgn16PaI 5tViTECPDQBWKg5iCi9QFg5TAIBnIiGrS9XLxQC6WHoUp59b7fveqf8/U/RLCQEIKtTW c6O3aqk/sLn8OqmOXxSZUGkY8t/wT8Z1nQrJgdespIRO35a8av6BxAwBAnGstVV0W3bN EO0/QxjNbyZLAwnQ74p3TgnEoxSs7YIcfcug87GQvckBbJ8YGy4ut8uKvtCp1xDejjO4 3COMhsXYEtSRI64Xjs+ENtIL41Ngs6Dx25S2Cx2Dy6iDh/YG9k1zNst+YtI5nw/JFDJG 0png== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877865; x=1768482665; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=24uu20SH0A5RmWohW/685UZSc7h5TVZr0Shnp1cHoEY=; b=YsSauTAQ0IiIaU90Qf5VBRRl+sEKdlQA65ODTMJDe2KDy/kWn3vrnvSho1wTVSSMCG Uc0zZSYydlkCfnNt/XKkZCeuBeWhUbZf6HYYpiCtwdfpUz4GhTvGApvIGmRle6tzJq3F cGvQENit94bIW+tBbi48a7ZJ7nkJDOto+qyG98BAoS5HrCM5uaHCrK+q5G3b1JtnmiXA P49hXZh7Rw+IjRZ/eNtECf1byk7rLMryppAHY+ckSEx8WWGhWCHAEncVVDacYyiq0PdT SAs6Zn+ll7cI/X8k3Wvddro1poN23JHmeEQrNeQLdIhC7bX+604RcW7IucodPBoYYI3T 2xFg== X-Gm-Message-State: AOJu0YxZ0fhiY65b6w8h/QMa0c1HV1ArnTb6LZyrsOsc0EnmINSvGXB2 g9Xtdm2mQ5ao7fyuIMMbuI7JvXWBwRAaQbIsZiEfnbI4+qHNE6NLe91ZZ5v6BD+K5Jo8oDuUa9o PYZPYgp2uQzdJovwRvhH4VcZT7mbhwDEMXCKSZPeVtUYcIvSgkIn/37ANuVpj/HKFJ/YyBq1W14 EqkgnDjmU8Dx+X9OKnT0OP3NOBhCUugvf8r8/Q24Qy X-Gm-Gg: AY/fxX6NBjdmcSCYLi2q8XvV69weRqEYE7R0j0lbNXnsSg8+j2NS2AKsIP8ekv+6fXh ShHFMX/PxX/Lc2g2OfDWqXfKn9D/VZQ9LkaQ9L2YkQ/v6wejRxiMdbWHdusskHe2Vohw7QAMKj/ I8mSbcyL4d3149J7DmmhyDbsvgPNtayjaywL1DgC62qMKWZ0+YnNVbMOuIB2KTwvX1Za2h2RHEA 1GyCXl99FtjCTcORn9W6LUNKowqTvv4Vo4I5O21hIRqsJDORnnC7U7zV5I3TymwEbz0e44wyDwT yZ4cKy31MvGJMs6XRl/J5L8pacCnTQjMkOOzGTwF+kfoV/Cv/o4WQV6ivo+XDUnI6F2TDiwKHNY 6O8lY45uQ86jbUvkVQajJ8IUHRo+0osetqr/QfTUp5O53jpbOBj8hNVTlZ2yxNCTuXiRPoWtog6 URlwdqisu/0qG2Ew== X-Received: by 2002:a05:6000:4024:b0:431:808:2d50 with SMTP id ffacd0b85a97d-432c3628270mr7910121f8f.13.1767877865223; Thu, 08 Jan 2026 05:11:05 -0800 (PST) X-Google-Smtp-Source: AGHT+IGvittNCdbmpfD2E/UnLYDT9eFkNlLTgyzUxBRwqQaAyJ/ztD9DUB9Z6TDup7+DvYUKcUJ6Uw== X-Received: by 2002:a05:6000:4024:b0:431:808:2d50 with SMTP id ffacd0b85a97d-432c3628270mr7910093f8f.13.1767877864765; Thu, 08 Jan 2026 05:11:04 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 07/16] rust/qobject: add from/to JSON bindings for QObject Date: Thu, 8 Jan 2026 14:10:34 +0100 Message-ID: <20260108131043.490084-8-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767878010557158500 These are used by tests. However it could even be an idea to use serde_json + transcoding and get rid of the C version... Co-authored-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu --- rust/util/wrapper.h | 1 + rust/util/src/qobject/mod.rs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h index 0907dd59142..c88820a5e5b 100644 --- a/rust/util/wrapper.h +++ b/rust/util/wrapper.h @@ -37,3 +37,4 @@ typedef enum memory_order { #include "qobject/qobject.h" #include "qobject/qlist.h" #include "qobject/qdict.h" +#include "qobject/qjson.h" diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs index b95dddddedc..95d41cec4f1 100644 --- a/rust/util/src/qobject/mod.rs +++ b/rust/util/src/qobject/mod.rs @@ -23,6 +23,7 @@ use common::assert_field_type; pub use deserializer::from_qobject; pub use error::{Error, Result}; +use foreign::prelude::*; pub use serializer::to_qobject; =20 use crate::bindings; @@ -112,6 +113,22 @@ fn refcnt(&self) -> &AtomicUsize { let qobj =3D self.0.get(); unsafe { AtomicUsize::from_ptr(addr_of_mut!((*qobj).base.refcnt)) } } + + pub fn to_json(&self) -> String { + let qobj =3D self.0.get(); + unsafe { + let json =3D bindings::qobject_to_json(qobj); + glib_sys::g_string_free(json, glib_sys::GFALSE).into_native() + } + } + + pub fn from_json(json: &str) -> std::result::Result { + let c_json =3D std::ffi::CString::new(json)?; + unsafe { + crate::Error::with_errp(|errp| bindings::qobject_from_json(c_j= son.as_ptr(), errp)) + .map(|qobj| QObject::from_raw(qobj)) + } + } } =20 /// Rust equivalent of the C `QOBJECT` macro; for internal use only, becau= se --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877899; cv=none; d=zohomail.com; s=zohoarc; b=AD5owiNopWrN50AbYljFdA6O7KhQuO/WcgJkHtTDhXtqvXJvQcUfCgZArO+ZEEYofwfmfu627oyids5XzMJ+E0ak9Vw5EV8y5V/MLtF+Ly9qamVWHcMGgAR/R5juVgxbKA/5oSzVy5/66W26mqhHKbBRqqnOHhSiJzUh8SocMM8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877899; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=b03v/X6J7oomQaGRutqWEu7f8gmtoiSOE8nhF1tA+Qc=; b=ZOt063zRkkueXKjcMvkiu1pmh5PVg4sZ1tzoDmZqgVD7snG2svCjNKISW8pK3AZ65sacDP0fOe5mSeeauV983Yoeq5iOMqR1ecUeabvxz9OX4ARfIR2FgMejvJzMj5gHWs4ceVUoshgo9jpiGH5OWkm8UgYHCct1JhTJNKhaaO4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877899867575.9532730297935; Thu, 8 Jan 2026 05:11:39 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnJ-0007e7-If; Thu, 08 Jan 2026 08:11:17 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnG-0007cE-RE for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:14 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnF-00080y-3j for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:14 -0500 Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-510-1zXU6DFqOXeVQe8My_ZNCw-1; Thu, 08 Jan 2026 08:11:10 -0500 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-4779edba8f3so27560635e9.3 for ; Thu, 08 Jan 2026 05:11:10 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d7f410c6csm163472975e9.1.2026.01.08.05.11.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877871; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=b03v/X6J7oomQaGRutqWEu7f8gmtoiSOE8nhF1tA+Qc=; b=Z8W4zuE5vNHIfPQarfldUrkYZgdy/50/1FfCXqcJMdPTMeRr0A1k45vAZDh5s0FXJ6RKTt MFNl+J5ilrELGZkVrmSCVXFITbnxrKX8GVidCXtzYYGzF5Vc376Fjbu/JRQ4m0/TTQvKpP IQKNzC1ROY2juLnRas5pNJcucQL4K+w= X-MC-Unique: 1zXU6DFqOXeVQe8My_ZNCw-1 X-Mimecast-MFC-AGG-ID: 1zXU6DFqOXeVQe8My_ZNCw_1767877869 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877868; x=1768482668; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=b03v/X6J7oomQaGRutqWEu7f8gmtoiSOE8nhF1tA+Qc=; b=rTh8ymSJsgLHYRvaHH5YhAZDBYHnj9jWJWrdkWNs8/MBtzqmCf7PkAQAwZIKVRgPaK e0tyKj5azH+BBgpYdoMQedvat+wad2rR73cFXL+vvRBOmdm7P6b9I+LnTAV+OH+4c8zd mRBRKzv7SV/6CzM4JrGdECn5buvN1lpUuuXGzRinEJgqSaAImIUnH0HMAPZNJvKd5aPn dsZhPveKX1dfy/Hj/XzhKkuDVEAtu0rcm1Fye4dcnOu5cHrJKGFPNmuZ64+FIcL241YG xjXpC6CWNWgcOX7JRIlVLQPzTB19YuLruL0/dtZW/LEbTcYx4BL4y4I0/+hwdgWYEomU q3FA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877868; x=1768482668; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=b03v/X6J7oomQaGRutqWEu7f8gmtoiSOE8nhF1tA+Qc=; b=mf4I4P/pGiwhw9zosQ03qLNcKe0aBbPcQ+ztO93lNT+kq+F+zfBeUNT/JYz7C9AwCX /MoFnGOlKyA3t95ZaDbN5C7Ohf48WTNS7JrnzZ7Ko0wQ4kAK21aTDxFDq+O2ruegdIR5 COkTOs8l0MyYCuqxUxgbxpsKgXEyNtddRpZKE0Y0eI2D8+RLCUzsDoqbt7g6Yq/GUtA0 KzCrGbULXGL+wUNMiE7qSAB1mKIgYsDSaQ3+RdUgI6ntKBj9rcT9DjCdEkAQKoYiIdFk d/c1jopUGDO9WfMbOnaMqhOMfhHLG9+aCp5B6bgjJ7KHsUwiCXg7s1RhERiAxs+lbs2J UDGw== X-Gm-Message-State: AOJu0YyVIKoHu+2eoD3jASNgsh3HVZc213sqKzGEmP17B8/+M/O48sBI YaRLQZLcmtDZUlEFPOqg36wMIwmSETi1cl47446swJCSUa+LhNvhXig+EHbM9Wo4B44Fs5tbRaC DStHKMTl3fudG7DsPi9JRHtU1DVyiyIwx3/tqbWsXkvDDLpOCth7XlxTGvRh0RgVc9u8BvHRK66 4cflt3dqTXPRYJwYGjK+oNv7pGAURUTEj7zPj66yPn X-Gm-Gg: AY/fxX7APQECUEuJicdkt5ZlXxvGDSaI4+nxgkVEVZ3DRr0aaByH3JYIMyu8H2fAsdr AhITd4LUjMgpcowhcGKPKGJJ06fQOZFJWhDxgCN80+QFHQ0efhDCTM94p6jotuGoga8H9XvYCEB LKcxkssAGz9nZaXtZz32x9K2/TvsvVk5jKHRX8k4vfnx6RQ5AOK0p6tcXiKAW2J4s5d49QuPC+k H2USPMXiOQrOmB5HSWCXys1nOl0qC7KwqBHGiOeN4VV9Re5LdR3GsAtiS4GtImW/ls2ItnxW03U lYA78UPiIkrmPBgNxIThJcAFUzq86ZkxA+Wp2hApY5H4yVbFn/efvEhAYIZr4r0bVJ78ATgCfbP IQI5P9aLYqY/n7ZLW/oC+FlNl+Z7HzThLCsIct+lV5t5ycwVuMM5fuEGo5jrrmS9B/sV9z6FX80 2c0wndqRCtbVkpXQ== X-Received: by 2002:a05:600c:444c:b0:477:9f34:17b8 with SMTP id 5b1f17b1804b1-47d84b0a9e5mr75664165e9.1.1767877868551; Thu, 08 Jan 2026 05:11:08 -0800 (PST) X-Google-Smtp-Source: AGHT+IGgeAw6U45pzrqLZ+eF+lmM3Y5HhM8mwGDLIgKhDNBeRWq37Htljn5SujciM9nPWOyXkRNf2g== X-Received: by 2002:a05:600c:444c:b0:477:9f34:17b8 with SMTP id 5b1f17b1804b1-47d84b0a9e5mr75663875e9.1.1767877868110; Thu, 08 Jan 2026 05:11:08 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 08/16] rust/qobject: add Display/Debug Date: Thu, 8 Jan 2026 14:10:35 +0100 Message-ID: <20260108131043.490084-9-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877902041158500 From: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu --- rust/util/src/qobject/mod.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs index 95d41cec4f1..8033b5fafa0 100644 --- a/rust/util/src/qobject/mod.rs +++ b/rust/util/src/qobject/mod.rs @@ -12,6 +12,7 @@ mod serialize; mod serializer; =20 +use core::fmt::{self, Debug, Display}; use std::{ cell::UnsafeCell, ffi::{c_char, CString}, @@ -270,6 +271,33 @@ fn drop(&mut self) { } } =20 +impl Display for QObject { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // replace with a plain serializer? + match_qobject! { (self) =3D> + () =3D> write!(f, "QNull"), + bool(b) =3D> write!(f, "QBool({})", if b { "true" } else { "fa= lse" }), + i64(n) =3D> write!(f, "QNumI64({})", n), + u64(n) =3D> write!(f, "QNumU64({})", n), + f64(n) =3D> write!(f, "QNumDouble({})", n), + CStr(s) =3D> write!(f, "QString({})", s.to_str().unwrap_or("ba= d CStr")), + QList(_) =3D> write!(f, "QList"), + QDict(_) =3D> write!(f, "QDict"), + } + } +} + +impl Debug for QObject { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let val =3D self.to_string(); + f.debug_struct("QObject") + .field("ptr", &self.0.get()) + .field("refcnt()", &self.refcnt()) + .field("to_string()", &val) + .finish() + } +} + macro_rules! match_qobject { (@internal ($qobj:expr) =3D> $(() =3D> $unit:expr,)? --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877985; cv=none; d=zohomail.com; s=zohoarc; b=ZOiRHeQO4xGIq2w8Pp3YgqH5plVmcnByY0iRRwWLa/gr3nA3p4Zd4GYpDI9XLIxbOr5oYR/VhAX/Xuew/kCQMoY7Kgggfl5HJ+M62iOgmpLXltim7eVeYNpyYhXbIYr8t7HwEtmBpCBQ3J77qvf9LiZT0sN3Z2W4C0zOkonCmmI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877985; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=0E5oaa3MQAn5B2LP1tawJrB7fiKlLEpjdtNPCmWmE88=; b=cV4HopU8AccMlrdekvplYsIpRdF2nA9JCqrzeU3srA4ygYY4bJtzvcQQxuRhNAbbaROGgMo2kqHuqW2SeU0KWVZjDgZJbPYhaCMq1rvD7xnuNPpG5aDZwHoahKKNTYEWH0lOTjlWYNyNrsgG42EoCtVxeNLAC3SApOVqt2o/lSs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877985680457.656461062631; Thu, 8 Jan 2026 05:13:05 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnX-00084q-Rl; Thu, 08 Jan 2026 08:11:31 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnV-0007uW-G4 for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:29 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnT-00083P-CL for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:29 -0500 Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-307-ELlq0_tvMayRkvZVl1v-vg-1; Thu, 08 Jan 2026 08:11:18 -0500 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-47d28e7960fso35947975e9.0 for ; Thu, 08 Jan 2026 05:11:16 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d87189e54sm36061825e9.12.2026.01.08.05.11.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877886; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0E5oaa3MQAn5B2LP1tawJrB7fiKlLEpjdtNPCmWmE88=; b=QrOcyNKcZxVWdsizXLHbmrHP2CavuAsb1Loqm+WpdGFvLiCZ2WiPrOvx8OWe4apyuPVcCf KIp5KdNG+7USERc8KJBR5oGeEPtBHhCfRJJ25ltGJOfCq79iv+e1f7rtlqmM1nxERu0aN8 Na95J1az4Wlwat7XxK8a+7Z7UsDChWM= X-MC-Unique: ELlq0_tvMayRkvZVl1v-vg-1 X-Mimecast-MFC-AGG-ID: ELlq0_tvMayRkvZVl1v-vg_1767877876 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877871; x=1768482671; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=0E5oaa3MQAn5B2LP1tawJrB7fiKlLEpjdtNPCmWmE88=; b=OJuYeurXGa+IkSAPshSs52qR7qKTfInqZXu86zUglLOSvYhn8l4Xs0d/DJQM3FYlGs bZqXxm6T8PPS5iM3ZATNAxPou88H8Q6GwfqX5rU+luabhiX7vXKRGl6aNG1j8AHSo2Fk FrHhhaTuGc6QFRjZitrCSKJN9D3/ZAMVgGwRgi56MlTDWe9MXSuAlMKuOAoY4jEM2heX Y5KSwBO2TjQA8dAxX4CtSsDzA1+BlEyZ4HxSVhDkGp1BvCklmtr+CP1kGaKiR5a8EB89 n2oS8vSOpMQwhIZHc8e2Wsi0uchtYxrTu1jiVDYOqfD+eRxgmit741zuI+Jk6JKW1fyt 9eAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877871; x=1768482671; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=0E5oaa3MQAn5B2LP1tawJrB7fiKlLEpjdtNPCmWmE88=; b=amAKsLL1B5OQgYStFumMH3b15zpsTBYYhPBCgC+/RLkXDCRZrjgFDl0/BjXHrs2fBk j5uErK86T6rpJeD7gLMkoFBo5r4P0F7WdoNkgGEfXry3Oh7HnGsoTt9ywwIQcJ2lDAo3 PFLkOhnSsSMqzuOVzKRpTU6lYg11e+FR1FyUWLkr/QfQgcXSQkt5hSa+q5L/1IhlpTIz HzXZ88oAmgzDeVd4q7ndRgSPeacNqhxqP3MGt1Xmzqw23vd/3/oN/lMe8f7TtnlMQmgF Zoc0Ve664c2UmzChx1OgGwTOpEIGKRLWaJ53TO8XraUvuK1AwTzygSvV1icFKVt4QQ9U sUYA== X-Gm-Message-State: AOJu0YwAaNrtSJDl9KBzaDzEUDpw0pOjNXEhKWswrfuvqQLvS7NBRiLy 8cNK2DnSIk3BJfW+oMDGvBBdxH9ooL3HtBEN5FAOVI9eAR+E9IglJGHvS0soa8ybQL6I6QZ6qUD BmgmXXSYjUmh4a26r/DYoAD+IJ76Q1dZ/ZqswZkUaj6QDOgFTM+QHJ5xiAXyYG5ClfNZOab8Iqq RY0ETYcoILybB4gMiXDKCEix4WfFSUAdyFjuXLnfNS X-Gm-Gg: AY/fxX5wdhY3xHAXg+m8mqcI8TLetzOQPilxcuNvY54HaVbGtrC3ZdiR2qJqUkgXP1j upo6UqpsqQB8azpUJgjSl101zPKpBJViq3y7wnewm3tn9tK7DAJaIz8Zp4QeLMlM9uxqXI5YMPK dBZ+Oo8S0xXDqCxeDnwgdputg2XJYqwl8HjCF8tFceMhoMA1Y95L24DEXC37EwjDp/n7Q3OR0sO HtWQJDO80H7zcfXp2s36ci0wo68+LC4aZoOvAZq6oi1PHjRYgWnIWTQK9f2cAw92eveFT2T3o9I nCt12uvHtiWyaRCc2IPw1tEoSEnj/QUbbE51HzxY4yVRR2Qh3uPgNmKitOEGXgnjJ++6Fqa+RGX fx7RoQZmKuG6ULlbOg5g/SnLM+pki1vP/0QoBkvAwuyG3MDJE68lOvkYbLkidpzLJRYHzsJFg5J MjVQDn8g+W9rvO/Q== X-Received: by 2002:a05:600c:3110:b0:47d:3ffa:980e with SMTP id 5b1f17b1804b1-47d84b4101dmr63206555e9.28.1767877871238; Thu, 08 Jan 2026 05:11:11 -0800 (PST) X-Google-Smtp-Source: AGHT+IEORBF3pH9Z8JBQv2ifgXj4MTlDYGlTPRH00T6XtzFVSNi6ECYe8urA1hTRN7/M++9RelRR7g== X-Received: by 2002:a05:600c:3110:b0:47d:3ffa:980e with SMTP id 5b1f17b1804b1-47d84b4101dmr63206255e9.28.1767877870746; Thu, 08 Jan 2026 05:11:10 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 09/16] scripts/qapi: add QAPISchemaIfCond.rsgen() Date: Thu, 8 Jan 2026 14:10:36 +0100 Message-ID: <20260108131043.490084-10-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877986417158500 From: Marc-Andr=C3=A9 Lureau Generate Rust #[cfg(...)] guards from QAPI 'if' conditions; it turns out that they are very similar, with both of them using not/any/all, so just walk the tree. The next commit will put it to use. Signed-off-by: Marc-Andr=C3=A9 Lureau Link: https://lore.kernel.org/r/20210907121943.3498701-15-marcandre.lureau@= redhat.com Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu --- scripts/qapi/common.py | 19 +++++++++++++++++++ scripts/qapi/schema.py | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index d7c8aa3365c..14d5dd259c4 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -199,6 +199,25 @@ def guardend(name: str) -> str: name=3Dc_fname(name).upper()) =20 =20 +def rsgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str: + + def cfg(ifcond: Union[str, Dict[str, Any]]) -> str: + if isinstance(ifcond, str): + return ifcond + assert isinstance(ifcond, dict) and len(ifcond) =3D=3D 1 + if 'not' in ifcond: + oper =3D 'not' + arg =3D cfg(ifcond['not']) + else: + oper, operands =3D next(iter(ifcond.items())) + arg =3D ', '.join([cfg(c) for c in operands]) + return f'{oper}({arg})' + + if not ifcond: + return '' + return '#[cfg(%s)]' % cfg(ifcond) + + def gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]], cond_fmt: str, not_fmt: str, all_operator: str, any_operator: str) -> str: diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 8d88b40de2e..848a7401251 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -37,6 +37,7 @@ docgen_ifcond, gen_endif, gen_if, + rsgen_ifcond, ) from .error import QAPIError, QAPISemError, QAPISourceError from .expr import check_exprs @@ -63,6 +64,9 @@ def gen_endif(self) -> str: def docgen(self) -> str: return docgen_ifcond(self.ifcond) =20 + def rsgen(self) -> str: + return rsgen_ifcond(self.ifcond) + def is_present(self) -> bool: return bool(self.ifcond) =20 --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877975; cv=none; d=zohomail.com; s=zohoarc; b=T7JCBEJaC4M7xtkwEHtE7hdztAzz8aPr0XWZXLweu+oIrb5HY6Xo3hEdEwZBcO3dP0iASdQR60SyqOTSQsfvIGCjEe4Lqu3cZ9wyfm4c1KH9BC0zFlKUSYtoy3X1OX/MWobLkKOwAN7yEmQ60KJO7aVWQusm/xnSHchBW0y0bf4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877975; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=uACPJ3krJUuogeqFQ/FMG+kdPSn5YGZfGjsX3Hw0XBU=; b=DwuzhZHKfu1r0v+kcGUSCjdKN7ZfOyf0wI2odcBFxKPKyN4ZPDsN4dsx46IUh6OwMgBIU7SOn/jBtoPc46RAsduTT/5GMQcGrjmGT6okrRYTARXqjgKSQt92DI9nyRhVenARnCiWsTVbsOrm02HggYwSB2+yNeyrqBLaucCo6Pg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877975269395.5210963537089; Thu, 8 Jan 2026 05:12:55 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnO-0007fV-Eo; Thu, 08 Jan 2026 08:11:22 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnM-0007ep-0t for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:20 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnK-00082H-LA for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:19 -0500 Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-42-H4epy9taP-SgW4d_Oq8RRA-1; Thu, 08 Jan 2026 08:11:16 -0500 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-477cf25ceccso29459255e9.0 for ; Thu, 08 Jan 2026 05:11:16 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d8384646fsm118672205e9.15.2026.01.08.05.11.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877877; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=uACPJ3krJUuogeqFQ/FMG+kdPSn5YGZfGjsX3Hw0XBU=; b=eq/3xTcoPBeDu3qqSYwFNNNh2GpldPyQTAbxFkYgdlnJWAsgGMlpnxrZKwdSYR6F5hPd0G BjWUUvNPnbX4ASq9Zp/aDWlHaR4TmXjqAzj8G65QVG07LaHMb/1Jj2tToygNGXFfqL6gNK HwLh1XvXjUB0RU1MJjz9PNAHwxoHTUM= X-MC-Unique: H4epy9taP-SgW4d_Oq8RRA-1 X-Mimecast-MFC-AGG-ID: H4epy9taP-SgW4d_Oq8RRA_1767877876 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877874; x=1768482674; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uACPJ3krJUuogeqFQ/FMG+kdPSn5YGZfGjsX3Hw0XBU=; b=momTga1I2BL8NzajqUVfbH1bjEymUyZmsUJwYsWM/1edEu05oyM30riFjE7faBTszk hWO3hkvdIpxtrrr3EEVKxY8YZNXRjFWGAQ6hZhh8khpkl62IaHrAl9Eo63JKMrLcS2my eHBNbH2UoCotzULoEdytfILuIWolA14fTRJk3mT82RlefI4VqMnDkhO9UhQtjNjClOh6 JC9XBb5S0qegs9zbUZwUM5jA0KqszZa+lOFPXkZW/BlzwsKSfamxz3Jy51sMHwjx/yhQ KQ0YqIyAXAYFxHqP4QSU9WWz+9aXNq9DxLX2MADGexJgJd+aUOSD+TDNjwByLqFloV8M XbmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877874; x=1768482674; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=uACPJ3krJUuogeqFQ/FMG+kdPSn5YGZfGjsX3Hw0XBU=; b=DapvUQ6ZVX5WUOJOKt896qvtJRhg6KYekxG7ct5FTnPUJzrcUSlFAVH5C4soMb4qln QvtORaKkKWgmfAA4mvMq9yF5azRKL0NZl5RhATTtcPvlcS0Go8ADGBg2vyqs5LJVtbfh eiCBdma9MuJpR4lDhrYgQqZVsBf8tyzwLql09+fBsSspAd3F551RrGHbqV2nJRpaKm+H g9j6KAcOdqv/YftKSO14e7QGjLCfa8zoQO75wtkLR7lsA8OhHjkd4dnLZ9v9ITKJwBBP WOi36bCBiyGyYKaK4NuNGe9mGp9CYOYwjwps0HMA+l4hCjXCH7IXCZ7QKonLVkcITkFs oD1w== X-Gm-Message-State: AOJu0YxCUK811C4MvxexLcGT/ltocsagTb7AN408Ggv2Z7IpEjLpu51T P+dLE3z5/BieS8ISZUrzn6Vc7kwF10Hx7SIR/dOX5nJyiPJ1b+XpTWA/npASdiyZGje+dKY8M4g JYDfeH+o7Ae7Kwbs9ZixB1AwXrLCqwhcAEws/WzirVK+2c3K7iEj7T8FeNPqogBMlMVFE7czQkI VZLawY8oYzIpLxEbjuwr7oO3uSJVQBjh1U3S4zbHwE X-Gm-Gg: AY/fxX5QMGuoqh0+IeG39IEaNk9zJMLK7NphVMNo5rnjBt8hZO6lb4o3yckS5X9pdEA cjs6hwwAShqrFkQGbPLm18AecWjbQyMUMvfqeOFcfQJABofEy1c2NGJNN8mzitWVALIiNCrW//u R5bUq06jcAjyIMMfJWiezAJJwltvwrC0NIXwZRhUpJ5Za3aXATy4LCTwqxGlfT8LGdWHbY2HK8c D7GAHROCkOjy7a/0f+DNuoOOYMWFJ/NCZgmk+X+nw9nTs6ACKrh1TGUDrufMhcnMD1DUEoWkxKK KLiCi/6qD33ymCYsBAP6mv5hV6MXMcaP98JBDnWrgeV3j1B2Zqp/Awq7/iqFX7pE0MulkIBNmvY UL+tHKMwDcXUlKv1Z1zGB0t1hF7cB+92Nl0TIzKZtR1eDHjYzSr2r71zJhqroGTOisg3UwzD+Sh RIWxUVAC+eUI+FmA== X-Received: by 2002:a05:600c:4709:b0:479:1348:c614 with SMTP id 5b1f17b1804b1-47d84b41b2dmr72474625e9.26.1767877874433; Thu, 08 Jan 2026 05:11:14 -0800 (PST) X-Google-Smtp-Source: AGHT+IGzdn86O6GcGD3KNIx09WwJrMsgMzsGPd/wpZ83CXQPEcHNMRDuX6587bEr2jyIrHaOzHWwkw== X-Received: by 2002:a05:600c:4709:b0:479:1348:c614 with SMTP id 5b1f17b1804b1-47d84b41b2dmr72474355e9.26.1767877873993; Thu, 08 Jan 2026 05:11:13 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 10/16] scripts/qapi: add QAPISchemaType.is_predefined Date: Thu, 8 Jan 2026 14:10:37 +0100 Message-ID: <20260108131043.490084-11-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877976360158500 Content-Type: text/plain; charset="utf-8" It is impossible to call is_implicit on an enum type from the visitor, beca= use the QAPISchemaEnumType has already been exploded into its costituent fields. The Rust backend is also not modular (yet?) so it is not possible to filter out the builtin module; add a way to query for implicit type names without having the object itself. Signed-off-by: Paolo Bonzini --- scripts/qapi/schema.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 848a7401251..15f5d97418f 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -1243,6 +1243,17 @@ def _def_builtin_type( # schema. self._make_array_type(name, None) =20 + def is_predefined(self, name: str) -> bool: + # See QAPISchema._def_predefineds() + entity =3D self._entity_dict[name] + if isinstance(entity, QAPISchemaBuiltinType): + return True + if entity is self.the_empty_object_type: + return True + if name =3D=3D 'QType': + return True + return False + def _def_predefineds(self) -> None: for t in [('str', 'string', 'char' + POINTER_SUFFIX), ('number', 'number', 'double'), --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877976; cv=none; d=zohomail.com; s=zohoarc; b=FS4HN6UZOhrfqtSkT8bfrncxzzyhQ0rgr/2q6t917bAY/Vv13bNj3r3uPqgOpTHwCBWAO1s55c1DJe41mXqz1Ju8azxsER88wMehDTF15k9uNzKTeR6JddgDB04CkziYmYiHb1Fip7v/NZN1zgh8pZEc/WUhrGyEGXa8nRXlniw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877976; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=kzld2q5RlJkN3vtzdl66CcPbGlXEZCEofO8swei4zsY=; b=dfunfVG6hl/ATttwI0n3fuG57jl9YYbHLbg4YiEaL2V5mcrG5zgT4OKspgB695DgpoKhhKTaB0beMeQZ4JDrHyt8W6fEUCEOO68Lx9pFWw5vJsFqpaMDNwn+ZD5FsyhHtSYuJTrof74Pxf6b+xYgl+97Is3h9kEbCMsEb8LPWzA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877976762152.865453935966; Thu, 8 Jan 2026 05:12:56 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnQ-0007gR-GR; Thu, 08 Jan 2026 08:11:24 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnO-0007fi-Ma for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:22 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnN-00082i-3f for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:22 -0500 Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-139-Jf9TNhVFPmqM1bZ24GDLKQ-1; Thu, 08 Jan 2026 08:11:19 -0500 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-477563e531cso23867535e9.1 for ; Thu, 08 Jan 2026 05:11:18 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d7f69e802sm155710765e9.8.2026.01.08.05.11.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877880; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=kzld2q5RlJkN3vtzdl66CcPbGlXEZCEofO8swei4zsY=; b=LmOAflH3ME9g1E59rSX+Yl/x13SN0ErmjNWXKLPdQXJsFY4pNGipc1UNKUv5LokgrDJh+6 gNJB/PExd+PHW68gh6htBL5g7GzHI4bslAvqEmucK0tTgkg1gY4ju15CVWTw0lUFU3gvRa fImAJ/B2UMfPZzREKM+tH/Wz4rhi/Y0= X-MC-Unique: Jf9TNhVFPmqM1bZ24GDLKQ-1 X-Mimecast-MFC-AGG-ID: Jf9TNhVFPmqM1bZ24GDLKQ_1767877878 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877877; x=1768482677; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kzld2q5RlJkN3vtzdl66CcPbGlXEZCEofO8swei4zsY=; b=SeG6K/UMNfjZtIX7eFBAepYP8o0btdA2Q5LcVyEQImFqt3osLhARFAGCp260sU97J1 kq/ESFEtmpFpTkoL2xo3+6KyWJh/EeelmB/RlBtshzYBkTMjuhXufjxXh2ynoSL4nEYZ edbgOB6pQuFvpKL4CSYWkPSFEDyHwCZ38PEA6+gu+PyDiRZVUtBbGKMvzJd2HXIliGwJ PIGeqb17zEQ2BRRjUliHQS1RMtx6ElX6tXmI+zv0ljdF+wO7dwfnK/eanFfH1SYJlije 6MkiXH2OMi9mnApemq30o+UiVx64CjocbZVQN8yQhNhCS+geYCIH2LhOChSgMpouW4EC wP4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877877; x=1768482677; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=kzld2q5RlJkN3vtzdl66CcPbGlXEZCEofO8swei4zsY=; b=gK+XbxEAqeu8OkLTde3hCVYhPgK32q8sVeMiTfcYYT5UNzpFcGvRw4Ad4WdSigOBc9 +4YDMTr6GPYXPvwjM/zEJDMXBiA4DLIt+DICGJAvg7qyOUyIhHPjFWdHPpCk0xJS9KFo ycQl8wIT60qcWQ/dgdhOL7/2dlOy3Vna9sNhPAGXwNp4i+842dqTWpvqN1XlmSQY6BDQ N/xELhIc0ppjgNIzi+Cj2OSpYlzsXLLGinIS0sUPvlldZZkcal9r0KH8jb3AMygZOcSb HogoH0bEY5u9y2XKomi7PhD1WggPBAX6nEJPCUHw3CUWq36SFdKq6KE58fY2GJqBwVtO aopw== X-Gm-Message-State: AOJu0Yw5Vgt+g+NW5AZ/bpAqjVbxgeIPRQ7C9LlqvqleqdgFmvPg2Yig qVqF6Fq4+Ur9Dam2+6+L/NIJPP7ZRop2UJT603dS+eQHD8t/z31dklctCM+LQ/aBDHvAn0y6vnZ wgMU7ETwXchJqTvD2DoBNiehoH/AylH1dGC4PEUnHqCEKxTOcEL7INol6wz2upMpcrGlInqxcKd vvquK6s5APYCoxpw4DzDkMWQBmBcVylYb8G00SjT0+ X-Gm-Gg: AY/fxX44V4MmuHmb53VcxElgImMGRflzBy6vVJRcv60zKLA6OspzYzNXZTkv+w/QvK+ kAho6jHHr6uliby3linTPj7hlxxsAzDc0l2xrLWudWpBRiNYsabSr8hLm7K7ZWIHpEtTV5cXBtI Xr/Wzo1aTXqvXAVA1GJAQ8pYCpSJgcsvDpB8m4PWvuaCPjY8O5TPMHzmF3n8UdbRkKzyMbxz7II LNUT3P3+xVeNx/Zv9RcEfqD+tZ8n3kq5bQ9OBB0U2GuFliD9Vz5aUSuJIjiYWuQ6bp0BytDJfcz fGQ+tsoVthrcnNr24qFyNDG0YkVLrjbuXarJ28iPpNhmWyXgxR+hr5yTbWx1p+xlewTaMiOR5yt 6ISWsPMsG0wGwM/Igx0KAfuEolc9ospc6gtB7vOWtgooaV7RH27RAQPP4+MUDmfKOfLew4s6v4b LsWKI3uEr/uljUwg== X-Received: by 2002:a05:600c:4e13:b0:47d:25ac:3a94 with SMTP id 5b1f17b1804b1-47d84b32f30mr79824975e9.17.1767877876994; Thu, 08 Jan 2026 05:11:16 -0800 (PST) X-Google-Smtp-Source: AGHT+IHb/egjF3zBUGVDoYK29auPfPeGuQmD2Zgs5XazD5fX4zjGL6AyDI72+ZkzZ1E03v51m+S4kA== X-Received: by 2002:a05:600c:4e13:b0:47d:25ac:3a94 with SMTP id 5b1f17b1804b1-47d84b32f30mr79824625e9.17.1767877876629; Thu, 08 Jan 2026 05:11:16 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 11/16] scripts/qapi: pull c_name from camel_to_upper to caller Date: Thu, 8 Jan 2026 14:10:38 +0100 Message-ID: <20260108131043.490084-12-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877978404158500 Content-Type: text/plain; charset="utf-8" Allow using it for other languages too. Signed-off-by: Paolo Bonzini Reviewed-by: Zhao Liu --- scripts/qapi/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 14d5dd259c4..c75396a01b5 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -61,7 +61,7 @@ def camel_to_upper(value: str) -> str: ret +=3D ch upc =3D ch.isupper() =20 - return c_name(ret.upper()).lstrip('_') + return ret.upper() =20 =20 def c_enum_const(type_name: str, @@ -75,7 +75,7 @@ def c_enum_const(type_name: str, :param prefix: Optional, prefix that overrides the type_name. """ if prefix is None: - prefix =3D camel_to_upper(type_name) + prefix =3D c_name(camel_to_upper(type_name)).lstrip('_') return prefix + '_' + c_name(const_name, False).upper() =20 =20 --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877975; cv=none; d=zohomail.com; s=zohoarc; b=GnvyLFX6qwEX9MgNMsIe0VdNRIc/C49SOdfOn14nTSlv5TdJI+oRISnppC8Vbfaju9bV5/d/KY0UBM/x62DbAU19orI7FeV+6Cg2sedr4lVZeh94HRk/j2s9f3AmtE0Y9tnVEIzEpaKFeuQ4G2/m8LQwnxr5gVU9vJAn0lRR3ys= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877975; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=RLf8C04HThWu7hxDLSyEOaJNCSCBlM6N9D2NQTVosRM=; b=K4dAchvV9z9skGZ+xC3gBPLOpyLSasDrjkOyISh54WQLDePYXyUZOYXKZ8ZJoDClWyfAOW+oRz5BsaF3Ykxwy4fSMIdkbsgVhx1MTaW4FNmtp/B6wKiiw5KxtepO6HTrx/R2gQsi6C1QEkefgKItxFdA772K3vuYxtDvJz1rPZw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877975413233.5913698394404; Thu, 8 Jan 2026 05:12:55 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnW-0007xl-8c; Thu, 08 Jan 2026 08:11:30 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnT-0007qX-8X for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:29 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnQ-00083D-3y for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:27 -0500 Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-556-W-SwEtCLOIq8zOGfPCYsfw-1; Thu, 08 Jan 2026 08:11:22 -0500 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-43102ac1da8so2261449f8f.2 for ; Thu, 08 Jan 2026 05:11:21 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432bd5ee870sm16251504f8f.36.2026.01.08.05.11.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877883; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RLf8C04HThWu7hxDLSyEOaJNCSCBlM6N9D2NQTVosRM=; b=CQiZBmxzEhwnYMyVhVyY47HoD9Y6lQD45G+uMjTjCyz9Z06XIQw2tJYoJ+hLh/jfi8YqBp fdKFi0mdhHL5mCUAoAf0i6Ezx8PIpE7C46dtjqc20Bha+a0aTQ5nJqMyv5rMIlZxsNcX48 Wj7agLnWVMtHgwVpjoN+YH0PFueomKY= X-MC-Unique: W-SwEtCLOIq8zOGfPCYsfw-1 X-Mimecast-MFC-AGG-ID: W-SwEtCLOIq8zOGfPCYsfw_1767877881 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877880; x=1768482680; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RLf8C04HThWu7hxDLSyEOaJNCSCBlM6N9D2NQTVosRM=; b=MBhgK77p1YL7u68zoCxSkuAuCPFVdBlNWck80MQzA0wXIxEPjjIGFo6JAAOKpFedm9 w80owy87G3X7MzpwK2bicXqKWQ903nKhK7r2WW+/vAKFuQ9YWoXaDS6TW2rtfcIABvZ7 XoutCTh8DkkMyhuHZxw4UpcR4+4CfBsHsEevG2Jl3fEffZRZgR3itX9MMX73g1Kd0+K4 uH/iWALlwR2BHtOat9dP8a3PrLJ9RR6bqUmtVcwPckxQgxgmKOiHumDqEqG9045rdQwM +RMmT1ZgPhUYwtOLnEaBWGQNporhPcgVgrBEORgufnM9ita7n7qTE9AfJBC9A4AQQbvs PAUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877880; x=1768482680; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=RLf8C04HThWu7hxDLSyEOaJNCSCBlM6N9D2NQTVosRM=; b=GUzkDiNgEUUWj/ogn9GiOcBqYELHFBdLP5D98l1/bbFf7mc342lQTnR4GbLTPsJ8Fc Fy38TWZ2plvOrElsBwtabN7wsyFyDXJ5YHw37HFi7ZUDDWSxLWhtQF8hhY2tMmKxsFhT 8N7Cv0ZMzSBzjYZ7nxgtJrGIgZU9oQpmjyd+NeH/E9FU40MknxcFaxYzZ4gwXO9Df98i eZ0ax0f+Fhwbue9tzwJNYtZPMsNpR26MkY+rnRWwMkVnkZZ1wedMLz+v3np03bxM4BpA TNKaC0YokAsinSpziBVpH0SE2JMTxqg0ZfH2cZywIHULCWZbzkTlU+fPigS4MBzFL1jN SwkA== X-Gm-Message-State: AOJu0Yy7KGyWNS+DjgaGIWsdO92lGePgDD39gqfDG4R+A8m0Pc3nypRH w4mV+NvNL+y8TzydmTiJXQ1/ZzqwkCwb/QEnWN/kEiI6pjrteVqo7E6chZ+gZJB6JAiA3rwOnYc cgah2tykmS8PLEw+T0EHTzTHaFNgOb6JyrgF0vUKzRXoC7NqmZMad1YsrEq0PSD7N7gUk36WPow v6F/JWzUI9s7jgErZaOU1wTX8dAXMdp+eUILUTa3qu X-Gm-Gg: AY/fxX5vY/1ePPvUzMOaqQWMelIWGeAO3d9LE/4lH/QREzsG9WVbjynL2xSMJ+KqzIv BQBemb8H4kHrlUpm/6rvqdCGiIbc3QFqAhyVgZ1/qS6GfZmyHO9lPV+o0FdhSIW14DP7l21yYLl x0qsmijSSsDAAfTLS22VAI8qlEUD1XTyFuQCj9WL9/1yaO/zH0fC++EaJlWk0TXDHKdrBlt6Z/f gCErObXZ2pll2MCXOfmhDhS/ybskCkH9nMvxOtKqJfXgW+i1JCOIOG0b0i6ZKxYfsiQ1OFl205S bKQTqc2lTeXSgIpsD/MCuyqtoWwtXYOvp+l1cdSLhvWjCYhrwDVYrkEvzmTGGkwYDhEo5D4UohD bgp7a7nARrrF4sX/MO7J4xXl09MkyweE+6FhrE2nkuaek73xzGqDpXJZHTdkTO5fw8/6fOM0YHQ DAIPTCx9VWcMgXqA== X-Received: by 2002:a05:6000:2908:b0:431:a38:c2f7 with SMTP id ffacd0b85a97d-432c37a7d86mr7165471f8f.59.1767877879852; Thu, 08 Jan 2026 05:11:19 -0800 (PST) X-Google-Smtp-Source: AGHT+IHIBPHXJGQY+rvLsf+AbhuQcxem2L+UpS2wdfb1SS+pCtTPKnI+J60VkjZFNDBpeX7UKeSWpA== X-Received: by 2002:a05:6000:2908:b0:431:a38:c2f7 with SMTP id ffacd0b85a97d-432c37a7d86mr7165423f8f.59.1767877879040; Thu, 08 Jan 2026 05:11:19 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 12/16] scripts/qapi: generate high-level Rust bindings Date: Thu, 8 Jan 2026 14:10:39 +0100 Message-ID: <20260108131043.490084-13-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877976498158500 From: Marc-Andr=C3=A9 Lureau Generate high-level native Rust declarations for the QAPI types. - char* is mapped to String, scalars to there corresponding Rust types - enums use #[repr(u32)] and can be transmuted to their C counterparts - has_foo/foo members are mapped to Option - lists are represented as Vec - structures map fields 1:1 to Rust - alternate are represented as Rust enum, each variant being a 1-element tuple - unions are represented in a similar way as in C: a struct S with a "u" member (since S may have extra 'base' fields). The discriminant isn't a member of S, since Rust enum already include it, but it can be recovered with "mystruct.u.into()" Anything that includes a recursive struct puts it in a Box. Lists are not considered recursive, because Vec breaks the recursion (it's possible to construct an object containing an empty Vec of its own type). Signed-off-by: Marc-Andr=C3=A9 Lureau Link: https://lore.kernel.org/r/20210907121943.3498701-21-marcandre.lureau@= redhat.com [Paolo: rewrite conversion of schema types to Rust types] Signed-off-by: Paolo Bonzini --- meson.build | 4 +- scripts/qapi/backend.py | 25 +++ scripts/qapi/common.py | 43 +++++ scripts/qapi/rs.py | 61 +++++++ scripts/qapi/rs_types.py | 373 +++++++++++++++++++++++++++++++++++++++ scripts/qapi/schema.py | 59 +++++-- 6 files changed, 546 insertions(+), 19 deletions(-) create mode 100644 scripts/qapi/rs.py create mode 100644 scripts/qapi/rs_types.py diff --git a/meson.build b/meson.build index db87358d62d..4228792f0f6 100644 --- a/meson.build +++ b/meson.build @@ -3540,11 +3540,13 @@ qapi_gen_depends =3D [ meson.current_source_dir() /= 'scripts/qapi/__init__.py', meson.current_source_dir() / 'scripts/qapi/introspect= .py', meson.current_source_dir() / 'scripts/qapi/main.py', meson.current_source_dir() / 'scripts/qapi/parser.py', + meson.current_source_dir() / 'scripts/qapi/rs_types.p= y', meson.current_source_dir() / 'scripts/qapi/schema.py', meson.current_source_dir() / 'scripts/qapi/source.py', meson.current_source_dir() / 'scripts/qapi/types.py', meson.current_source_dir() / 'scripts/qapi/visit.py', - meson.current_source_dir() / 'scripts/qapi-gen.py' + meson.current_source_dir() / 'scripts/qapi-gen.py', + meson.current_source_dir() / 'scripts/qapi/rs.py', ] =20 tracetool =3D [ diff --git a/scripts/qapi/backend.py b/scripts/qapi/backend.py index 49ae6ecdd33..8023acce0d6 100644 --- a/scripts/qapi/backend.py +++ b/scripts/qapi/backend.py @@ -7,6 +7,7 @@ from .events import gen_events from .features import gen_features from .introspect import gen_introspect +from .rs_types import gen_rs_types from .schema import QAPISchema from .types import gen_types from .visit import gen_visit @@ -63,3 +64,27 @@ def generate(self, gen_commands(schema, output_dir, prefix, gen_tracing) gen_events(schema, output_dir, prefix) gen_introspect(schema, output_dir, prefix, unmask) + + +class QAPIRsBackend(QAPIBackend): + # pylint: disable=3Dtoo-few-public-methods + + def generate(self, + schema: QAPISchema, + output_dir: str, + prefix: str, + unmask: bool, + builtins: bool, + gen_tracing: bool) -> None: + """ + Generate Rust code for the given schema into the target directory. + + :param schema_file: The primary QAPI schema file. + :param output_dir: The output directory to store generated code. + :param prefix: Optional C-code prefix for symbol names. + :param unmask: Expose non-ABI names through introspection? + :param builtins: Generate code for built-in types? + + :raise QAPIError: On failures. + """ + gen_rs_types(schema, output_dir, prefix) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index c75396a01b5..e9261a3411e 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -64,6 +64,13 @@ def camel_to_upper(value: str) -> str: return ret.upper() =20 =20 +def camel_to_lower(value: str) -> str: + """ + Converts CamelCase to camel_case. + """ + return camel_to_upper(value).lower() + + def c_enum_const(type_name: str, const_name: str, prefix: Optional[str] =3D None) -> str: @@ -129,6 +136,42 @@ def c_name(name: str, protect: bool =3D True) -> str: return name =20 =20 +def rs_name(name: str) -> str: + """ + Map @name to a valid, possibly raw Rust identifier. + """ + name =3D re.sub(r'[^A-Za-z0-9_]', '_', name) + if name[0].isnumeric(): + name =3D '_' + name + # based from the list: + # https://doc.rust-lang.org/reference/keywords.html + if name in ('Self', 'abstract', 'as', 'async', + 'await', 'become', 'box', 'break', + 'const', 'continue', 'crate', 'do', + 'dyn', 'else', 'enum', 'extern', + 'false', 'final', 'fn', 'for', + 'if', 'impl', 'in', 'let', + 'loop', 'macro', 'match', 'mod', + 'move', 'mut', 'override', 'priv', + 'pub', 'ref', 'return', 'self', + 'static', 'struct', 'super', 'trait', + 'true', 'try', 'type', 'typeof', + 'union', 'unsafe', 'unsized', 'use', + 'virtual', 'where', 'while', 'yield'): + name =3D 'r#' + name + # avoid some clashes with the standard library + if name in ('String',): + name =3D 'Qapi' + name + + return name + + +def to_camel_case(value: str) -> str: + return ''.join('_' + word if word[0].isdigit() + else word[:1].upper() + word[1:] + for word in filter(None, re.split("[-_]+", value))) + + class Indentation: """ Indentation level management. diff --git a/scripts/qapi/rs.py b/scripts/qapi/rs.py new file mode 100644 index 00000000000..2cf0c0e07f1 --- /dev/null +++ b/scripts/qapi/rs.py @@ -0,0 +1,61 @@ +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. +""" +QAPI Rust generator +""" + +import os +import re +import subprocess +import sys + +from .common import mcgen as mcgen_common +from .gen import QAPIGen +from .schema import QAPISchemaVisitor + + +def mcgen(s: str, **kwds: object) -> str: + s =3D mcgen_common(s, **kwds) + return re.sub(r'(?: *\n)+', '\n', s) + + +class QAPIGenRs(QAPIGen): + def __init__(self, fname: str, blurb: str, pydoc: str): + super().__init__(fname) + self._blurb =3D blurb + self._copyright =3D '\n//! '.join(re.findall(r'^Copyright .*', pyd= oc, + re.MULTILINE)) + + def _top(self) -> str: + return mcgen(''' +// @generated by qapi-gen, DO NOT EDIT + +//! +//! Schema-defined QAPI types +//! +//! %(copyright)s +//! +//! This work is licensed under the terms of the GNU LGPL, version 2.1 or +//! later. See the COPYING.LIB file in the top-level directory. + +''', + tool=3Dos.path.basename(sys.argv[0]), + blurb=3Dself._blurb, copyright=3Dself._copyright) + + +class QAPISchemaRsVisitor(QAPISchemaVisitor): + + def __init__(self, prefix: str, what: str, + blurb: str, pydoc: str): + super().__init__() + self._prefix =3D prefix + self._what =3D what + self._gen =3D QAPIGenRs(self._prefix + self._what + '.rs', blurb, = pydoc) + + def write(self, output_dir: str) -> None: + self._gen.write(output_dir) + + try: + subprocess.check_call(['rustfmt', self._gen.fname], cwd=3Doutp= ut_dir) + except FileNotFoundError: + pass diff --git a/scripts/qapi/rs_types.py b/scripts/qapi/rs_types.py new file mode 100644 index 00000000000..64702eb54ae --- /dev/null +++ b/scripts/qapi/rs_types.py @@ -0,0 +1,373 @@ +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. +""" +QAPI Rust types generator + +Copyright (c) 2025 Red Hat, Inc. + +This work is licensed under the terms of the GNU GPL, version 2. +See the COPYING file in the top-level directory. +""" + +from typing import List, Optional, Set + +from .common import ( + camel_to_lower, + camel_to_upper, + rs_name, + to_camel_case, +) +from .rs import QAPISchemaRsVisitor, mcgen +from .schema import ( + QAPISchema, + QAPISchemaAlternateType, + QAPISchemaEnumMember, + QAPISchemaFeature, + QAPISchemaIfCond, + QAPISchemaObjectType, + QAPISchemaObjectTypeMember, + QAPISchemaType, + QAPISchemaVariants, +) +from .source import QAPISourceInfo + + +objects_seen =3D set() + + +def gen_rs_variants_to_tag(name: str, + ifcond: QAPISchemaIfCond, + variants: QAPISchemaVariants) -> str: + ret =3D mcgen(''' + +%(cfg)s +impl From<&%(rs_name)sVariant> for %(tag)s { + fn from(e: &%(rs_name)sVariant) -> Self { + match e { +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name), + tag=3Dvariants.tag_member.type.rs_type()) + + for var in variants.variants: + type_name =3D var.type.name + tag_name =3D var.name + patt =3D '(_)' + if type_name =3D=3D 'q_empty': + patt =3D '' + ret +=3D mcgen(''' + %(cfg)s + %(rs_name)sVariant::%(var_name)s%(patt)s =3D> Self::%(tag_name= )s, +''', + cfg=3Dvar.ifcond.rsgen(), + rs_name=3Drs_name(name), + tag_name=3Drs_name(camel_to_upper(tag_name)), + var_name=3Drs_name(to_camel_case(tag_name)), + patt=3Dpatt) + + ret +=3D mcgen(''' + } + } +} +''') + return ret + + +def gen_rs_variants(name: str, + ifcond: QAPISchemaIfCond, + variants: QAPISchemaVariants) -> str: + ret =3D mcgen(''' + +%(cfg)s +#[derive(Clone, Debug, PartialEq)] +pub enum %(rs_name)sVariant { +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name)) + + for var in variants.variants: + type_name =3D var.type.name + var_name =3D rs_name(to_camel_case(var.name)) + if type_name =3D=3D 'q_empty': + ret +=3D mcgen(''' + %(cfg)s + %(var_name)s, +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name) + else: + ret +=3D mcgen(''' + %(cfg)s + %(var_name)s(%(rs_type)s), +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name, + rs_type=3Dvar.type.rs_type()) + + ret +=3D mcgen(''' +} +''') + + ret +=3D gen_rs_variants_to_tag(name, ifcond, variants) + + return ret + + +def gen_rs_members(members: List[QAPISchemaObjectTypeMember], + exclude: Optional[List[str]] =3D None) -> List[str]: + exclude =3D exclude or [] + return [f"{m.ifcond.rsgen()} {rs_name(camel_to_lower(m.name))}" + for m in members if m.name not in exclude] + + +def has_recursive_type(memb: QAPISchemaType, + name: str, + visited: Set[str]) -> bool: + # pylint: disable=3Dtoo-many-return-statements + if name =3D=3D memb.name: + return True + if memb.name in visited: + return False + visited.add(memb.name) + if isinstance(memb, QAPISchemaObjectType): + if memb.base and has_recursive_type(memb.base, name, visited): + return True + if memb.branches and \ + any(has_recursive_type(m.type, name, visited) + for m in memb.branches.variants): + return True + if any(has_recursive_type(m.type, name, visited) + for m in memb.members): + return True + return any(has_recursive_type(m.type, name, visited) + for m in memb.local_members) + if isinstance(memb, QAPISchemaAlternateType): + return any(has_recursive_type(m.type, name, visited) + for m in memb.alternatives.variants) + return False + + +def gen_struct_members(members: List[QAPISchemaObjectTypeMember], + name: str) -> str: + ret =3D '' + for memb in members: + typ =3D memb.type.rs_type() + if has_recursive_type(memb.type, name, set()): + typ =3D 'Box<%s>' % typ + if memb.optional: + typ =3D 'Option<%s>' % typ + ret +=3D mcgen(''' + %(cfg)s + pub %(rs_name)s: %(rs_type)s, +''', + cfg=3Dmemb.ifcond.rsgen(), + rs_type=3Dtyp, + rs_name=3Drs_name(camel_to_lower(memb.name))) + return ret + + +def gen_rs_object(name: str, + ifcond: QAPISchemaIfCond, + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + variants: Optional[QAPISchemaVariants]) -> str: + if name in objects_seen: + return '' + + if variants: + members =3D [m for m in members + if m.name !=3D variants.tag_member.name] + + ret =3D '' + objects_seen.add(name) + + if variants: + ret +=3D gen_rs_variants(name, ifcond, variants) + + ret +=3D mcgen(''' + +%(cfg)s +#[derive(Clone, Debug, PartialEq)] +pub struct %(rs_name)s { +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name)) + + if base: + if not base.is_implicit(): + ret +=3D mcgen(''' + // Members inherited: +''', + c_name=3Dbase.c_name()) + base_members =3D base.members + if variants: + base_members =3D [m for m in base.members + if m.name !=3D variants.tag_member.name] + ret +=3D gen_struct_members(base_members, name) + if not base.is_implicit(): + ret +=3D mcgen(''' + // Own members: +''') + + ret +=3D gen_struct_members(members, name) + + if variants: + ret +=3D mcgen(''' + pub u: %(rs_type)sVariant, +''', rs_type=3Drs_name(name)) + ret +=3D mcgen(''' +} +''') + return ret + + +def gen_rs_enum(name: str, + ifcond: QAPISchemaIfCond, + members: List[QAPISchemaEnumMember]) -> str: + ret =3D mcgen(''' + +%(cfg)s +#[derive(Copy, Clone, Debug, PartialEq)] +''', + cfg=3Difcond.rsgen()) + + if members: + ret +=3D '''#[repr(u32)] +#[derive(common::TryInto)] +''' + ret +=3D mcgen(''' +pub enum %(rs_name)s { +''', + rs_name=3Drs_name(name)) + + for member in members: + ret +=3D mcgen(''' + %(cfg)s + %(c_enum)s, +''', + cfg=3Dmember.ifcond.rsgen(), + c_enum=3Drs_name(camel_to_upper(member.name))) + ret +=3D '''} + +''' + + # pick the first, since that's what malloc0 does + if members: + default =3D rs_name(camel_to_upper(members[0].name)) + ret +=3D mcgen(''' +%(cfg)s +impl Default for %(rs_name)s { + #[inline] + fn default() -> %(rs_name)s { + Self::%(default)s + } +} +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name), + default=3Ddefault) + return ret + + +def gen_rs_alternate(name: str, + ifcond: QAPISchemaIfCond, + variants: QAPISchemaVariants) -> str: + if name in objects_seen: + return '' + + ret =3D '' + objects_seen.add(name) + + ret +=3D mcgen(''' +%(cfg)s +#[derive(Clone, Debug, PartialEq)] +pub enum %(rs_name)s { +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name)) + + for var in variants.variants: + if var.type.name =3D=3D 'q_empty': + continue + typ =3D var.type.rs_type() + if has_recursive_type(var.type, name, set()): + typ =3D 'Box<%s>' % typ + ret +=3D mcgen(''' + %(cfg)s + %(mem_name)s(%(rs_type)s), +''', + cfg=3Dvar.ifcond.rsgen(), + rs_type=3Dtyp, + mem_name=3Drs_name(to_camel_case(var.name))) + + ret +=3D mcgen(''' +} +''') + return ret + + +class QAPISchemaGenRsTypeVisitor(QAPISchemaRsVisitor): + _schema: Optional[QAPISchema] + + def __init__(self, prefix: str) -> None: + super().__init__(prefix, 'qapi-types', + 'Schema-defined QAPI types', __doc__) + + def visit_begin(self, schema: QAPISchema) -> None: + self._schema =3D schema + objects_seen.add(schema.the_empty_object_type.name) + + self._gen.preamble_add( + mcgen(''' +#![allow(unexpected_cfgs)] +#![allow(non_camel_case_types)] +#![allow(clippy::empty_structs_with_brackets)] +#![allow(clippy::large_enum_variant)] +#![allow(clippy::pub_underscore_fields)] + +// Because QAPI structs can contain float, for simplicity we never +// derive Eq. Clippy however would complain for those structs +// that *could* be Eq too. +#![allow(clippy::derive_partial_eq_without_eq)] + +use util::qobject::QObject; +''')) + + def visit_object_type(self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + branches: Optional[QAPISchemaVariants]) -> None: + assert self._schema is not None + if self._schema.is_predefined(name) or name.startswith('q_'): + return + self._gen.add(gen_rs_object(name, ifcond, base, members, branches)) + + def visit_enum_type(self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + members: List[QAPISchemaEnumMember], + prefix: Optional[str]) -> None: + assert self._schema is not None + if self._schema.is_predefined(name): + return + self._gen.add(gen_rs_enum(name, ifcond, members)) + + def visit_alternate_type(self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + alternatives: QAPISchemaVariants) -> None: + self._gen.add(gen_rs_alternate(name, ifcond, alternatives)) + + +def gen_rs_types(schema: QAPISchema, output_dir: str, prefix: str) -> None: + vis =3D QAPISchemaGenRsTypeVisitor(prefix) + schema.visit(vis) + vis.write(output_dir) diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 15f5d97418f..a65b25141fa 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -37,6 +37,7 @@ docgen_ifcond, gen_endif, gen_if, + rs_name, rsgen_ifcond, ) from .error import QAPIError, QAPISemError, QAPISourceError @@ -341,6 +342,11 @@ def c_param_type(self) -> str: def c_unboxed_type(self) -> str: return self.c_type() =20 + # Return the Rust type for common use + @abstractmethod + def rs_type(self) -> str: + pass + @abstractmethod def json_type(self) -> str: pass @@ -382,11 +388,12 @@ def describe(self) -> str: class QAPISchemaBuiltinType(QAPISchemaType): meta =3D 'built-in' =20 - def __init__(self, name: str, json_type: str, c_type: str): + def __init__(self, name: str, json_type: str, rs_type: str, c_type: st= r): super().__init__(name, None, None) assert json_type in ('string', 'number', 'int', 'boolean', 'null', 'value') self._json_type_name =3D json_type + self._rs_type_name =3D rs_type self._c_type_name =3D c_type =20 def c_name(self) -> str: @@ -406,6 +413,9 @@ def json_type(self) -> str: def doc_type(self) -> str: return self.json_type() =20 + def rs_type(self) -> str: + return self._rs_type_name + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_builtin_type(self.name, self.info, self.json_type()) @@ -449,6 +459,9 @@ def is_implicit(self) -> bool: def c_type(self) -> str: return c_name(self.name) =20 + def rs_type(self) -> str: + return rs_name(self.name) + def member_names(self) -> List[str]: return [m.name for m in self.members] =20 @@ -498,6 +511,9 @@ def is_implicit(self) -> bool: def c_type(self) -> str: return c_name(self.name) + POINTER_SUFFIX =20 + def rs_type(self) -> str: + return 'Vec<%s>' % self.element_type.rs_type() + def json_type(self) -> str: return 'array' =20 @@ -630,6 +646,9 @@ def c_type(self) -> str: def c_unboxed_type(self) -> str: return c_name(self.name) =20 + def rs_type(self) -> str: + return rs_name(self.name) + def json_type(self) -> str: return 'object' =20 @@ -711,6 +730,9 @@ def c_type(self) -> str: def json_type(self) -> str: return 'value' =20 + def rs_type(self) -> str: + return rs_name(self.name) + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_alternate_type( @@ -1234,9 +1256,10 @@ def _def_include(self, expr: QAPIExpression) -> None: QAPISchemaInclude(self._make_module(include), expr.info)) =20 def _def_builtin_type( - self, name: str, json_type: str, c_type: str + self, name: str, json_type: str, rs_type: str, c_type: str ) -> None: - self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type= )) + builtin =3D QAPISchemaBuiltinType(name, json_type, rs_type, c_type) + self._def_definition(builtin) # Instantiating only the arrays that are actually used would # be nice, but we can't as long as their generated code # (qapi-builtin-types.[ch]) may be shared by some other @@ -1255,21 +1278,21 @@ def is_predefined(self, name: str) -> bool: return False =20 def _def_predefineds(self) -> None: - for t in [('str', 'string', 'char' + POINTER_SUFFIX), - ('number', 'number', 'double'), - ('int', 'int', 'int64_t'), - ('int8', 'int', 'int8_t'), - ('int16', 'int', 'int16_t'), - ('int32', 'int', 'int32_t'), - ('int64', 'int', 'int64_t'), - ('uint8', 'int', 'uint8_t'), - ('uint16', 'int', 'uint16_t'), - ('uint32', 'int', 'uint32_t'), - ('uint64', 'int', 'uint64_t'), - ('size', 'int', 'uint64_t'), - ('bool', 'boolean', 'bool'), - ('any', 'value', 'QObject' + POINTER_SUFFIX), - ('null', 'null', 'QNull' + POINTER_SUFFIX)]: + for t in [('str', 'string', 'String', 'char' + POINTER_SUFFIX= ), + ('number', 'number', 'f64', 'double'), + ('int', 'int', 'i64', 'int64_t'), + ('int8', 'int', 'i8', 'int8_t'), + ('int16', 'int', 'i16', 'int16_t'), + ('int32', 'int', 'i32', 'int32_t'), + ('int64', 'int', 'i64', 'int64_t'), + ('uint8', 'int', 'u8', 'uint8_t'), + ('uint16', 'int', 'u16', 'uint16_t'), + ('uint32', 'int', 'u32', 'uint32_t'), + ('uint64', 'int', 'u64', 'uint64_t'), + ('size', 'int', 'u64', 'uint64_t'), + ('bool', 'boolean', 'bool', 'bool'), + ('any', 'value', 'QObject', 'QObject' + POINTER_SUF= FIX), + ('null', 'null', '()', 'QNull' + POINTER_SUFFI= X)]: self._def_builtin_type(*t) self.the_empty_object_type =3D QAPISchemaObjectType( 'q_empty', None, None, None, None, None, [], None) --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877938; cv=none; d=zohomail.com; s=zohoarc; b=ICwthpZLmMHchbC/NoHiVmq4cMUoAqN4WtdAv1LjFafzYh6xtHPCTl53xzx1sXcRQ8ufIOR2Z0G6XNO/jvZSqIEScy+DQlghxlEt4ldhJq3bYgEU6DYel+l5NL09wyr1AXLO5ot+itfQy33RdzFCM0idWqXIZJA9VCAaDgiO4OI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877938; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=69DigzWj4ZfxAoDrRrcKXKL2TbKieDPgsraALABmsSU=; b=nZpWngtqo+W47jx8yqoRv2dffKU5PpamqBX7yWg/61VSi9FEM92vShZ/ipOInAwtoqFjqTnGC4niFQpRB6GwxKA4nQnITl6+TX9uS+feUZ8oXfu9LVXTi6aMJv2tKW7PKdnOd0FbhkbCtxwKKDCrLbssurojag0U+2dUkv3mLX4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877938626849.8620711942311; Thu, 8 Jan 2026 05:12:18 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnW-00081O-TT; Thu, 08 Jan 2026 08:11:30 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnU-0007s0-JU for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:29 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnS-00083K-7r for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:28 -0500 Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-518-a6ph6lbcPnW6LtUal3GIeA-1; Thu, 08 Jan 2026 08:11:24 -0500 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-477cf2230c8so30185845e9.0 for ; Thu, 08 Jan 2026 05:11:23 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d7f410c6csm163484795e9.1.2026.01.08.05.11.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877885; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=69DigzWj4ZfxAoDrRrcKXKL2TbKieDPgsraALABmsSU=; b=aVkG+XdzGvT57u/gQpqFsnUum0R4w9nAXspra7LS5Eezu8V90IXK+vHpiI6nWtvUgNsOKz 2DPeBnRNvJooVxkTQ6tI1SGXIXM0wEO/26inudXamZnDX7G8v5GF3Y/jLRkzMXJdTaPkb0 TXIzEsAqnBETq1wOmM62uxNKmC0rQWM= X-MC-Unique: a6ph6lbcPnW6LtUal3GIeA-1 X-Mimecast-MFC-AGG-ID: a6ph6lbcPnW6LtUal3GIeA_1767877883 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877882; x=1768482682; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=69DigzWj4ZfxAoDrRrcKXKL2TbKieDPgsraALABmsSU=; b=fDYK9afxtQT3zvg6EjY6irOKWKCaWUPnYr501QguAF15G7lXjlMISCdAprVnzTHqgY ls9DwloQ0k2KgKUHxXkEXFvi53V14HkLqJv7Q9CYJBIwJAZgsF8ywVAtXwdpnytdd93/ LHVHVOrkIo1iD30UeCl2AjNj+f/q4ypLgQ63Fd5zDm5MuppiNXHARf0j0jyzVZHHfxiP qweQ8S9wCzYa6zLObSEI6IOrUW7Tge/tito/ZOjpwrLXYuSS9h9euFLTKb8N9/NxkLyK 6A0Wk+iBplWJyA7OCv99rBgy6iYJaDT5U81NUd6MRezZae4VBBkCKzjGLR7qRCqlYUVF /dUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877882; x=1768482682; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=69DigzWj4ZfxAoDrRrcKXKL2TbKieDPgsraALABmsSU=; b=lunQ+h95d7Be7NtmjWIle26aqn18llgTpaXjSCRjirjPxS3qCcXGguj6aeWmcURhsp fUklzGu0rGkbYSHxl9mwL30TmYVkthjKNw5VOkkt41qO+oMBH1yqlXnPVdC175IPr58h QKTkIID+n3QQqKM1cQdzaHtP+1bXcJz0QrU9H5OgXiCEbcl+2ncAitaMD572dbMAMHYt /lsbo9GvJoFDZNgs0qjvVB6a1w473yTmBtKKDoTF8+jhfGu4EvZOJVskcBpxlmc5sV3a 07CCF9+bjIKBBabnw8WpCwVGNmEqbG8nnu/hHpBr31srR8LbS1DawAtzN1ALqlj8OWFs saZg== X-Gm-Message-State: AOJu0YxThJZtiEBVdKlYReBU0CN+Zzl0CYCPBG9hoThvVAwVMq8JLvpe fs2LBXfVZ+XvhL4tWznjojPKITsqxFHwcjz8szZNx+iBIFkzz4BAmdOeRP/ItQTiWjHSDW21b95 8E5H6EBTDMWNScbzEI/OoO92hCGnbCvMDD/4Fu9e4TLLKx5/LFQx4dVbvJtxv+eCwuH6i0ivf6Y 4VxmKqFq9sEWBaP8FhxR7tQlQVtojQJ3Lbegux/vhu X-Gm-Gg: AY/fxX4Gi1ZEZzXI3H+/xK3AavQ4ZVAUXKhmGmFyxd3gyers1brsiIDBATp6kNZc29/ NFX+Zt7ov81yJqzdL/fo/FPvJZHwS/vyNSZI3SC4Eg2FjWX7tw6t1YbEKXIlNwqAna5xARGYLrx xYVze6aP+wTBzTrxZ1KWnG1j+OmSb6B18ePQoEMm7rtN9Vux3jtKpX368qyizbzZcV+vbe9fZBy biZoRZ0IzgcuZyyUyME/fzd99obzlZNJoU6iSFZvp5mXTZIfH9/KsRfQ7uJFeLprW8cLmH83JlX LabInvu5Y5JS6/K5a/+RM74TyZ8bkFLRlhywYt/ZxugmxqfPrAATFzIeShPZ32GinuhGqwbQaQa xfp0YaM9jjLkNmRscdfxAORE9g6rDHH3fhxVA9tCv+zj6QJtfnRPZVsj09CKxOIU1S/Q6P20Nuz 2mlK6Fw01opKH1Cg== X-Received: by 2002:a05:600c:630f:b0:47d:5d27:2a7f with SMTP id 5b1f17b1804b1-47d84b38534mr67895515e9.26.1767877882416; Thu, 08 Jan 2026 05:11:22 -0800 (PST) X-Google-Smtp-Source: AGHT+IEZRkwS4JBE3z+OEyFzuopJCLYfolNYNYO+oOKcKdPqfFgMR4KlXOfmyc7/a34yqGzdx8kYEA== X-Received: by 2002:a05:600c:630f:b0:47d:5d27:2a7f with SMTP id 5b1f17b1804b1-47d84b38534mr67895205e9.26.1767877881966; Thu, 08 Jan 2026 05:11:21 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 13/16] scripts/rustc_args: add --no-strict-cfg Date: Thu, 8 Jan 2026 14:10:40 +0100 Message-ID: <20260108131043.490084-14-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877940170158500 From: Marc-Andr=C3=A9 Lureau Allow to generate all --cfg flags, regardless of Cargo.toml content. We can't easily list and include all the features used by QAPI types. Access via #[cfg()] then requires #![allow(unexpected_cfgs)]. Signed-off-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini --- scripts/rust/rustc_args.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index 8098053720a..6a156b9608e 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -108,7 +108,7 @@ def generate_lint_flags(cargo_toml: CargoTOML) -> Itera= ble[str]: yield from lint.flags =20 =20 -def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str= ]: +def generate_cfg_flags(header: str, cargo_toml: Optional[CargoTOML]) -> It= erable[str]: """Converts defines from config[..].h headers to rustc --cfg flags.""" =20 with open(header, encoding=3D"utf-8") as cfg: @@ -117,8 +117,9 @@ def generate_cfg_flags(header: str, cargo_toml: CargoTO= ML) -> Iterable[str]: cfg_list =3D [] for cfg in config: name =3D cfg[0] - if f'cfg({name})' not in cargo_toml.check_cfg: - continue + if cargo_toml: + if f'cfg({name})' not in cargo_toml.check_cfg: + continue if len(cfg) >=3D 2 and cfg[1] !=3D "1": continue cfg_list.append("--cfg") @@ -179,6 +180,13 @@ def main() -> None: required=3DFalse, default=3D"1.0.0", ) + parser.add_argument( + "--no-strict-cfg", + help=3D"only generate expected cfg", + action=3D"store_false", + dest=3D"strict_cfg", + default=3DTrue, + ) args =3D parser.parse_args() if args.verbose: logging.basicConfig(level=3Dlogging.DEBUG) @@ -209,7 +217,7 @@ def main() -> None: print(f'cfg(feature,values("{feature}"))') =20 for header in args.config_headers: - for tok in generate_cfg_flags(header, cargo_toml): + for tok in generate_cfg_flags(header, cargo_toml if args.strict_cf= g else None): print(tok) =20 =20 --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877980; cv=none; d=zohomail.com; s=zohoarc; b=QN8nvPn92xc8Mt44RkTSDU3G6pmaPN2cR1eUmC/Q5Hea6hgsv3TrBj7lWlLVQ1G6jR0GuFw5SBG+5OlX1q4EqrK1KmjpHTHNKAv/pnJyDdXHFfzuPGZ6ucdZsGN7JhE+LeDm6YHtL60b9rdY3xu+VPjD4KOXvrr+zuS74rHM0mc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877980; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=X2CkPrmAjPj0SGqqtniQ7b64jK4eZjmHP1vEC37hTGs=; b=hUGZgT6jDkn58ilT0KKsEohZsa0AlsuvaUGSBL8X+91BzEK+YFDq3/WRzK1GPqK6CD5+5NK8o6iXUlZcVeZlBN4THa9i8SbTjnnhsuwkBB5NXzQUhcPS+Ec2e8/HlMjalqRTYwohHsX+n0bmHX+UCA4fah9qSUrB5YhXglbe6RM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877980799588.5101435243678; Thu, 8 Jan 2026 05:13:00 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnZ-000884-EL; Thu, 08 Jan 2026 08:11:33 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnX-00084T-Ju for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:31 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnW-000849-0D for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:31 -0500 Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-439-kohOy7-YPWiBPJ45wvXy5w-1; Thu, 08 Jan 2026 08:11:28 -0500 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-47a83800743so10536025e9.0 for ; Thu, 08 Jan 2026 05:11:27 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432bd5edb7esm15860259f8f.30.2026.01.08.05.11.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877889; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=X2CkPrmAjPj0SGqqtniQ7b64jK4eZjmHP1vEC37hTGs=; b=LKhYcgKvo6OkizESHScQjScrrGiEt3LZNVkHcJX0mc8tMEFL0ilKOEWbIoRjGCuQiVQpjH 2ZJTy0szc8F0mo6lSswHsn1dgWl3+22YVGy0mQH0UP8P21/BSzv2rqAh9gfHZemyiot5Eb Y8e+nh3oIYxRf7yg2b5vWrcdE5HARr8= X-MC-Unique: kohOy7-YPWiBPJ45wvXy5w-1 X-Mimecast-MFC-AGG-ID: kohOy7-YPWiBPJ45wvXy5w_1767877887 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877886; x=1768482686; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=X2CkPrmAjPj0SGqqtniQ7b64jK4eZjmHP1vEC37hTGs=; b=dh1xswT6ywq8EwIigCplt5NIekW7xxwgiJUn9B6+8cQcuNoWIN4YIQJ9DCHcPr3f8g YBVGGLqOxLpmHjrb61X7XYbJ7uP1GDJYiwP2Rc1342VmR/IM0wpYLw1mgOPZHOl+gtb0 95n4VWwpzVJ8xZFRaOQoWWFtUCapjJflk+GPe3WjssxFE4dbLyVb/vvZ0nAn2IerSVsL +reDzcr8hzLg/13C4ifEOOwYO3cODAhaGenZ5n4hQ1OAxmLrpbKv/oDJXwrVLBa9ejLp B408NMK0US/QHMEqrWgCMV2vHhMUoKpOO6D2hedPT0HTr/YEQOH75WNuJFEPu7UtJxZJ 8xLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877886; x=1768482686; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=X2CkPrmAjPj0SGqqtniQ7b64jK4eZjmHP1vEC37hTGs=; b=joeapbSABYwpyR7Z3q/9QU80y6m5+l/ONGSkvqQvNCG0MiD/NCoUnRB1W+74pRmlCt FkK3Gpd9q+TRlBsYFJgAvStUOuwSYUC6uo4vsdXAVqJo4tpQWRlWyCzgzNgHfyEak/gT oA2kRlWkGv/2CjNQHnn4rOBNanGcni+2htN5MjxJtGROzrtsslnBQy2xrA3ifY/eACMk 7Z7nzI17e8OZ9gKRLERKjdGO5DGi5F4Lz1gqrboTw4L+LCPayBciaoXUKa4ehX3YFURI wgzbm9sRHy/ZnmH5sVSgPtdEHmq5gtg4DUxt9Z90HfjCOko0OTmzFS8ateZfU7FtQQF3 is5w== X-Gm-Message-State: AOJu0Yy3NbyrO+pJk4wCRsLlnC4xNYuVwksTIFroWbUI+G3Rh0uIy/kG UO5mumR9WlJDfmsOMYa4JUYsmvJUV2PLMJQMB3+J+K0vngADFJCLQBNXOFvrGRtmTsrSjReqfRY HXs9XMeC19wUpjHGZvt9Fdm+efQgU+4QK9B7/rN+bnPdfkrCVZpJfcMUPVmJwAri9mxfyEMwdIN zSeEaR2YuTeOYvBwz0iHXK88rqAqwc9DEFjGWfgjTT X-Gm-Gg: AY/fxX7Q2vJy/7dMMM9XC7mF/2kkXKOEM+2b9gQ06149Q3o0wiXycDoYkTW/ZkONcN6 2HOF3hjIKiuep3g4BSL2VvFA22zXdhRXZUpSOLYm7r0jFMBwoK+lyNcT435dG+DBLaXZy2AiZrX h487RYoz0mAqc7d3ZbDJUCtRG6LQuVtkibRxVSlEFM9CrouuElDzuuBgZaLKICiOoXbLQdsOpQF YQuiVfZP0ZGQQKTCEqF7Xy7R6IaN30D2r6001APLCjEfV7435JRq5luM1rDroAtfG7B9wDi3+S6 GAXe1WUIE1zPyvqtisIgqGYOzJzOobKEZuzVSHfGxu3qkX+ya6mdpTVPcuAZ3AbPgXbH08HZQ6D G70knNOngMgn3e9n8DlRocrYKVzaMPk9zzT7mjNoS0G+Jdhf2nZQupk+yz0mmT/2S3vtkaveF1e yXyMBN4zXM8w8pBg== X-Received: by 2002:a05:600c:46c3:b0:477:a2f7:74de with SMTP id 5b1f17b1804b1-47d7f404854mr137698415e9.3.1767877886263; Thu, 08 Jan 2026 05:11:26 -0800 (PST) X-Google-Smtp-Source: AGHT+IEJOUy2wsdEPWdJd5w6lCP1oLrGJraiAqzUrHKYXtwpn0d+29LjKgOmAnUJLS+N29zBXbNPjA== X-Received: by 2002:a05:600c:46c3:b0:477:a2f7:74de with SMTP id 5b1f17b1804b1-47d7f404854mr137698135e9.3.1767877885878; Thu, 08 Jan 2026 05:11:25 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 14/16] rust/util: build QAPI types Date: Thu, 8 Jan 2026 14:10:41 +0100 Message-ID: <20260108131043.490084-15-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877982372158500 From: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini --- qapi/meson.build | 9 +++++++++ rust/Cargo.lock | 1 + rust/Cargo.toml | 2 +- rust/util/Cargo.toml | 1 + rust/util/meson.build | 17 +++++++++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/qapi/meson.build b/qapi/meson.build index a46269b5a0c..a019ec19db1 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -130,3 +130,12 @@ foreach output : qapi_outputs util_ss.add(qapi_files[i]) i =3D i + 1 endforeach + +# TODO: build together with the other files, perhaps when Rust is not +# optional and/or the Rust backend is complete (currently lacking +# commands, events, modules) +qapi_rs_files =3D custom_target('QAPI Rust', + output: 'qapi-types.rs', + input: [ files('qapi-schema.json') ], + command: [ qapi_gen, '-o', 'qapi', '-b', '@INPUT0@', '-B', 'qapi.backend= .QAPIRsBackend' ], + depend_files: [ qapi_inputs, qapi_gen_depends ]) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index b6c9f934140..7bca9b189db 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -454,6 +454,7 @@ dependencies =3D [ "glib-sys", "libc", "serde", + "serde_derive", ] =20 [[package]] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 45ebfa693a4..caddb93722e 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -20,7 +20,7 @@ anyhow =3D "~1.0" foreign =3D "~0.3.1" libc =3D "0.2.162" glib-sys =3D { version =3D "0.21.2", features =3D ["v2_66"] } -serde =3D "1.0.226" +serde =3D { version =3D "1.0.226", features =3D ["derive"] } serde_derive =3D "1.0.226" =20 [workspace.lints.rust] diff --git a/rust/util/Cargo.toml b/rust/util/Cargo.toml index 554004816eb..9f6c52c5acd 100644 --- a/rust/util/Cargo.toml +++ b/rust/util/Cargo.toml @@ -18,6 +18,7 @@ foreign =3D { workspace =3D true } glib-sys =3D { workspace =3D true } libc =3D { workspace =3D true } serde =3D { workspace =3D true } +serde_derive =3D { workspace =3D true } common =3D { path =3D "../common" } =20 [lints] diff --git a/rust/util/meson.build b/rust/util/meson.build index 92d27957eda..7987056962c 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -63,3 +63,20 @@ rust.doctest('rust-util-rs-doctests', dependencies: util_rs, suite: ['doc', 'rust'] ) + +_qapi_cfg =3D run_command(rustc_args, + '--no-strict-cfg', + '--config-headers', config_host_h, + capture: true, check: true).stdout().strip().splitlines() + +_qapi_rs =3D static_library( + 'qapi', + qapi_rs_files, + rust_args: _qapi_cfg, + override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], + rust_abi: 'rust', + link_with: [_util_rs], + dependencies: [common_rs, serde_rs], +) + +qapi_rs =3D declare_dependency(link_with: [_qapi_rs]) --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877928; cv=none; d=zohomail.com; s=zohoarc; b=Ez29KsFMNDzplDDQ/3EJyQ2kZiuV0++InskolvD36loEbCKPEX5oJu7hDYWdwEa1L/6hrhv5lQFWVXMyfrGIbt1evibNwPa3LWDlBKD8peOjmZ9Wu0E3P3CXCERJEUyWYBRO4k0IB2fgN5BwfpZPe3qexntX1kkrqD4aleIFvrc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877928; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=nyF4XBGwym7P72pkoLWyXPv/dFuoXRVdsycFcj1MYQ4=; b=hQeI1Q0V+jYhyumr8jXB+dPaemL4xJYnDU5CM/0gLWrMhQdduh2qXpOdj5JRZSB2dyZ1u3ryodBSxCxkfLzF3nzDkoPKEzE125hGgeUlIOP8iF+AiYKscjpf3/0M3N9QxOy5jGNEDA8z/NI3LGjr3nXzUHIp+zVQX6cuD0pTecs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877928469983.0366881716574; Thu, 8 Jan 2026 05:12:08 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnc-00089N-Ok; Thu, 08 Jan 2026 08:11:36 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpna-00088S-K9 for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:34 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnY-00084k-Vi for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:34 -0500 Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-388-IzAUtesNMW6i-gbouiPFXw-1; Thu, 08 Jan 2026 08:11:30 -0500 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-43009df5ab3so2062140f8f.1 for ; Thu, 08 Jan 2026 05:11:30 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432bd0e6784sm16315281f8f.19.2026.01.08.05.11.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877892; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=nyF4XBGwym7P72pkoLWyXPv/dFuoXRVdsycFcj1MYQ4=; b=PL5XQht5AlriPF5rYJem/HENfBDy4xBMexJNj+KJT6XX1V2RTGt/eNTjTRdlZ7q3zu3PyI rRBnJePGpHXwTSXpzaqw9bWQP2pe9xUxg65cDITXMiFcIjgbthwFpKJ/KNN6cbyn/OUSh6 fghs3+0mr7QsojahScUse6Lg7jq8UkU= X-MC-Unique: IzAUtesNMW6i-gbouiPFXw-1 X-Mimecast-MFC-AGG-ID: IzAUtesNMW6i-gbouiPFXw_1767877890 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877889; x=1768482689; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=nyF4XBGwym7P72pkoLWyXPv/dFuoXRVdsycFcj1MYQ4=; b=AVMXrEDupfTgJ6o6/Q3wrrhGVYKWaxruMIYlbh90kTb4UuIrRS8P6yDgXgZ4reD9dl EiXRCbkZtmzN+mBePeX3aoY9Qr8o3cyEaeMSqBSTDBr5oyDJyY0w8K/0LMg+BonXr9vg gCI3rpgowVw/X+9y1RA35/cAtlMBbWcGljEU5yQfhnly9TNo0F7q2sr7QZkfZKPIvOSV t0NiXjvU3dgtSbaFjuGEn3mEMDzn7HYdqJLOY/nXjI5YZdTV/MUMxW3nDapuHh+4Cs+l 4W44lYOiEAqCI1Ha9U30tGk4zBCUHYQH2UBnkz9nwVyixlc3MuC5dNFvXx8ifza27Xlk 562A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877889; x=1768482689; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=nyF4XBGwym7P72pkoLWyXPv/dFuoXRVdsycFcj1MYQ4=; b=OcAsUYjWDihcEULYKsxHF5TF+GW6fsH0Gns0DqormFX4fIxZ+ac33B0TgrHJEUi5nj PtzcpBcgMsl/LHaMftGb58dhCjJpxWyePazhcTLM/4NfwMmZBnRXXpG1JzOSYrCW1kpk DwHdcuDoy72S4Eciqt+xcV5angX3IPpFlgiApS9EXDrCe0Pgg8QlpDt3BgRVhu+kSqjQ kV57TmpEkJY5Hsz/1EKrXThoDZ0kMrsRr77ilfsRMSTfjjsJxIrxjiTEmPx3GUHGnDOu 2RGQL2ibSX9jHObCalhxcU6yhq8iAtYz1avvN/m7WFxrb83IDB/mh3ES+TVuygd1B8FH wq8g== X-Gm-Message-State: AOJu0Yyj0L8PeKH+655cVkg8vGrGi3sZMNG4dRATtfPGJRvSTLGJmC4A i+zJdWkb15MS7veFjx3M8996OrLPTl4URdzSkJEaKWxDXGvwMuRZNlLHZvGxwpO1zNw6oGiBTTs djMKHqOWOm/mujsy5WX0DzEpUlQmUUS/gJu8TVPz3WysoUEIx7C7Sh/2ToJJIp3HCuNlg3aDYb7 sFKpQ3RCsMs/N/D44sM4LyEqv7V93Lpn2KbK5w9Gwd X-Gm-Gg: AY/fxX4OFBW8mH17blUJgF/kb0bsguFacbuD0lzpg/E4mGYMtbLO047xIaAo9sFEGpR KkqxmobMSbbDHTUquqLJ3P9yEtRXGvrHnYhJ+zn51X0TkDYGNCaNdxsybH6S2BAZmyksW3UvN9g iJiH1BQtLjv5AIWi7fzzgk93BuGF42ANewfTEx2j1ZIqWs+nTmjuVn/CGW4i6enCF2QapYmY4zp g6XJfvBuTMasmOJ/39JbwQ1RPqPZAfx+chjJVKYRhZ7gDtYYY5XDpnhcGTHWHlJBtkOyh9GRc6W ecfxEyC3T7+NlyihQuttghetQcnFRMQfB69FS0gAb73oZmaGCtcl4xbd3fzg/cC6QnvSrGV+O8c Pg6sJtn2TKzMjt8QV/4myQnMNarNgjtHftMb/sXdqODE+xUbcTHAUuIWWHx02gDMmeIpWzoeRXv pixKWj6+5DwJ1R4A== X-Received: by 2002:a5d:5f51:0:b0:430:ff81:295d with SMTP id ffacd0b85a97d-432c374fc38mr8437619f8f.41.1767877888726; Thu, 08 Jan 2026 05:11:28 -0800 (PST) X-Google-Smtp-Source: AGHT+IEeYdz1HHfLuHgabdLWpi2Jvct5rCgEFFttH08X/7L6jRyLNg+eS5CiRzT/qYKozVSkJ61Bxw== X-Received: by 2002:a5d:5f51:0:b0:430:ff81:295d with SMTP id ffacd0b85a97d-432c374fc38mr8437582f8f.41.1767877888225; Thu, 08 Jan 2026 05:11:28 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 15/16] scripts/qapi: add serde attributes Date: Thu, 8 Jan 2026 14:10:42 +0100 Message-ID: <20260108131043.490084-16-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877930073158500 From: Marc-Andr=C3=A9 Lureau Generate serde attributes to match the serialization format to QAPI's: - for enums, map Rust enum variants to original QAPI names - for structs, rejects JSON with extra fields and omit optional fields (as opposed to serializing them as null) - for union variants: - use tagged union format matching QAPI's discriminator, - map variant names to original QAPI names - flatten union data into parent struct - for alternates, use type-based discrimination Signed-off-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini --- scripts/qapi/rs_types.py | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/scripts/qapi/rs_types.py b/scripts/qapi/rs_types.py index 64702eb54ae..fb3362ad601 100644 --- a/scripts/qapi/rs_types.py +++ b/scripts/qapi/rs_types.py @@ -33,6 +33,7 @@ =20 =20 objects_seen =3D set() +SERDE_SKIP_NONE =3D '#[serde(skip_serializing_if =3D "Option::is_none")]' =20 =20 def gen_rs_variants_to_tag(name: str, @@ -79,11 +80,13 @@ def gen_rs_variants(name: str, ret =3D mcgen(''' =20 %(cfg)s -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag =3D "%(tag)s")] pub enum %(rs_name)sVariant { ''', cfg=3Difcond.rsgen(), - rs_name=3Drs_name(name)) + rs_name=3Drs_name(name), + tag=3Dvariants.tag_member.name) =20 for var in variants.variants: type_name =3D var.type.name @@ -91,18 +94,22 @@ def gen_rs_variants(name: str, if type_name =3D=3D 'q_empty': ret +=3D mcgen(''' %(cfg)s + #[serde(rename =3D "%(rename)s")] %(var_name)s, ''', cfg=3Dvar.ifcond.rsgen(), - var_name=3Dvar_name) + var_name=3Dvar_name, + rename=3Dvar.name) else: ret +=3D mcgen(''' %(cfg)s + #[serde(rename =3D "%(rename)s")] %(var_name)s(%(rs_type)s), ''', cfg=3Dvar.ifcond.rsgen(), var_name=3Dvar_name, - rs_type=3Dvar.type.rs_type()) + rs_type=3Dvar.type.rs_type(), + rename=3Dvar.name) =20 ret +=3D mcgen(''' } @@ -158,9 +165,11 @@ def gen_struct_members(members: List[QAPISchemaObjectT= ypeMember], typ =3D 'Option<%s>' % typ ret +=3D mcgen(''' %(cfg)s + %(serde_skip_if)s pub %(rs_name)s: %(rs_type)s, ''', cfg=3Dmemb.ifcond.rsgen(), + serde_skip_if=3DSERDE_SKIP_NONE if memb.optional else= '', rs_type=3Dtyp, rs_name=3Drs_name(camel_to_lower(memb.name))) return ret @@ -181,17 +190,23 @@ def gen_rs_object(name: str, ret =3D '' objects_seen.add(name) =20 + serde_deny_unknown_fields =3D "#[serde(deny_unknown_fields)]" if variants: ret +=3D gen_rs_variants(name, ifcond, variants) + # we can't use because of the flatten unions + # serde FlatMapAccess should consume the fields? + serde_deny_unknown_fields =3D "" =20 ret +=3D mcgen(''' =20 %(cfg)s -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +%(serde_deny_unknown_fields)s pub struct %(rs_name)s { ''', cfg=3Difcond.rsgen(), - rs_name=3Drs_name(name)) + rs_name=3Drs_name(name), + serde_deny_unknown_fields=3Dserde_deny_unknown_fields) =20 if base: if not base.is_implicit(): @@ -213,6 +228,7 @@ def gen_rs_object(name: str, =20 if variants: ret +=3D mcgen(''' + #[serde(flatten)] pub u: %(rs_type)sVariant, ''', rs_type=3Drs_name(name)) ret +=3D mcgen(''' @@ -227,7 +243,7 @@ def gen_rs_enum(name: str, ret =3D mcgen(''' =20 %(cfg)s -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] ''', cfg=3Difcond.rsgen()) =20 @@ -243,10 +259,12 @@ def gen_rs_enum(name: str, for member in members: ret +=3D mcgen(''' %(cfg)s + #[serde(rename =3D "%(member_name)s")] %(c_enum)s, ''', cfg=3Dmember.ifcond.rsgen(), - c_enum=3Drs_name(camel_to_upper(member.name))) + c_enum=3Drs_name(camel_to_upper(member.name)), + member_name=3Dmember.name) ret +=3D '''} =20 ''' @@ -280,7 +298,8 @@ def gen_rs_alternate(name: str, =20 ret +=3D mcgen(''' %(cfg)s -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] pub enum %(rs_name)s { ''', cfg=3Difcond.rsgen(), @@ -330,6 +349,8 @@ def visit_begin(self, schema: QAPISchema) -> None: // that *could* be Eq too. #![allow(clippy::derive_partial_eq_without_eq)] =20 +use serde_derive::{Serialize, Deserialize}; + use util::qobject::QObject; ''')) =20 --=20 2.52.0 From nobody Sat Jan 31 19:05:17 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877983; cv=none; d=zohomail.com; s=zohoarc; b=JH2Vap6jVJWRqCPlPGt5wd0gUKKOITgqm2qoXmevsx2nAdg4ndNI5NhhLDQi0y29vZZ68g2zWUdqYLy9bH5v4Jv/AFZgyt4WG3j9uQhy9UdhOnd11tdcCfe1JfxxkKULR4HaExIuTYMEBqIfdQgmJiShYzYzT4/37WX9K75rauQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877983; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=Pl9MaI8akrkrPlYIE8JGPWnSFtcqCQUxWaCZczxL/vQ=; b=mv+9PjEihdOl28HLyKcAk3KnGYYW3hgP8UcsqCBtVHz6ZBJMQWlS60awZ9uDhaW65oQrJh8ri1WI2IChvDMes6xLKBTq9ZebWNFV7moCJmi0TnC8QkaaafWXJVPzos+Tk9vrLCdotmGYjHaRPa4pcrNOgiQEAWaRxcCsRCCeVyU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877983271340.76789039194546; Thu, 8 Jan 2026 05:13:03 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpng-0008FF-DR; Thu, 08 Jan 2026 08:11:40 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpne-0008BF-AZ for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:38 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnb-00085H-RK for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:38 -0500 Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-696-HPnyAdv3OSid3qjanDRgmg-1; Thu, 08 Jan 2026 08:11:33 -0500 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-477bf8c1413so21894435e9.1 for ; Thu, 08 Jan 2026 05:11:33 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d7f418538sm150823175e9.5.2026.01.08.05.11.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877894; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Pl9MaI8akrkrPlYIE8JGPWnSFtcqCQUxWaCZczxL/vQ=; b=AvKoF2PD3DSi7u1+KpnmHn6CJ0nepHLOc3ArNn99g05gwAxXJJEWFGcFLHEz1nCVvYOabX 9p7Y/h//7izrMIerIQiurRvkbfpkEMCyzoetq/+3/nOoBJcPNVTpoBo0SDFaHuxi3w9UkK nl7SgLsP2pnENc5Jffnv0tkUVS+h4+U= X-MC-Unique: HPnyAdv3OSid3qjanDRgmg-1 X-Mimecast-MFC-AGG-ID: HPnyAdv3OSid3qjanDRgmg_1767877892 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877891; x=1768482691; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Pl9MaI8akrkrPlYIE8JGPWnSFtcqCQUxWaCZczxL/vQ=; b=eTlAbxti5lPJuJqlScJ/Mpz9iPyVwB3OeaOyaVi3kM/Qz9z9rJu+tBpSvCHoMI5joz ZFDt8deUyEAKVKaf0tZvTXS+iBaFxIJZafG8dVzDOW2RiDszqasXDcJCjzAyEBlTqcx3 LmBukw2otXnPm9/Ep7fH2a1A+OsoPalTWyyCCIxhT+D3M+Vs5D/SaB+irvzt+4XhjWji qCq9Z7sUudVpDZ3ZACdN8O7uiF229tWFkW4DSiSebuCfG9cu9lJNUmRc27i29Uu5yrzH X2+LFglb7kQA3F3G7LgVKSPqu2Ah+JzyjtyOGtj7pCuIu6k0H+8vy7g+9fd2P1eaeLD7 sseQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877891; x=1768482691; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Pl9MaI8akrkrPlYIE8JGPWnSFtcqCQUxWaCZczxL/vQ=; b=cEE97ckqnEq2YOoP4lA0/GLX6N0antxHjMO83hGNo32PsbnZaNwgt+5qSZNzMdemci 9L/R2ZYV1830MIGwtLZo+No/A/lLCzVJ4SxwxLRIZgnhoklg+hsmUL9euOCt+WGXUDV6 87yl2aniRWL76X6xrqshYGPQa596dEjko/YUZ8i0gR/I6Lrddwji1prB+shn+pjmwu0/ Uzg6yRJnylxA8VvDgTidY21vkwf+82caO3KO0LUq8SqJLYW9fNc27NMMbeqcqo+UgUrr JBngWowRrIY4HmN1H0GUQKb4H9gxo/ImjEVDYXcgvAiFAIgbbxtGDDCffgAPWlGWId3d 3mRA== X-Gm-Message-State: AOJu0Ywjdgfl7fTh1wVNbnIh9tcVuxqiV7Qp0jxHAB/DN6ADijOSKgTP gD4jIUDiOc81N3Lkxkl4w9629xOwDrVZM+06uK6H93vjQL6jK3N9TjHyBhZOFRt5gY4jwM6rtBs 8jyVAbGJlNG4vd3Mr2xzMSLSW77HyRn3TmestENriljureA9/oP8RkyKA4iio0PupzFvS0CKehi gJ+pbS7bJpXIgh1w7tnsL64B/DAshee8Z5f9TVypFb X-Gm-Gg: AY/fxX5AfGZbAZF8WMHYsx+e7AqfBILm+P/Yflsq0sB4FZQF089RVHGQMtjKu1BnAbV Sth8DdelTTWbFxvnE0eGm9cZYQ3+CiikerT8GqJ6H6EC5t2l9iahLO5RxSx5WAy/BFAk34pWGmf Xb5NoWaTjS+bzBGiGqRjoVElPcgxzTAwpMrOas4Rna1kmVLb0+5fSWNv05DrOPrxjLc7Qbvbqys 3KgE7sRYOYMYwMFV+IMqyzu0cK2OAtsOU49eGFsmH44Mxr5k7ovzi8wZDKrUzyXfaAaq3OIS210 YIqIk0yL4fHlom2CDlJnuhb4tvlPbyST+gKvCV+1Bik0KRhr+w5LosMh+TC4CNFzmMnNNG0U2f4 j6XXnO9f9yJigAIOWuXwD5al4rjCf5L6zZbzZZIhXHiAQga3iz21jbMDbhq2HpwL6/RwBRPs75Z 1fI4kf+/rkLv2QzQ== X-Received: by 2002:a05:600c:82c3:b0:477:9392:8557 with SMTP id 5b1f17b1804b1-47d84b3463fmr67039885e9.18.1767877891218; Thu, 08 Jan 2026 05:11:31 -0800 (PST) X-Google-Smtp-Source: AGHT+IE7jTJIl7PPoheEFtg3b1V3QiQigsyWyeXNqsvJj4L6m2tw3NnRWiGyNREBEWux8acQiI7JbA== X-Received: by 2002:a05:600c:82c3:b0:477:9392:8557 with SMTP id 5b1f17b1804b1-47d84b3463fmr67039595e9.18.1767877890628; Thu, 08 Jan 2026 05:11:30 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org Subject: [PATCH v2 16/16] rust/tests: QAPI integration tests Date: Thu, 8 Jan 2026 14:10:43 +0100 Message-ID: <20260108131043.490084-17-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877984387158500 From: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini --- rust/tests/meson.build | 21 +- rust/tests/tests/integration.rs | 2 + rust/tests/tests/qapi.rs | 444 ++++++++++++++++++++++++++++++++ 3 files changed, 464 insertions(+), 3 deletions(-) create mode 100644 rust/tests/tests/integration.rs create mode 100644 rust/tests/tests/qapi.rs diff --git a/rust/tests/meson.build b/rust/tests/meson.build index 3c5020490b0..d781b52bd78 100644 --- a/rust/tests/meson.build +++ b/rust/tests/meson.build @@ -1,10 +1,25 @@ +test_qapi_rs_files =3D custom_target('QAPI Rust', + output: 'test-qapi-types.rs', + input: [ files(meson.project_source_root() + '/tests/qapi-schema/qapi-sc= hema-test.json') ], + command: [ qapi_gen, '-o', meson.current_build_dir(), '-b', '@INPUT0@', = '-B', 'qapi.backend.QAPIRsBackend', '-p', 'test-' ], + depend_files: [ qapi_inputs, qapi_gen_depends ]) + +_test_qapi_rs =3D static_library( + 'test_qapi', + test_qapi_rs_files, + override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], + rust_abi: 'rust', + dependencies: [common_rs, util_rs, serde_rs, serde_derive_rs]) + +test_qapi_rs =3D declare_dependency(link_with: [_test_qapi_rs]) + test('rust-integration', executable( 'rust-integration', - files('tests/vmstate_tests.rs'), - rust_args: ['--test'], + files('tests/integration.rs'), + rust_args: ['--test'] + _qapi_cfg, install: false, - dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs]), + dependencies: [bql_rs, common_rs, util_rs, migration_rs, qom_rs, q= api_rs, test_qapi_rs]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', diff --git a/rust/tests/tests/integration.rs b/rust/tests/tests/integration= .rs new file mode 100644 index 00000000000..ebc17cb5550 --- /dev/null +++ b/rust/tests/tests/integration.rs @@ -0,0 +1,2 @@ +mod qapi; +mod vmstate_tests; diff --git a/rust/tests/tests/qapi.rs b/rust/tests/tests/qapi.rs new file mode 100644 index 00000000000..f8a585e5802 --- /dev/null +++ b/rust/tests/tests/qapi.rs @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#![allow(unexpected_cfgs)] +#![allow(clippy::shadow_unrelated)] + +use util::qobject::{from_qobject, to_qobject, QObject}; + +#[test] +fn test_char() { + let json =3D "\"v\""; + let qo =3D QObject::from_json(json).unwrap(); + let c: char =3D from_qobject(qo).unwrap(); + assert_eq!(c, 'v'); + assert_eq!(to_qobject(c).unwrap().to_json(), json); + + let json =3D "'va'"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); +} + +#[test] +fn test_enum() { + let json =3D "\"value1\""; + let qo =3D QObject::from_json(json).unwrap(); + let e: test_qapi::EnumOne =3D from_qobject(qo).unwrap(); + assert_eq!(e, test_qapi::EnumOne::VALUE1); + assert_eq!(to_qobject(e).unwrap().to_json(), json); +} + +#[test] +fn test_struct() { + let expected =3D test_qapi::TestStruct { + integer: -42, + boolean: true, + string: "foo".into(), + }; + let json =3D "{\"integer\": -42, \"boolean\": true, \"string\": \"foo\= "}"; + let qo =3D QObject::from_json(json).unwrap(); + let ts: test_qapi::TestStruct =3D from_qobject(qo).unwrap(); + assert_eq!(ts, expected); + assert_eq!(to_qobject(ts).unwrap().to_json(), json); +} + +#[test] +fn test_struct_nested() { + let expected =3D test_qapi::UserDefTwo { + string0: "string0".into(), + dict1: test_qapi::UserDefTwoDict { + string1: "string1".into(), + dict2: test_qapi::UserDefTwoDictDict { + userdef: test_qapi::UserDefOne { + integer: 42, + string: "string".into(), + enum1: None, + }, + string: "string2".into(), + }, + dict3: None, + }, + }; + let json =3D "{\"string0\": \"string0\", \"dict1\": {\"dict2\": {\"str= ing\": \"string2\", \ + \"userdef\": {\"integer\": 42, \"string\": \"string\"}}, \= "string1\": \ + \"string1\"}}"; + let qo =3D QObject::from_json(json).unwrap(); + let udt: test_qapi::UserDefTwo =3D from_qobject(qo).unwrap(); + assert_eq!(udt, expected); + assert_eq!(to_qobject(udt).unwrap().to_json(), json); +} + +#[test] +fn test_list() { + let expected =3D [ + test_qapi::UserDefOne { + integer: 42, + string: "string0".into(), + enum1: None, + }, + test_qapi::UserDefOne { + integer: 43, + string: "string1".into(), + enum1: None, + }, + test_qapi::UserDefOne { + integer: 44, + string: "string2".into(), + enum1: None, + }, + ]; + let json =3D "[{\"integer\": 42, \"string\": \"string0\"}, {\"integer\= ": 43, \"string\": \ + \"string1\"}, {\"integer\": 44, \"string\": \"string2\"}]"; + let qo =3D QObject::from_json(json).unwrap(); + let ud_list: Vec =3D from_qobject(qo).unwrap(); + assert_eq!(ud_list, expected); + assert_eq!(to_qobject(ud_list).unwrap().to_json(), json); +} + +#[test] +fn test_flat_union() { + let expected =3D test_qapi::UserDefFlatUnion { + integer: 41, + string: "str".into(), + u: test_qapi::UserDefFlatUnionVariant::Value1(test_qapi::UserDefA { + boolean: true, + a_b: None, + }), + }; + let json =3D "{\"integer\": 41, \"boolean\": true, \"enum1\": \"value1= \", \"string\": \"str\"}"; + let qo =3D QObject::from_json(json).unwrap(); + let ud_fu: test_qapi::UserDefFlatUnion =3D from_qobject(qo).unwrap(); + assert_eq!(ud_fu, expected); + assert_eq!(to_qobject(ud_fu).unwrap().to_json(), json); +} + +#[test] +fn test_union_in_union() { + let expected =3D test_qapi::TestUnionInUnion { + u: test_qapi::TestUnionInUnionVariant::ValueA(test_qapi::TestUnion= TypeA { + u: test_qapi::TestUnionTypeAVariant::ValueA1(test_qapi::TestUn= ionTypeA1 { + integer: 2, + name: "fish".into(), + }), + }), + }; + let json =3D + "{\"name\": \"fish\", \"integer\": 2, \"type-a\": \"value-a1\", \"= type\": \"value-a\"}"; + let qo =3D QObject::from_json(json).unwrap(); + let uu: test_qapi::TestUnionInUnion =3D from_qobject(qo).unwrap(); + assert_eq!(uu, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::TestUnionInUnion { + u: test_qapi::TestUnionInUnionVariant::ValueA(test_qapi::TestUnion= TypeA { + u: test_qapi::TestUnionTypeAVariant::ValueA2(test_qapi::TestUn= ionTypeA2 { + integer: 1729, + size: 87539319, + }), + }), + }; + let json =3D + "{\"integer\": 1729, \"type-a\": \"value-a2\", \"size\": 87539319,= \"type\": \"value-a\"}"; + let qo =3D QObject::from_json(json).unwrap(); + let uu: test_qapi::TestUnionInUnion =3D from_qobject(qo).unwrap(); + assert_eq!(uu, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::TestUnionInUnion { + u: test_qapi::TestUnionInUnionVariant::ValueB(test_qapi::TestUnion= TypeB { + integer: 1729, + onoff: true, + }), + }; + let json =3D "{\"integer\": 1729, \"onoff\": true, \"type\": \"value-b= \"}"; + let qo =3D QObject::from_json(json).unwrap(); + let uu: test_qapi::TestUnionInUnion =3D from_qobject(qo).unwrap(); + assert_eq!(uu, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); +} + +#[test] +fn test_alternate() { + let expected =3D test_qapi::UserDefAlternate::I(42); + let json =3D "42"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::UserDefAlternate =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::UserDefAlternate::E(test_qapi::EnumOne::VA= LUE1); + let json =3D "\"value1\""; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::UserDefAlternate =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::UserDefAlternate::N(()); + let json =3D "null"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::UserDefAlternate =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::UserDefAlternate::Udfu(test_qapi::UserDefF= latUnion { + integer: 42, + string: "str".to_string(), + u: test_qapi::UserDefFlatUnionVariant::Value1(test_qapi::UserDefA { + boolean: true, + a_b: None, + }), + }); + let json =3D "{\"integer\": 42, \"boolean\": true, \"enum1\": \"value1= \", \"string\": \"str\"}"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::UserDefAlternate =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::WrapAlternate { + alt: test_qapi::UserDefAlternate::I(42), + }; + let json =3D "{\"alt\": 42}"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::WrapAlternate =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::WrapAlternate { + alt: test_qapi::UserDefAlternate::E(test_qapi::EnumOne::VALUE1), + }; + let json =3D "{\"alt\": \"value1\"}"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::WrapAlternate =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::WrapAlternate { + alt: test_qapi::UserDefAlternate::Udfu(test_qapi::UserDefFlatUnion= { + integer: 1, + string: "str".to_string(), + u: test_qapi::UserDefFlatUnionVariant::Value1(test_qapi::UserD= efA { + boolean: true, + a_b: None, + }), + }), + }; + let json =3D "{\"alt\": {\"integer\": 1, \"boolean\": true, \"enum1\":= \"value1\", \"string\": \ + \"str\"}}"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::WrapAlternate =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); +} + +#[test] +fn test_alternate_number() { + let expected =3D test_qapi::AltEnumNum::N(42.0); + let json =3D "42"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::AltEnumNum =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::AltNumEnum::N(42.0); + let json =3D "42"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::AltNumEnum =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::AltEnumInt::I(42); + let json =3D "42"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::AltEnumInt =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::AltListInt::I(42); + let json =3D "42"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::AltListInt =3D from_qobject(qo).unwrap(); + assert_eq!(&uda, &expected); + assert_eq!(to_qobject(&expected).unwrap().to_json(), json); + + // double + let json =3D "42.5"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); + + let expected =3D test_qapi::AltEnumNum::N(42.5); + let json =3D "42.5"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::AltEnumNum =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let expected =3D test_qapi::AltNumEnum::N(42.5); + let json =3D "42.5"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::AltNumEnum =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); + + let json =3D "42.5"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); +} + +#[test] +fn test_alternate_list() { + let expected =3D test_qapi::AltListInt::L(vec![42, 43, 44]); + let json =3D "[42, 43, 44]"; + let qo =3D QObject::from_json(json).unwrap(); + let uda: test_qapi::AltListInt =3D from_qobject(qo).unwrap(); + assert_eq!(uda, expected); + assert_eq!(to_qobject(expected).unwrap().to_json(), json); +} + +#[test] +fn test_errors() { + let json =3D "{ 'integer': false, 'boolean': 'foo', 'string': -42 }"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); + + let json =3D "[ '1', '2', false, '3' ]"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::>(qo).unwrap_err(); + + let json =3D "{ 'str': 'hi' }"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); + + let json =3D "{}"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); +} + +#[test] +fn test_wrong_type() { + let json =3D "[]"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); + + let json =3D "{}"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::>(qo).unwrap_err(); + + let json =3D "1"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); + + let json =3D "{}"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); + + let json =3D "1"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::>(qo).unwrap_err(); + + let json =3D "[]"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); +} + +#[test] +fn test_fail_struct() { + let json =3D "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'ext= ra': 42 }"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); +} + +#[test] +fn test_fail_struct_nested() { + let json =3D "{ 'string0': 'string0', 'dict1': { 'string1': 'string1',= 'dict2': { 'userdef1': { \ + 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo= ':'bar'}] }, 'string2': \ + 'string2'}}}"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::(qo).unwrap_err(); +} + +#[test] +fn test_fail_struct_in_list() { + let json =3D "[ { 'string': 'string0', 'integer': 42 }, { 'string': 's= tring1', 'integer': 43 }, \ + { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::>(qo).unwrap_err(); +} + +#[test] +fn test_fail_union_flat() { + let json =3D "{ 'enum1': 'value2', 'string': 'c', 'integer': 41, 'bool= ean': true }"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::>(qo).unwrap_err(); +} + +#[test] +fn test_fail_union_flat_no_discrim() { + // test situation where discriminator field ('enum1' here) is missing + let json =3D "{ 'integer': 42, 'string': 'c', 'string1': 'd', 'string2= ': 'e' }"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::>(qo).unwrap_err(); +} + +#[test] +fn test_fail_alternate() { + let json =3D "3.14"; + let qo =3D QObject::from_json(json).unwrap(); + from_qobject::>(qo).unwrap_err(); +} + +#[test] +fn test_qapi() { + let expected =3D qapi::InetSocketAddress { + host: "host-val".to_string(), + port: "port-val".to_string(), + numeric: None, + to: None, + ipv4: None, + ipv6: None, + keep_alive: None, + #[cfg(HAVE_TCP_KEEPCNT)] + keep_alive_count: None, + #[cfg(HAVE_TCP_KEEPIDLE)] + keep_alive_idle: Some(42), + #[cfg(HAVE_TCP_KEEPINTVL)] + keep_alive_interval: None, + #[cfg(HAVE_IPPROTO_MPTCP)] + mptcp: None, + }; + + let qsa =3D to_qobject(&expected).unwrap(); + let json =3D qsa.to_json(); + assert_eq!( + json, + "{\"port\": \"port-val\", \"keep_alive_idle\": 42, \"host\": \"hos= t-val\"}" + ); + let sa: qapi::InetSocketAddress =3D from_qobject(qsa).unwrap(); + assert_eq!(sa, expected); + + let expected =3D qapi::SocketAddressVariant::Inet(expected); + let qsav =3D to_qobject(&expected).unwrap(); + let json =3D qsav.to_json(); + assert_eq!( + json, + "{\"port\": \"port-val\", \"keep_alive_idle\": 42, \"host\": \"hos= t-val\", \"type\": \ + \"inet\"}" + ); + let sav: qapi::SocketAddressVariant =3D from_qobject(qsav).unwrap(); + assert_eq!(sav, expected); + + let expected =3D qapi::Qcow2BitmapInfo { + name: "name-val".to_string(), + granularity: 4096, + flags: vec![ + qapi::Qcow2BitmapInfoFlags::IN_USE, + qapi::Qcow2BitmapInfoFlags::AUTO, + ], + }; + let qbi =3D to_qobject(&expected).unwrap(); + let json =3D qbi.to_json(); + assert_eq!( + json, + "{\"flags\": [\"in-use\", \"auto\"], \"name\": \"name-val\", \"gra= nularity\": 4096}" + ); + let bi: qapi::Qcow2BitmapInfo =3D from_qobject(qbi).unwrap(); + assert_eq!(bi, expected); +} --=20 2.52.0