From nobody Fri Nov 14 18:19:13 2025 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=1760109275; cv=none; d=zohomail.com; s=zohoarc; b=kQUdXyc6TXBT3s9Bafgwal11seGoO5rZOMnV2kE2JKydIcL6jUWmfLYAEPN7yMmqA2dGWwoCrBLTkBWo0hpIcRtz1K2AsCgFgfstBNuUyXSHlknRS8imfystzFKNBdE//95a1jvKAd2mCg3M2xCDE7JkN1iVFBu05BL7fY2ktXs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109275; 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=FCuQFa5fX4KQKF7cPe8uVRBcotSflZGTjBWBJT+Gw1A=; b=l/n08V8OXbN8uE7b5xUWfI2rOGNVxFePMqV7YZdQpQSeylwSVcO9Gb3DaaOFtimu13PLzhAp0nwZAaiRjbOFxg4bvI/lLPoNPYT+d9/hWlzNLVN+f8njHkR2cjVv4AZXhwU3Um47CQkc4LGqpFQ27AmHGcFofi5nh/TrlH1iDkQ= 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 1760109275539874.2774855869051; Fri, 10 Oct 2025 08:14:35 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7ElQ-0008LA-9e; Fri, 10 Oct 2025 11:10:36 -0400 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 1v7ElK-0008IH-QF for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:30 -0400 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 1v7El7-00042W-8s for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:30 -0400 Received: from mail-ed1-f71.google.com (mail-ed1-f71.google.com [209.85.208.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-594-jef618aBNuSVrW4vuVYYJA-1; Fri, 10 Oct 2025 11:10:12 -0400 Received: by mail-ed1-f71.google.com with SMTP id 4fb4d7f45d1cf-6349af0e766so2508832a12.3 for ; Fri, 10 Oct 2025 08:10:12 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-63a5c32249esm2454971a12.41.2025.10.10.08.10.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109014; 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=FCuQFa5fX4KQKF7cPe8uVRBcotSflZGTjBWBJT+Gw1A=; b=CUEZPO0MmUV8onUngrlXt+EVf7ZLiP1J3ZMocvG78bYdWKyZm48KVXQ5gi8hqr+k17vzo+ Lt1zFbDtq/2x92M50oGW0GqkI1/E8uBoV36g66S5HucODENLzixJ0xd/jNfyW75/6gXajl oEJ3GVVkzBm2z7o2ModRzgYau/HxkRw= X-MC-Unique: jef618aBNuSVrW4vuVYYJA-1 X-Mimecast-MFC-AGG-ID: jef618aBNuSVrW4vuVYYJA_1760109012 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109011; x=1760713811; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=FCuQFa5fX4KQKF7cPe8uVRBcotSflZGTjBWBJT+Gw1A=; b=smCFdhm38ExnLq7LCkHGy0rBjKOHdVlc+FFXx9E41I42iXMlRpg3RMdd095rVT2m9c aQ/sSrlgxLp0VIDQeW0zTdP3CTdHNvKGgHjXeD6RdsELmLm3C2WZcFVexoHsh2e3F7P+ hkdkHyKqBpnzod6xj6j4BJQ/mNP4bbphtgbpYDuAN2THuMWNrHWONCckoufnUEoYvuto Tv/wAuMbAAF0bSZ50Oc6gngt0tH2y1WYeo2y794yfemTB8j/DD7Abhx3tTlvTarkf5qh H7oi7KFZhHg94EbJMu+u6C26qx8vxYKg+qVCAgI2TjbgJz3iYIPIp/1LvqY430eWYGH+ dyhA== X-Gm-Message-State: AOJu0YziCjkdt2T748RlJGsiO88SITj5sZVCuteC8zZtwqRsWF9+SNut 5UJyNbKNBp2MnzcCJP7WJ3rP8rwT0MCavFI9GvMAE+x0i+dzVwRqeQz1WW15LUSnsk0EmqvGEGZ WX8Xf9hM/6tt2u8fU/PgJa7xEiaqtEKxybOXQWTXJKcjszIyq7dG9CI35lQSlthjyscS+pBuMZi ZNPxqKTugmtixtXKp5+8C9CgoW7rO5sEuDcpMjdZXV X-Gm-Gg: ASbGnctZEwT21OndyX6V65s9V16Vf2Q3lWjlJm8PgmmEMrBo2093Eomye2wIvc86eQa Oq8EkvFZRvv66PDLl4FhRk4o32hxE+eXTwPI3JKb22BKTX72KyH9dq4vlb+KLWuzyjiYXeZFUi1 SwA28M2SRBScvR5g4p0wWp/qrW6248VxCbeagMz7WFqVYuSj6YB+wNd/S68bXQP1Y2JtgMi+wbO +D77x/cNOKneJK6CODX3pyoqy5RKR3Wwr82E+2h8LRZ7zmhG7A21LLfcDdL4yY6HmpdZUe8YuX5 hs09lVRSS5B8iC/wuwbCtOvsWkYnoH4+d2E4Z1GyKceSjvb+jOrbBiWEj4qEq3WQCLSqRmP6Byp UqzKkO7uTYJvce2iAQI/eCt1PtZTvp0f5bpZs7yxxoNVj X-Received: by 2002:aa7:dcca:0:b0:636:240f:9ece with SMTP id 4fb4d7f45d1cf-639d5c75d2amr8827969a12.34.1760109010808; Fri, 10 Oct 2025 08:10:10 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFWT7mfaLiPP25ccYmgmOyWXrCLTdr+R4Lpky3/nq8T98ppQJkLXVDDiXep81ijjnycbqtbSg== X-Received: by 2002:aa7:dcca:0:b0:636:240f:9ece with SMTP id 4fb4d7f45d1cf-639d5c75d2amr8827942a12.34.1760109010199; Fri, 10 Oct 2025 08:10:10 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 01/19] util: add ensure macro Date: Fri, 10 Oct 2025 17:09:46 +0200 Message-ID: <20251010151006.791038-2-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 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, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, T_SPF_TEMPERROR=0.01 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: 1760109277912154100 Content-Type: text/plain; charset="utf-8" The macro is similar to anyhow::ensure but uses QEMU's variation on anyhow::Error. It can be used to easily check a condition and format an error message. Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/device.rs | 21 ++++++----- rust/hw/timer/hpet/src/fw_cfg.rs | 7 ++-- rust/util/src/error.rs | 65 ++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 13 deletions(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/devi= ce.rs index 86638c07666..4218372a2a1 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -25,7 +25,10 @@ bindings::{address_space_memory, address_space_stl_le, hwaddr}, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSP= ECIFIED, }; -use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}; +use util::{ + ensure, + timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, +}; =20 use crate::fw_cfg::HPETFwConfig; =20 @@ -728,14 +731,14 @@ fn post_init(&self) { } =20 fn realize(&self) -> util::Result<()> { - if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX= _TIMERS { - Err(format!( - "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HP= ET_MAX_TIMERS}" - ))?; - } - if self.int_route_cap =3D=3D 0 { - Err("hpet.hpet-intcap property not initialized")?; - } + ensure!( + (HPET_MIN_TIMERS..=3DHPET_MAX_TIMERS).contains(&self.num_timer= s), + "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_M= AX_TIMERS}" + ); + ensure!( + self.int_route_cap !=3D 0, + "hpet.hpet-intcap property not initialized" + ); =20 self.hpet_id.set(HPETFwConfig::assign_hpet_id()?); =20 diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_c= fg.rs index e569b57b93b..27b3b2495a0 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -5,6 +5,7 @@ use std::ptr::addr_of_mut; =20 use common::Zeroable; +use util::{self, ensure}; =20 /// Each `HPETState` represents a Event Timer Block. The v1 spec supports /// up to 8 blocks. QEMU only uses 1 block (in PC machine). @@ -36,7 +37,7 @@ unsafe impl Zeroable for HPETFwConfig {} }; =20 impl HPETFwConfig { - pub(crate) fn assign_hpet_id() -> Result { + pub(crate) fn assign_hpet_id() -> util::Result { assert!(bql::is_locked()); // SAFETY: all accesses go through these methods, which guarantee // that the accesses are protected by the BQL. @@ -47,9 +48,7 @@ pub(crate) fn assign_hpet_id() -> Result { fw_cfg.count =3D 0; } =20 - if fw_cfg.count =3D=3D 8 { - Err("Only 8 instances of HPET are allowed")?; - } + ensure!(fw_cfg.count !=3D 8, "Only 8 instances of HPET are allowed= "); =20 let id: usize =3D fw_cfg.count.into(); fw_cfg.count +=3D 1; diff --git a/rust/util/src/error.rs b/rust/util/src/error.rs index bfa5a8685bc..20b8e7d5af5 100644 --- a/rust/util/src/error.rs +++ b/rust/util/src/error.rs @@ -86,6 +86,19 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { } } =20 +impl From> for Error { + #[track_caller] + fn from(msg: Cow<'static, str>) -> Self { + let location =3D panic::Location::caller(); + Error { + msg: Some(msg), + cause: None, + file: location.file(), + line: location.line(), + } + } +} + impl From for Error { #[track_caller] fn from(msg: String) -> Self { @@ -126,6 +139,17 @@ fn from(error: anyhow::Error) -> Self { } =20 impl Error { + #[track_caller] + #[doc(hidden)] + pub fn format(args: fmt::Arguments) -> Self { + if let Some(msg) =3D args.as_str() { + Self::from(msg) + } else { + let msg =3D std::fmt::format(args); + Self::from(msg) + } + } + /// Create a new error, prepending `msg` to the /// description of `cause` #[track_caller] @@ -311,6 +335,47 @@ unsafe fn cloned_from_foreign(c_error: *const bindings= ::Error) -> Self { } } =20 +/// Ensure that a condition is true, returning an error if it is false. +/// +/// This macro is similar to [`anyhow::ensure`] but returns a QEMU [`Resul= t`]. +/// If the condition evaluates to `false`, the macro returns early with an= error +/// constructed from the provided message. +/// +/// # Examples +/// +/// ``` +/// # use util::{ensure, Result}; +/// # fn check_positive(x: i32) -> Result<()> { +/// ensure!(x > 0, "value must be positive"); +/// # Ok(()) +/// # } +/// ``` +/// +/// ``` +/// # use util::{ensure, Result}; +/// # const MIN: i32 =3D 123; +/// # const MAX: i32 =3D 456; +/// # fn check_range(x: i32) -> Result<()> { +/// ensure!(x >=3D MIN && x <=3D MAX, "{} not between {} and {}", x, MIN, = MAX); +/// # Ok(()) +/// # } +/// ``` +#[macro_export] +macro_rules! ensure { + ($cond:expr, $fmt:literal, $($arg:tt)*) =3D> { + if !$cond { + let e =3D $crate::Error::format(format_args!($fmt, $($arg)*)); + return $crate::Result::Err(e); + } + }; + ($cond:expr, $err:expr $(,)?) =3D> { + if !$cond { + let s =3D ::std::borrow::Cow::<'static, str>::from($err); + return $crate::Result::Err(s.into()); + } + }; +} + #[cfg(test)] mod tests { use std::ffi::CStr; --=20 2.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109152; cv=none; d=zohomail.com; s=zohoarc; b=F4YvzDUFwnH80kZTklxBx6f8x1bwBsdkHpbl8JaI36dw6xxTjY0Xew3GHZuzK1RrcJnXHuqGj66eO3a89qQVZOP1WabHHM9zIKlyFMEwJUlwOiuMBpj9KOhNrjCBTuluiuwoN6nVxQ49d26ThRORd3kk+HpfEv6wyLQ5lV95isU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109152; 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=1p2DJoRVBR8tV1MAyYgxZfuil60xzN5SPRhSiWSYteE=; b=aWnwlgKBHCaq123U2D6Sfn/aesxyzsE5vMj4Hq3urqvclkezXWkizvHYZIGllpJdaZMpemHUBILZGcUJufN52oIdgtcC2GyxJ1P5ZgwyJar4r+qZ9F1kf04K4kaUR8R7pHDsaLTpezq07VbnVNQmVyMgLbb1uY3U/+5ljnbSets= 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 1760109152883871.2171166613778; Fri, 10 Oct 2025 08:12:32 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Ela-0008Qf-1s; Fri, 10 Oct 2025 11:10:46 -0400 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 1v7ElY-0008Pf-UW for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:44 -0400 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 1v7ElG-00043o-PI for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:44 -0400 Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-693-IJoMKxw4OOiOK_AV9Kux5g-1; Fri, 10 Oct 2025 11:10:15 -0400 Received: by mail-ej1-f70.google.com with SMTP id a640c23a62f3a-b48635cf06fso365942866b.1 for ; Fri, 10 Oct 2025 08:10:15 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d952a273sm266264866b.83.2025.10.10.08.10.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109020; 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=1p2DJoRVBR8tV1MAyYgxZfuil60xzN5SPRhSiWSYteE=; b=EDDBiNVyt0Z5dLmKlnh6yFEqZsrmXgg2BsnmdjqN2j9GdQuh9+WAdoZ2mmYnLzJJUFxxoJ x/vq9yBzKrljSUhe0zdein5YrQXOA3heHFYzHYUePxSIO82y5ilEnmPGfK+vfu+QM41Vl9 O/hcGIVX5sZJxLcGPqhZqGiiuphkdII= X-MC-Unique: IJoMKxw4OOiOK_AV9Kux5g-1 X-Mimecast-MFC-AGG-ID: IJoMKxw4OOiOK_AV9Kux5g_1760109014 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109013; x=1760713813; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1p2DJoRVBR8tV1MAyYgxZfuil60xzN5SPRhSiWSYteE=; b=eTSmr4jWLes59L7ODlb+3nCemJvt1NKWwhjnayqPV/wxipkOf1pzMW2vsPRq3NHfzY Cnc3efEfG78b1oVZE6e5DGzvyNavmEmfXAwv9uGQCMBnr2yEOvWJU+oSjVCTlbK2AZC4 2df/rOymw8FqbuA3QuQfgZTdzbp4qZ2xl5Eo86RXPXKAG5q7a9LjiXcybuWdPJ88ghNt LZhM9siIDdZQWM+/sRRY7z778Tkv0ZKnR+UILKvcpbWfZ7Gyh7mwIFLNvf37Xe/JdkU2 aHbXXqwYUkR04sScPcwzFgy/ifgYlpQqrfcUVRriBZ1Yh7YU1gh0S3FuKlovv2giG8ij V5Yg== X-Gm-Message-State: AOJu0Yzm6bGmX2jZXCO5IIsvMAEyP36aeNlBuAaBK1pIrbIFQG7+hmQN rxonJ+WCtzd3OjssFAIsxpdSX3XdellQTl9N2Pv0uhdgW3UpBqtRtju+Y/yR34rDST2YYSFYe6w 4/jBKFUpdhGnJK3VEdCxQXTFRQi/Ii2X6eSMVN0Ag+CKvdcgqwr6ijDaANRjO8ACqa+Z0eVTjFE zJosz58fm/n2A/kPh9ZqaPuBrL0C6EbYGhOaPAbsxk X-Gm-Gg: ASbGnctBfsCyASH7PC6s/saZ01qkbpBvEc4ljGzbS4UfAanW29EOF/Lm9/vGKVTjos7 M9u2MpKbnUbPkBm6cCZhI4qEa5Be7X53Pwzp8vG66abKnsBs3lzIGFTixC28LfhT+2pKsjBNktA 7f5ncV0NOPzV4ft0pJknkPV4Z0i8kY4CcEOeiqFitIUjuS4N3/yV62wlRNNFFqtXjh33bZbQZuN 237IduS5tNDaanKB+vwkAbuZ+b23g9dvhanL2pKe96z3QCMT2y9exx5aXSqrYz+w5Ww1PVEAVwg /dIjek3ogyU15U5ZIOYYbeEymLK8+NMWDf9tAoyC2tUrkPEr7m/J80kkdj5Cc9LD9S6hqh56BQl SaG0/sIrs6GfUgcJXMSc0ZVNr08SjQKBQNR6P8MR8UonW X-Received: by 2002:a17:907:809:b0:b45:b078:c534 with SMTP id a640c23a62f3a-b50ac5cf768mr1355354666b.45.1760109013394; Fri, 10 Oct 2025 08:10:13 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEEqY/Lmavybv5EXJzRc4lxTfQq/vdsK6Ltvb6SFc3/2PjVQdDkvjwgnechuXAJwrkP2t8DpA== X-Received: by 2002:a17:907:809:b0:b45:b078:c534 with SMTP id a640c23a62f3a-b50ac5cf768mr1355349166b.45.1760109012636; Fri, 10 Oct 2025 08:10:12 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 02/19] rust/util: use anyhow's native chaining capabilities Date: Fri, 10 Oct 2025 17:09:47 +0200 Message-ID: <20251010151006.791038-3-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, T_SPF_TEMPERROR=0.01 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: 1760109154289154100 Content-Type: text/plain; charset="utf-8" This simplifies conversions, making it possible to convert any error into a QEMU util::Error with ".into()" (and therefore with "?"). The cost is having a separate constructor for when the error is a simple string, but that is made easier by the ensure! macro. If necessary, another macro similar to "anyhow!" can be returned, but for now there is no need for that. Signed-off-by: Paolo Bonzini --- rust/util/src/error.rs | 139 ++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 93 deletions(-) diff --git a/rust/util/src/error.rs b/rust/util/src/error.rs index 20b8e7d5af5..bdbf2634170 100644 --- a/rust/util/src/error.rs +++ b/rust/util/src/error.rs @@ -38,6 +38,7 @@ borrow::Cow, ffi::{c_char, c_int, c_void, CStr}, fmt::{self, Display}, + ops::Deref, panic, ptr, }; =20 @@ -49,104 +50,65 @@ =20 #[derive(Debug)] pub struct Error { - msg: Option>, - /// Appends the print string of the error to the msg if not None - cause: Option, + cause: anyhow::Error, file: &'static str, line: u32, } =20 -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.cause.as_ref().map(AsRef::as_ref) - } +impl Deref for Error { + type Target =3D anyhow::Error; =20 - #[allow(deprecated)] - fn description(&self) -> &str { - self.msg - .as_deref() - .or_else(|| self.cause.as_deref().map(std::error::Error::descr= iption)) - .expect("no message nor cause?") + fn deref(&self) -> &Self::Target { + &self.cause } } =20 impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut prefix =3D ""; - if let Some(ref msg) =3D self.msg { - write!(f, "{msg}")?; - prefix =3D ": "; - } - if let Some(ref cause) =3D self.cause { - write!(f, "{prefix}{cause}")?; - } else if prefix.is_empty() { - panic!("no message nor cause?"); - } - Ok(()) + Display::fmt(&format_args!("{:#}", self.cause), f) } } =20 -impl From> for Error { +impl From for Error where anyhow::Error: From { #[track_caller] - fn from(msg: Cow<'static, str>) -> Self { - let location =3D panic::Location::caller(); - Error { - msg: Some(msg), - cause: None, - file: location.file(), - line: location.line(), - } - } -} - -impl From for Error { - #[track_caller] - fn from(msg: String) -> Self { - let location =3D panic::Location::caller(); - Error { - msg: Some(Cow::Owned(msg)), - cause: None, - file: location.file(), - line: location.line(), - } - } -} - -impl From<&'static str> for Error { - #[track_caller] - fn from(msg: &'static str) -> Self { - let location =3D panic::Location::caller(); - Error { - msg: Some(Cow::Borrowed(msg)), - cause: None, - file: location.file(), - line: location.line(), - } - } -} - -impl From for Error { - #[track_caller] - fn from(error: anyhow::Error) -> Self { - let location =3D panic::Location::caller(); - Error { - msg: None, - cause: Some(error), - file: location.file(), - line: location.line(), - } + fn from(src: E) -> Self { + Self::new(anyhow::Error::from(src)) } } =20 impl Error { + /// Create a new error from an [`anyhow::Error`]. + /// + /// This wraps the error with QEMU's location tracking information. + /// Most code should use the `?` operator instead of calling this dire= ctly. + #[track_caller] + pub fn new(cause: anyhow::Error) -> Self { + let location =3D panic::Location::caller(); + Error { + cause, + file: location.file(), + line: location.line(), + } + } + + /// Create a new error from a string message. + /// + /// This is a convenience wrapper around [`Error::new`] for simple str= ing errors. + /// Most code should use the [`ensure!`](crate::ensure) macro instead = of calling + /// this directly. + #[track_caller] + pub fn msg(src: impl Into>) -> Self { + Self::new(anyhow::Error::msg(src.into())) + } + #[track_caller] #[doc(hidden)] pub fn format(args: fmt::Arguments) -> Self { if let Some(msg) =3D args.as_str() { - Self::from(msg) + Self::new(anyhow::Error::msg(msg)) } else { let msg =3D std::fmt::format(args); - Self::from(msg) + Self::new(anyhow::Error::msg(msg)) } } =20 @@ -155,9 +117,10 @@ pub fn format(args: fmt::Arguments) -> Self { #[track_caller] pub fn with_error(msg: impl Into>, cause: impl Into<= anyhow::Error>) -> Self { let location =3D panic::Location::caller(); + let msg: Cow<'static, str> =3D msg.into(); + let cause: anyhow::Error =3D cause.into(); Error { - msg: Some(msg.into()), - cause: Some(cause.into()), + cause: cause.context(msg), file: location.file(), line: location.line(), } @@ -326,8 +289,7 @@ unsafe fn cloned_from_foreign(c_error: *const bindings:= :Error) -> Self { }; =20 Error { - msg: FromForeign::cloned_from_foreign(error.msg), - cause: None, + cause: anyhow::Error::msg(String::cloned_from_foreign(erro= r.msg)), file: file.unwrap(), line: error.line as u32, } @@ -370,8 +332,8 @@ macro_rules! ensure { }; ($cond:expr, $err:expr $(,)?) =3D> { if !$cond { - let s =3D ::std::borrow::Cow::<'static, str>::from($err); - return $crate::Result::Err(s.into()); + let e =3D $crate::Error::msg($err); + return $crate::Result::Err(e); } }; } @@ -410,19 +372,10 @@ unsafe fn error_get_pretty<'a>(local_err: *mut bindin= gs::Error) -> &'a CStr { unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } } =20 - #[test] - #[allow(deprecated)] - fn test_description() { - use std::error::Error; - - assert_eq!(super::Error::from("msg").description(), "msg"); - assert_eq!(super::Error::from("msg".to_owned()).description(), "ms= g"); - } - #[test] fn test_display() { - assert_eq!(&*format!("{}", Error::from("msg")), "msg"); - assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg"); + assert_eq!(&*format!("{}", Error::msg("msg")), "msg"); + assert_eq!(&*format!("{}", Error::msg("msg".to_owned())), "msg"); assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); =20 assert_eq!( @@ -439,7 +392,7 @@ fn test_bool_or_propagate() { assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); assert_eq!(local_err, ptr::null_mut()); =20 - let my_err =3D Error::from("msg"); + let my_err =3D Error::msg("msg"); assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)= ); assert_ne!(local_err, ptr::null_mut()); assert_eq!(error_get_pretty(local_err), c"msg"); @@ -456,7 +409,7 @@ fn test_ptr_or_propagate() { assert_eq!(String::from_foreign(ret), "abc"); assert_eq!(local_err, ptr::null_mut()); =20 - let my_err =3D Error::from("msg"); + let my_err =3D Error::msg("msg"); assert_eq!( Error::ptr_or_propagate(Err::(my_err), &mut loc= al_err), ptr::null_mut() --=20 2.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109153; cv=none; d=zohomail.com; s=zohoarc; b=GqUyOuXXBoIhMXD8igDPxa1Q6E3y26p3nSDoHdn2yWftCJUkBPeifnbmbLpTccbw62Mc4FJtwUzxQPcKTM3k7QnTX5J4Kso0U9QQSfUlufdQc7/OML+pnBosFbryf0jlWFyqmjauXPy4+524lPP9+I0b2QV0vdPocoKbJu62lH8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109153; 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=G/xFnT8f0DpAhs4XBkL1hdT97m682bWgM6Ej0zAVW6U=; b=ZsEAbzDrISfmhCs6El/HXZifn3gkbEiMo0sWWd83tA3zocpf+dgOdnikUGuit8JyAaLVBorybesUxaxxSEUnLbDEsYcipOgf07e+sKfCWUKVo9e/aWRIVeKh3Cva8KC086AliDUnA2mYnZb09Lj9U8iDFfU5gL3XY4xC6GAh9h0= 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 1760109153186401.2447594883165; Fri, 10 Oct 2025 08:12:33 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7ElT-0008MZ-3e; Fri, 10 Oct 2025 11:10:39 -0400 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 1v7ElO-0008MA-P9 for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:34 -0400 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 1v7ElC-00043i-TS for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:34 -0400 Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-259-GVmbAhQSNcuY4FR3Jn1R0A-1; Fri, 10 Oct 2025 11:10:18 -0400 Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-b4635c413a7so169305466b.1 for ; Fri, 10 Oct 2025 08:10:17 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d900e2bcsm248932966b.64.2025.10.10.08.10.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109019; 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=G/xFnT8f0DpAhs4XBkL1hdT97m682bWgM6Ej0zAVW6U=; b=JAKTQU26QnL176D79P97teq0uatmaSrgeA1fEnWegMpK0FJpnE4j3ByJ2R1QCsMz98Hdcc r5Ok0+cQvI1tF71deBFwJSBRgnGD026WADTae+Q6l4JGW98hCzU4Pqkar0XL/dhlAEUMaD fLNAYbG6XfP6eDQQJo8UZgqU1G2bPzs= X-MC-Unique: GVmbAhQSNcuY4FR3Jn1R0A-1 X-Mimecast-MFC-AGG-ID: GVmbAhQSNcuY4FR3Jn1R0A_1760109016 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109014; x=1760713814; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=G/xFnT8f0DpAhs4XBkL1hdT97m682bWgM6Ej0zAVW6U=; b=KOoHZ++G3osLIrx7YOWYDIsZ+buwLhfG5X2TiDgoW74V+0DwIGSEMBXpJWTxfAB2SY ycyOAKa96LRTmZNRDoCzNHz0mzbcK+Bln64l7s5PVjyNXDS1Z+BISdZOBeoofgNC4bNi 2nLN3zHHBhl2bWvZyCjDdni17T4qKLu0VP0UO5b55opI7+4ckNJZI5X7Au63zfIJk6rJ cnD9yFWJr2odPlpnyL5ax0OTxeEqs+dfNqdcFKwsa4akyFdjGk/bMd/uZrRp1j9RWcbo lCh7x8y6GeVofAff8j6rY4L+2A2DRSe7qE4AiO7Mk60PtJiy5gynVMjOcaKLvOzFKrxi ZBhw== X-Gm-Message-State: AOJu0YwLzquLm7oSJvD97KVCnTQrhDOhLntEscLmyfKSwLUYuxvrpq26 H5cUbHMEYaxAqDZbiG4eOLMKQZAU+5PlOC659EJLPMaBG8AKlm0Y0IoJJJJE18z7SLXLqS54701 qtmJWQArJ1bBIPGXbUo6ooQ+7I2se774PVHQODeOfUAYfcNRMVwFVEV7DUYn730Z6doe2+kPh3X na/WKVmSw1Wmrp/029L6ZeNe+NGePmJL1j5E9nCh3H X-Gm-Gg: ASbGncvYZmpv8ijXDxtEp1mvNjnO5VEwlWT/q6gmRXUWqhFumayA8rPYUcU6hrtJOrz k1N/urfZN32RuS7KBWt80OZ2luXRtjdBGub9OwSbsd+aPg2P7zhuVVSrpSrMXg7mWh2i5hFzRgj 0IHij0KtAtN9xzL81NnDPzDSAJWFBS4r9A+Qr5VBpqbUj3PLsMq7PHIGtsZvo0Hpa9Zd+fz/srq BbYqNSCIbIoQZu0sQ6f/tM9/wRUYwa8RWYJG2HuXwKq95u4N+al60W3sBDJOqxNkfEg3skwIwd8 Edm/KIi6Sso4dfCv1H3lsBL4BtNstlpCKAm3WfhTG9Hj9jb402lVBfRHmcuWkxWv6bV6qYUS8jB 1W6m73yAeE2GUZpfm+GLu7bCuibLIzc0Iu/gf2RrMV1jg X-Received: by 2002:a17:907:96a9:b0:b33:b8bc:d1da with SMTP id a640c23a62f3a-b50aa491efcmr1327481966b.1.1760109014428; Fri, 10 Oct 2025 08:10:14 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHWpvnoj2yV3OFPQlzNEULVZJYZM5xsvAJtnsLekGyDi5wRdbb2YipTV1Uj1mIqqNmOWjNRwQ== X-Received: by 2002:a17:907:96a9:b0:b33:b8bc:d1da with SMTP id a640c23a62f3a-b50aa491efcmr1327478766b.1.1760109014021; Fri, 10 Oct 2025 08:10:14 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 03/19] rust: do not add qemuutil to Rust crates Date: Fri, 10 Oct 2025 17:09:48 +0200 Message-ID: <20251010151006.791038-4-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, T_SPF_TEMPERROR=0.01 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: 1760109154232154100 This fails due to https://github.com/mesonbuild/meson/pull/15076. The config-host.h file from the qemuutil dependency ends up on the rustc command line for targets that do not use structured sources. It will be reverted once Meson 1.9.2 is released, or replaced with an update of the minimum supported version of Meson if 1.9.2 is released sooner. Reported-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini --- rust/chardev/meson.build | 2 +- rust/util/meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index d365d8dd0f4..36ada7c4546 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -39,4 +39,4 @@ _chardev_rs =3D static_library( dependencies: [glib_sys_rs, common_rs, qemu_macros], ) =20 -chardev_rs =3D declare_dependency(link_with: [_chardev_rs], dependencies: = [chardev, qemuutil]) +chardev_rs =3D declare_dependency(link_with: [_chardev_rs], dependencies: = [chardev]) diff --git a/rust/util/meson.build b/rust/util/meson.build index b0b75e93ff6..8ad344dccbd 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -43,7 +43,7 @@ _util_rs =3D static_library( dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, q= om, qemuutil], ) =20 -util_rs =3D declare_dependency(link_with: [_util_rs], dependencies: [qemuu= til, qom]) +util_rs =3D declare_dependency(link_with: [_util_rs]) =20 rust.test('rust-util-tests', _util_rs, dependencies: [qemuutil, qom], --=20 2.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109123; cv=none; d=zohomail.com; s=zohoarc; b=R7ya2YaqBlziNxDz5Qa1ZOiEwcrfmc2jCZgD1hCEDWIoS5BkvlZxva3iGTpOcEsH1f2n/yeIKK33jiToypt0Yzy+dLsk+a+z87XUda1csC9p1954tsbWkcma7y8h2JSTBg4fsnNgJZG4k4YvW4HuFYRuqvRwrvnYdc3pP76WfAY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109123; 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=EVTFiamtz2g0JhSEELeH53XdwlrM4EAcjsX38InL3bs=; b=YsOsJW0GaG4Q8ji+ZWZxWB1iFo0pU08mMG4Igo2WMh8b2VPeoDyUQiE0l5RHbB1y2ePc8+XoNgJqgZoypKjNRkfqCruXoaj7stpyQ3xDx5ha503oYEh0mvIo1pXOcIA1x7ytAIhh+GYfInyQWdix6PGFWfbaST3oDYz44Cvozy4= 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 1760109123303136.80450781525565; Fri, 10 Oct 2025 08:12:03 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Elb-0008Qs-JU; Fri, 10 Oct 2025 11:10:47 -0400 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 1v7ElX-0008PQ-Uh for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:44 -0400 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 1v7ElE-00044B-Tg for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:43 -0400 Received: from mail-ed1-f69.google.com (mail-ed1-f69.google.com [209.85.208.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-152-xBwoiKjtPF-Ez20Yhb0rYw-1; Fri, 10 Oct 2025 11:10:20 -0400 Received: by mail-ed1-f69.google.com with SMTP id 4fb4d7f45d1cf-639ffd6a3fbso2914707a12.3 for ; Fri, 10 Oct 2025 08:10:20 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-63a5c134062sm2529652a12.36.2025.10.10.08.10.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109021; 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=EVTFiamtz2g0JhSEELeH53XdwlrM4EAcjsX38InL3bs=; b=d3k36ZZQpvFH+QhrsQpQt317X9J44HNd01tK1SB6XfZVGiFqOP3YCXTQq/2xInjlErsYHm LnXe9DktVavrihxW3b8mS5hjBEoKKGvSrjObmVABrlzDYkGpyZLKwx1S+IRpJiFeXwjn4G cAo3FRxmlABwLuQkSpo5gGMKvFseJOM= X-MC-Unique: xBwoiKjtPF-Ez20Yhb0rYw-1 X-Mimecast-MFC-AGG-ID: xBwoiKjtPF-Ez20Yhb0rYw_1760109019 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109018; x=1760713818; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=EVTFiamtz2g0JhSEELeH53XdwlrM4EAcjsX38InL3bs=; b=FNIx3puGTvfc07gOsJhrOTKvoXpHUb4GyYtMNx4nY7IDAgz1rOQReIWtXRQZPCtF0R Nu6mizeTizX0kYmPwoZqTQ296wXFhHcGhCo2Pr4xmy05OlZANC/6xl+VuJV/0K+hjzK8 a91RXEaDj/XpI44+jpMwTYsu0sOGvRZ+wxDTMSpwQQLZr3/DVCsamkvJmSVH4yr6qelX c/uUwO5SR/++Kp0N0UMkyrg8WpKTturRBG+NaIdxn2d4VcYrpLn73xUvBogYnfoK22j2 cxAawOUoR0wRHMykjD7HEg8e68bnnOObinzuGi2taAJYdChuGcA8ZPtdJO8W2KZdYmNe kqnA== X-Gm-Message-State: AOJu0YzH/fX+jHmmBXcxUMXp0uQ0Tqv2mxdo7c57SGqr3NyzPL1sw9Xr I1imkWhZ4p9aRCwbKv9osHb6JEjVmov4CaICFkXycCBTpX+tRdxYvbYePLb4SmTn/Xj6b1UI/tG pKPSIAzO/6VqmtkTSrb2aaQDIblGE8reseZ6o2YGLdb9dU4u5RKE/sISe1Af4kThwV1HaaLvjqP XLiG4+/6fjmYJbXzntWTipoaf2ETeuJB2G3MiXzSFt X-Gm-Gg: ASbGncvamJ4yiQSQkaZ6jt1KgfPqaDWZ9LSh8Zi9AdsOhSGizI32VutX0i4uhxPUvsU hY9jwQTntR48AHBF792pR/PE7px2KM5p5a4eqSoMhYM04J27rUwbmOtc112Q379Umkez+5iHik/ fK4CB8JfWzalOPcIHwv3HzcOvn2kCkboPYMLvx30/1tUrBuoR98PP3L/YgyUz9Vz20DI5YRhWtx RzUh+z+FpDTOk7g0fgfgDnEKwiHvdPRkxCbf03LjGfM0LJOc2BO3lIJm/duk8aio5GvVeZHOp1k 4hsPSJd3eyd9si01nkp6cKbNII7axhrhYMXXU9Q0LQCs3djZunywF4DcwxcIdA/Gukb0+fAxDAu 5DVbLXAq+TJWxE0Li/T2clZGYqhKmnKzhb4r2XDpw8g2I X-Received: by 2002:a05:6402:4312:b0:637:f374:eeda with SMTP id 4fb4d7f45d1cf-639d5c31e8amr11194083a12.20.1760109018458; Fri, 10 Oct 2025 08:10:18 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGBFLnVpPt/Ln/uvtHT2tQWvrWAdARD8vHorn1C7WvZ3OQXdduMcqlRT0aVQfDyO9PIJ3gPpg== X-Received: by 2002:a05:6402:4312:b0:637:f374:eeda with SMTP id 4fb4d7f45d1cf-639d5c31e8amr11194045a12.20.1760109017922; Fri, 10 Oct 2025 08:10:17 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 04/19] rust/qobject: add basic bindings Date: Fri, 10 Oct 2025 17:09:49 +0200 Message-ID: <20251010151006.791038-5-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=no 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: 1760109125818154102 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 Signed-off-by: Paolo Bonzini --- rust/util/wrapper.h | 7 + rust/util/meson.build | 6 +- rust/util/src/lib.rs | 2 + rust/util/src/qobject/mod.rs | 317 +++++++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 rust/util/src/qobject/mod.rs 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 8ad344dccbd..ce468ea5227 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -36,8 +36,10 @@ _util_rs =3D static_library( 'src/module.rs', 'src/timer.rs', ], - {'.': _util_bindings_inc_rs} - ), + {'.': _util_bindings_inc_rs, + 'qobject': [ + 'src/qobject/mod.rs', + ]}), override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], rust_abi: 'rust', dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, q= om, qemuutil], diff --git a/rust/util/src/lib.rs b/rust/util/src/lib.rs index 16c89b95174..fe0128103c8 100644 --- a/rust/util/src/lib.rs +++ b/rust/util/src/lib.rs @@ -4,6 +4,8 @@ pub mod error; pub mod log; pub mod module; +#[macro_use] +pub mod qobject; pub mod timer; =20 pub use error::{Error, Result}; diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs new file mode 100644 index 00000000000..9c6f168d6e1 --- /dev/null +++ b/rust/util/src/qobject/mod.rs @@ -0,0 +1,317 @@ +//! `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 + /// and the C `QObject` pointer is leaked. + pub fn into_raw(self) -> *mut bindings::QObject { + let src =3D ManuallyDrop::new(self); + src.0.get() + } + + /// Construct a [`QObject`] from a C `QObject` 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)) } + } +} + +impl From<()> for QObject { + fn from(_null: ()) -> Self { + 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) }; + unsafe { QObject::from_base(addr_of!(qobj.base)) } + } +} + +macro_rules! from_int { + ($t:ty) =3D> { + impl From<$t> for QObject { + fn from(n: $t) -> Self { + let qobj =3D unsafe { &*bindings::qnum_from_int(n.into()) = }; + unsafe { QObject::from_base(addr_of!(qobj.base)) } + } + } + }; +} + +from_int!(i8); +from_int!(i16); +from_int!(i32); +from_int!(i64); + +macro_rules! from_uint { + ($t:ty) =3D> { + impl From<$t> for QObject { + fn from(n: $t) -> Self { + let qobj =3D unsafe { &*bindings::qnum_from_uint(n.into())= }; + unsafe { QObject::from_base(addr_of!(qobj.base)) } + } + } + }; +} + +from_uint!(u8); +from_uint!(u16); +from_uint!(u32); +from_uint!(u64); + +macro_rules! from_double { + ($t:ty) =3D> { + impl From<$t> for QObject { + fn from(n: $t) -> Self { + let qobj =3D unsafe { &*bindings::qnum_from_double(n.into(= )) }; + unsafe { QObject::from_base(addr_of!(qobj.base)) } + } + } + }; +} + +from_double!(f32); +from_double!(f64); + +impl From for QObject { + fn from(s: CString) -> Self { + let qobj =3D unsafe { &*bindings::qstring_from_str(s.as_ptr()) }; + unsafe { QObject::from_base(addr_of!(qobj.base)) } + } +} + +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); + } + } + unsafe { QObject::from_base(addr_of!(qlist.base)) } + } +} + +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); + } + } + unsafe { QObject::from_base(addr_of!(qdict.base)) } + } +} + +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> {} + } + }, + $($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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109400; cv=none; d=zohomail.com; s=zohoarc; b=Z6WcaOTs+fcXrGyvdIhkE1K/6aCsHveTdrGbWVJU5ICO9rd2KAGwGhbt8vfq1RnEp7OGxWSqc0DMul+nvY3p8pY86H0iojGG+fovOVlWFLVyA85uf4y9OxwC8Ohf2bAsZ+ANetoFcG3HUCZqx9nXgRC/BDYaXAHqD+0y8dDJ75g= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109400; 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=YRsSx/btzTva/S/q1QVS/AopDr2/0oXgK2m/mbc40QU=; b=VYQo8wMEW8LebgKTDxUNz0YKxnL/Rk8NaD2zMcnuanqI4oJE4+hqAs0+oFMhuRnuOV4d7g0cQp9Lv7mV51MluVzTJ86QYfWVHucNmM+tEjMG+AWiiVvXFHlZ6tjX/OYxqBDh6T6TWdr29tiOW56HZ4SvekOtkw9GO8IkPei8Fy0= 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 1760109400372553.7098975373814; Fri, 10 Oct 2025 08:16:40 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Elf-0008Tb-OZ; Fri, 10 Oct 2025 11:10:51 -0400 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 1v7Eld-0008Sn-LK for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:49 -0400 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 1v7ElK-00044m-LU for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:49 -0400 Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-551-DllfT2oiOpis5AWMYXhOSQ-1; Fri, 10 Oct 2025 11:10:23 -0400 Received: by mail-ej1-f70.google.com with SMTP id a640c23a62f3a-b41c67edce5so322346066b.3 for ; Fri, 10 Oct 2025 08:10:22 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d5cad80bsm252171666b.16.2025.10.10.08.10.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109024; 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=YRsSx/btzTva/S/q1QVS/AopDr2/0oXgK2m/mbc40QU=; b=IFEuZdoMf68LItVyNbMEWVejFqxqwkPnW0ZVoX+DqDnZJCvy6GgJuNnxAgd2KZBfc1YdAp /AzS9Wd4UQK/68PMymalr6BiTyj5K8N8yYbo5crCj/autNloQbvlTST0I9oHURGz0nmEXx 2e5OIL6rG+zUbXeIhlqHfsooqs07Cyk= X-MC-Unique: DllfT2oiOpis5AWMYXhOSQ-1 X-Mimecast-MFC-AGG-ID: DllfT2oiOpis5AWMYXhOSQ_1760109021 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109020; x=1760713820; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=YRsSx/btzTva/S/q1QVS/AopDr2/0oXgK2m/mbc40QU=; b=wDpojkEWNlGtVuS+Q4PaeALUEWiws1gEgU5mflDw1e5+fWuJjgeSFS7MJ8TIxwNlSE TIHypNTNVn/BdD1BwLMVedef3I1jkO0lvb4tZleGv7z7H5U70wh4xPzVs7S/lpw7iqKI yeucPqICVd1LIjSflw9d9ccpzmT/y20Gpju1+9gpM5HeGouICia74GOGyZ5zr48ekLB6 fO86eXQNK+n2RGMOvptmLqAZ8TEoWWeAWurRu80hxaKyXLw1q6dnsiRf0I2guxLoAesn ZDQiWLEfkm9xyVq/0VtOXIPBwRs1SgFdjw99hOqwAqgkLKVinXbrGKSPMsoI398vsut4 5P3Q== X-Gm-Message-State: AOJu0Yyr0EDSyrKyHRav5lwrj7VoyNNknkbqzKUI4hqRiTioe2N7BOsY RXXCwwkJdGc2js0B+llucyybzXpJEDVP6yoN5IQYWoNVUPyXXJSDDPm3ZG6BK2bulGTLyqEmhHH ossCpWMPYWmmtmYvJtY7OJGjK4jEYw/B6/geoAX3KaH2CjAue40ydC4nppQhVkZOvStAjTUtyZV 8xwsuaFfGLcTmDwkuVVh5RdE9AiGnaHJXk/iO8dt9F X-Gm-Gg: ASbGncu99hscZcwt/FqzVmyVJ8GU0ZMbPipuX4z4atqhgTT6jwLPo/LEvG59ullY0ZG BkaRHJh0+7unM5DjueXua972swBpwloVgBHuYorUWcuw1EyDnuKyFbiIFJJGVgJURabQPx17jV6 xyJSjx/DmWtFzwFWy9oSoUON6wb44OCcGnppPJIVvmHf3VhAPvq2g0DfpXOakEmTjqDHEdIWpZ3 fZNwkeNXdotFHd0Ys2zy9SqASBya9Yk3RAIERmkvOWbkqs872oraEuBXrvOsB13aw5+r5v6g4YC q6VGySiPs2geProH15tZ3FIs3OuRZq5uveXal4CDhNZh/1lJpgW1IV5kXn8nVmgfEqzHjPEwSsM e3ci5tXeknal5PGmzHXjtFBLYUQ7F3J8fA4uBfISkqp5s X-Received: by 2002:a17:907:c28:b0:b46:3f98:6ba5 with SMTP id a640c23a62f3a-b50aa48e3a6mr1435870466b.11.1760109019757; Fri, 10 Oct 2025 08:10:19 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEuQl6W1MulL3xWbAIO+JpGNItc70G29j6vMIZ+9VFy+pU1+CdSagjJdMcCIvSM3J/0YoskOQ== X-Received: by 2002:a17:907:c28:b0:b46:3f98:6ba5 with SMTP id a640c23a62f3a-b50aa48e3a6mr1435866866b.11.1760109019260; Fri, 10 Oct 2025 08:10:19 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 05/19] subprojects: add serde Date: Fri, 10 Oct 2025 17:09:50 +0200 Message-ID: <20251010151006.791038-6-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109401319154100 Content-Type: text/plain; charset="utf-8" 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 4f98b2c03d3..76fdd9b97bf 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -30,6 +30,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 6ba075c8c71..f65cc4018c2 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -4,6 +4,8 @@ subproject('bilge-impl-0.2-rs', required: true) subproject('foreign-0.3-rs', required: true) subproject('glib-sys-0.21-rs', required: true) subproject('libc-0.2-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') @@ -11,6 +13,8 @@ bilge_impl_rs =3D dependency('bilge-impl-0.2-rs') 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') +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 8f97b19a088..3ed0429d806 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -45,6 +45,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 bc1b43caa25..eb5808b83ec 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 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 c00c8478372..697d1ef3bdb 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -20,6 +20,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..6cb2b59a147 --- /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, native: true) 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..e917d9337f6 --- /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, native: true) 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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109123; cv=none; d=zohomail.com; s=zohoarc; b=RrbDn3whq7c/J4VDR4ArREM8WYPUZH0eibwNSlIoly4i0um/FZlcNYPv+S6XMSdZT/DeoKh+UJlY8EvYkx8mdU1UmLvYOSbieiHhDg0jlnkhnfjQIE5gGDa0lCeJCh7uB1td+xA3vFIevlJCEve0K35gSsAOQlv9voSeDXgA4jo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109123; 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=3FZgvDx1lu9fJSdOxBvB+MMur5Jir4fvIS9bMP6QLM0=; b=WGeVe66MdZxJVw5Z9Z9fcipWf2IPz6uNATwlwgYfeFiZiByZNUN+/P7OxtXD14j6WBQooQZ1+EoJD/H/vNgL+zaS8yW7PxmuN/2GKkcmOUwj5TgQVVWCDyP5ga+V5kO2XH1rG/92VHo+QMy4xvYQCW/zPTb7LxF+/n7QNt+64hA= 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 1760109123456204.78865932608278; Fri, 10 Oct 2025 08:12:03 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Eld-0008So-Ow; Fri, 10 Oct 2025 11:10:49 -0400 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 1v7ElZ-0008Qc-J3 for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:45 -0400 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 1v7ElJ-00045r-E2 for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:45 -0400 Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-128-mwdiVTONNimuWLFsdlsQig-1; Fri, 10 Oct 2025 11:10:25 -0400 Received: by mail-ej1-f70.google.com with SMTP id a640c23a62f3a-b3cd45a823cso257953966b.2 for ; Fri, 10 Oct 2025 08:10:25 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d8c12ccbsm249043566b.46.2025.10.10.08.10.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109026; 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=3FZgvDx1lu9fJSdOxBvB+MMur5Jir4fvIS9bMP6QLM0=; b=VA0GWXIMO3zMQi0mSqzgrjJ7+xw/qwtmdUMs05J4MmSC4ow8sDQaBRl1VyjYACYavhOd4H WVTALi+tIIuV2kV1aVQzFc5NEUNLuIFQpz/N8BWQe9l7l8n8NW7A0i/0iZDPCWqKzAnvL7 KYVlmX02oi+57FpwH98mB4rg003VkVw= X-MC-Unique: mwdiVTONNimuWLFsdlsQig-1 X-Mimecast-MFC-AGG-ID: mwdiVTONNimuWLFsdlsQig_1760109024 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109023; x=1760713823; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=3FZgvDx1lu9fJSdOxBvB+MMur5Jir4fvIS9bMP6QLM0=; b=HcX09fmEwMJN7LZIrGjOK80RokECxEv83GwxbY3MwS2C9wzFdvsrOyUxy9M1Y5S7bu fZD+W6sci/cbnWUAfML2NMKY7lp/e++up8xzM/R9KmMI3cWqjEsypcem0sGS8U27ghXB D6vCKqXJzc6w5XdIqc+xBRZpDvyyo11cKlczA0NaFKXQFlXtRXYQDOOHrHa/F6d8KfDQ mn4Qlqj1SyC0HIej3HYJjdsbtwlV+uZL7HUSJWgZFDgLhHEkrZoGTwX/IigIN7LFre5e fB08yNloDv5q0fmOpGj4aaHfET9dMRfR/tTQLvxw2YzwOnUQWGUOWjLcJRoD0lWOM9ap AW8w== X-Gm-Message-State: AOJu0YyBqKuGuKVK1+3xJ377R0qS1zEwFfdMfUh2QFn5RPIgxGdAr9Ht iDPn2D4NHJtLuwqp9BE2SLXgfXlmyt/l4qPX3vvvrLJ742mgI5RtbDQAAZd4fxUQZ7NaddRsxtw hTAxbHST4+MrEU/SF6yAWBc1hLz2iKcsGotY7Uml10veCuTN+/Lsw5dkbQD6h8nxWmeWr2sSPwl Vcx1FiicbX9XAkueQrpSM2Xaz4sbhasNtulyZtCyLA X-Gm-Gg: ASbGncvQtsp3Gb6kRPKT5UFbP2pwQegy7h8W/7nDAL4kJscOlrWf5LBRbrwX0a6wuF7 /FJhOmPnzxlZsvmfQIMqtXSoRjhHX4uthtoJm3w6/gsjkM9kWUZ7x+lU+3nQMRpccIJDMGpU682 NFW8+3gqSWdemvU5U2qxcz6f2X9lqADaExNdIYNxtKIszB/TgJWmIyhu/IuzXR7pY8D25K3roxb STGio0m5Nk0nGmmKQIpxwFGQ+hGKH+rwzBv73GBxzICKzkXCs0qOODU6UTbQBSE2X50M3KSdlQE xIrToJLutkZQkdblTeheX+CRQi7nuOe1S6xpAFLyka7ALUv5iF5UjfOxeWktj86IwZ32aE6BN58 prZqg0U5loBwIIXH+wvdEd9ExLtjLsNxmlNDSuNv1Rsdx X-Received: by 2002:a17:906:6a14:b0:b44:2d0:e05f with SMTP id a640c23a62f3a-b50ac2cf494mr1311064966b.33.1760109023388; Fri, 10 Oct 2025 08:10:23 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFTnWAf1thJZKPgL7ao/5GeYS/n+TrYEjpV4ceOm1F1ZsP59hS6+z1hjorwGFq0K+CGDu7qXQ== X-Received: by 2002:a17:906:6a14:b0:b44:2d0:e05f with SMTP id a640c23a62f3a-b50ac2cf494mr1311060466b.33.1760109022780; Fri, 10 Oct 2025 08:10:22 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 06/19] rust/qobject: add Serialize implementation Date: Fri, 10 Oct 2025 17:09:51 +0200 Message-ID: <20251010151006.791038-7-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109134542158500 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 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 11085133490..7c9f85d5728 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -445,6 +445,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 ce468ea5227..9fafaf76a37 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -39,10 +39,11 @@ _util_rs =3D static_library( {'.': _util_bindings_inc_rs, 'qobject': [ 'src/qobject/mod.rs', + 'src/qobject/serialize.rs', ]}), override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], rust_abi: 'rust', - dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, q= om, qemuutil], + dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, s= erde_rs, qom, qemuutil], ) =20 util_rs =3D declare_dependency(link_with: [_util_rs]) diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs index 9c6f168d6e1..0913fabc1ee 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}, @@ -230,7 +232,6 @@ fn drop(&mut self) { } } =20 -#[allow(unused)] macro_rules! match_qobject { (@internal ($qobj:expr) =3D> $(() =3D> $unit:expr,)? @@ -313,5 +314,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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109396; cv=none; d=zohomail.com; s=zohoarc; b=IIEhEs+h9ciAXInOB5n9LpEcqGAEam/H5rCXYcV0KbA/DN1hPTjYbH8Gfh+X8a67mzcOPmaSvQiWZ+1GBKz0xSx2pL8aiPubgbYhtmbCafD7a2jGJHOWEH1mjEwNBWZsHUd6rI/g3ENquKdEVFWUvtpOt1As6vcc003oMErY0Zc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109396; 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=B8pzOCHB8QdpwPymgUsOaqke2Hs7aNg8+KR/7IsvHXc=; b=mClIHHFxRCK2sGyfsECurFb2GpYp3AiZyY4RG0mKxtFEn4KcGepaKjZpqnDeFvQGPc3VXF0g65gJz0lRREuJfyz1ILb6DOjZf+Pv/JZ4f3AX7at8SBTAUgQtM2v0atbetTxNLFiha9pEwxqc66G/NYyYpx3tnp9ttAnM0YIH3l8= 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 1760109396181956.2137602795235; Fri, 10 Oct 2025 08:16:36 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Eln-00008e-8o; Fri, 10 Oct 2025 11:10:59 -0400 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 1v7Elj-00005w-MO for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:55 -0400 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 1v7ElN-00046t-E2 for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:55 -0400 Received: from mail-ej1-f71.google.com (mail-ej1-f71.google.com [209.85.218.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-467-0ca_Je0IOBGZzX1XU-uGnw-1; Fri, 10 Oct 2025 11:10:28 -0400 Received: by mail-ej1-f71.google.com with SMTP id a640c23a62f3a-b4068ff9c19so371581566b.0 for ; Fri, 10 Oct 2025 08:10:27 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d9525bd1sm247357966b.81.2025.10.10.08.10.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109029; 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=B8pzOCHB8QdpwPymgUsOaqke2Hs7aNg8+KR/7IsvHXc=; b=G8m1gR590EkYjZm8RYGDWMhMw2rYZQJKubf8Q0wpReGuPCz3f9ybgOW5ddSiEu8qojy3n5 WlQuZicIIhTJuYPW0s18Xv+9UfH8ywpYJPEJy29x6qWblmHjhIEUvxsusaiXJASemSqA8e L9pOxrvaj7VTP0G+TROBhZx0VKvsuMk= X-MC-Unique: 0ca_Je0IOBGZzX1XU-uGnw-1 X-Mimecast-MFC-AGG-ID: 0ca_Je0IOBGZzX1XU-uGnw_1760109027 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109025; x=1760713825; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=B8pzOCHB8QdpwPymgUsOaqke2Hs7aNg8+KR/7IsvHXc=; b=HPAed12Vrk3x+ipmFenceWJCNVgnenbFrDFkoxGFyt8pTaTQKUYyj5OJrGM/a9SC5n lIiFHu6WS7YQvBGV+m8w4z7AIu5dc4Tp1HQIkYjPbKKAZqhsGj1qarqwgewq6NFp8GSO 5H6AtS8wH0oVONuHM+gg+yYKfignX23ueFsqmLfYvfI4+MEOyYe+6YMMcFnNG+61bdPU oOdmSaJ61y9/lFYVqUE4+NaeAe26gY0YrDfztfMSb53w9VbF2DhKe0tpOgt9B7Dq6ka2 WmArDTXKYaFhnnHoHZPFMwgc7Xy+mZ5evxsTG0ih2qV+lL8iw1hosY8AB07lGvGxur7p v7Tg== X-Gm-Message-State: AOJu0Yz2hJZQkRQXAl4/XflMvcTh8CpMiav0ptKGSpiQuHX5Gt2peEWh 3tbYVg5RVxWx03dxxqz6K4FwAYNN/9nyu7DOiMyBCfHBKQP2Fy265CzfzSPfLyhMNyx+DtZLrCJ TGLSrCKr9ZXH2Vssoy+RZslvLqGEZD/BnImzP43I3LvDP+g3mGGJrQ5AFJ4tUbQZ4OtuXEeiRfa 6bu2Z8VKwUusz/GcaM124KW6lE2Hl1QTt6nyoO9Q/n X-Gm-Gg: ASbGncvLK6bh6wyiw70fSU11fjplcBqNzH9LeuHBq1Nke05n6Ysx2nkJjA4wA9KatRp G1VvsbhzWTqSf9hELsQZ1Cde7Bq+DnYphluYrA8swIg5K+lSpbQxEs3pVeHO0HvR69h5bbxB+8G YzrVdyOEiUKlblWmAZA8MAQHjYRUJTpi12ypqF0mh38goi67N26HU3HVuhY/x0PQQ6Eqp4Nc2cz flgX1XS8uFcCV3CxXGllnuPFjN1hPX1vpLLHylrsze9bTmVeqzfAhLnujLNzAdWlSmYTJ/tnnpy W2GAHbKh53SFCN+QYe35d+UbgxbAgaUoEMfhhHIyHYfuWis0HEltpqcNLHZHJUu2KM8ZsbIrAgA XvagyEDOkZGG+273A7xUeYjj1xt/RNRfFoX/hYrb/7qqU X-Received: by 2002:a17:907:ea5:b0:b3f:f822:2db2 with SMTP id a640c23a62f3a-b50a9c5b35emr1282399466b.11.1760109024817; Fri, 10 Oct 2025 08:10:24 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFb9Y6Nhh4eghDszIjmpdDe9oUgRUZsSxLi8ld/F/hu35fuiplepKfcw1isd6EhNfjWWK7Fow== X-Received: by 2002:a17:907:ea5:b0:b3f:f822:2db2 with SMTP id a640c23a62f3a-b50a9c5b35emr1282395566b.11.1760109024193; Fri, 10 Oct 2025 08:10:24 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 07/19] rust/qobject: add Serializer (to_qobject) implementation Date: Fri, 10 Oct 2025 17:09:52 +0200 Message-ID: <20251010151006.791038-8-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109397392154100 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 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 9fafaf76a37..39f427b3456 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -39,6 +39,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', ]}), override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], 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 0913fabc1ee..f8bd195ca2b 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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109328; cv=none; d=zohomail.com; s=zohoarc; b=Z8SuXShnV+FUQWejvfZE1fzWRpH2DIN3DLPwMXUMeYDA6R19OEwjk708e1Ilqt76EVAlcqiHFu1u75NfYDiidZuJGt0U+PZCyzIj/I4lRxPH6BEDn5v43TE0Ma+G2e0OFvwii+KfNNWJ+tkVzquj5n4Lm/ZxyWIsIpNxEMvgOW4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109328; 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=pCPK7I5rXb+g55P6MWyJcXND3mxADuY6GKf7ZyxSOeg=; b=Xvck/MPBU1WBINz/iDYylGyJ7Zh8R26bVnl4ywUe+iSZt34ORLjx5gt5fHMrm9RnJ6JFMuCLqnB3pADv/iCgDkHS1VF3yTdsl5csRLQLQbA6N3uawpsf3aOyePezNO0KpUNFwJP1kBUtYmWUyl8vyuBxrDXIMcUGxXxvBqprGyg= 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 1760109328678634.7194339945011; Fri, 10 Oct 2025 08:15:28 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Elm-00007e-81; Fri, 10 Oct 2025 11:10:58 -0400 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 1v7Eli-000054-Jl for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:54 -0400 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 1v7ElP-00047J-5L for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:54 -0400 Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-612-MKMyhrP3MziRcFiZKLs8nQ-1; Fri, 10 Oct 2025 11:10:30 -0400 Received: by mail-ej1-f70.google.com with SMTP id a640c23a62f3a-b2d1072a9c4so194885266b.3 for ; Fri, 10 Oct 2025 08:10:30 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d8c12b62sm253141466b.48.2025.10.10.08.10.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109031; 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=pCPK7I5rXb+g55P6MWyJcXND3mxADuY6GKf7ZyxSOeg=; b=KxaiTc7HdPWUe/YzO4EGeLsjuaECgx4pFzs/E6cGYvv4IO2D2hY6ppSnHGVt+FgP7J0mYs 5EB48nIFbi+UCuLOJqwI4STTTVLACAVVgVcJNuE8xfCYFCD1xyOZI/pyIkRrcUNIGUjN8m XiY35JEn7kIrSNvbLu2aGTmBDBRo6Fg= X-MC-Unique: MKMyhrP3MziRcFiZKLs8nQ-1 X-Mimecast-MFC-AGG-ID: MKMyhrP3MziRcFiZKLs8nQ_1760109029 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109028; x=1760713828; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=pCPK7I5rXb+g55P6MWyJcXND3mxADuY6GKf7ZyxSOeg=; b=LIOKJ7mn08uA3bQGPwe2QT4UlykxUitXItjuk5VqgkM8Wcd0le4WzpM7FzEdRKAm9l 76b0AHCEdmE23sQZDcDFtSUimNVr2xy9zXnvIvUGtjCtghs2Cy9e2H9BNgfjyqGMCPfr vWHglqgHTvUSArYgX1qxlvHmGS7bwURMTbERqNAJJ4737CXSs6fbQNGzCMsPwoKnEyVz CcWe7LporNfvEAH7iTpWpSQVm5MRNVkKXMeRzWrrrtO/9R3T2aiWAQBQt3NAwTMETn2Q Hrq3s10LdxiPPov0X46cHVf+VlLwcP/fKczji7pUv2b+fnz5v7aiOFmEOhPaAIooysmO ia9Q== X-Gm-Message-State: AOJu0Yw89brrexltQmJqajSQ2RrT2y4Xa1Tih7/LGpQQ3qqR1B3N7xlv 47hnWVE1vOfQdHSEsL2HJ51Ftg2y4LKs1pfVu5z4VKptcus8VfaCeJzN+EExlUsbc9ny7i0V5r4 Sif0aePA0cCi2DzjKnoRy5MfZ7eHfFozkBg+sIv0v79TZEPXxdkH0jqXmINUPl/IF7lq2mtLNm5 kIprpIwlc9nqPaXL5GngoiQsl/u6YewVek4Ra4pvJP X-Gm-Gg: ASbGnct4hqn44mwDL5sKLFurJTDcp3EO3OhAgiZXxhyC/lCuGP7HT1tM8sb3eKysI/w 9SKAQ8ry7m9bUKv4ib5VaqR8j3ns018Litplwhq0aM4ifgjhyWoeHZ50aLm9HQTx4PjkJMtdvR0 w712vnq2M1CJfiATY6pLOgV1G31pfDz9ELXizuWWyurnGWoc9LLHiyAY/OXS+cDkGKIAl6thhGh +vmn9Wh2IxQtN49Jz2jvf5MsrDbeX5I5ug88VLGLTfgNuE/0EltTViEfJfQFfA0v2YCX38Wl2Ym Efuo9LI6mPg4mf2IssMRhvsAgQQlnlo2tWd/DT5u80ZSXsMU8SvIbW0h/NPfjbx2RittmSWhKo6 5OsNfvLnkmhlafeN+LfNPHpXXzriZEykOGrHyq24D4eMe X-Received: by 2002:a17:907:72cf:b0:b40:b6a9:f704 with SMTP id a640c23a62f3a-b50a9b61d94mr1243689366b.7.1760109028452; Fri, 10 Oct 2025 08:10:28 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEHf+1tcAvT0bKtrZRDdvEXrXpXVQEPTz2o++Knf/KHNtenmJTOLop/xvRtQ/OliP7ciJSd0A== X-Received: by 2002:a17:907:72cf:b0:b40:b6a9:f704 with SMTP id a640c23a62f3a-b50a9b61d94mr1243685666b.7.1760109027938; Fri, 10 Oct 2025 08:10:27 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 08/19] rust/qobject: add Deserialize implementation Date: Fri, 10 Oct 2025 17:09:53 +0200 Message-ID: <20251010151006.791038-9-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109338998158500 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 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 39f427b3456..9f8fbd49f00 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -39,6 +39,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 f8bd195ca2b..80c496b8a63 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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109164; cv=none; d=zohomail.com; s=zohoarc; b=AKkA5DaLFq6NLobyRTlKAhSfq52nupI15wYYdnETa2VRhz0QYfZcY278RMw5tERjtF8sAp7N1HttqpKO7k0xlkKxgrWChBC3qDkfGNzwNqjr1KK503Z+yuwlzoq7MJQH88R+6xr6i73nWiY7gqLtF5WgTKfm2JoqusnJHZoiqEs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109164; 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=dXiTTUbNHDbzCZW92MIQx4Ti2Mz3wQylh/TTI5lqmoM=; b=Wir9zKCi58mhNTIl4wuJOMM89VeSy6NiezUB3Da/PWNejUqsGzOuxhff6rh8DfGaheYW2km+SuwMTEktNBeT429EfmC9je1qj0fp1bdC4eCO+IlnHFwbjceJ/hcHk+ApGr4kOL5RqCSRaWerKQtXx3vDBwAvvZOQLMcHrmOR1vI= 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 1760109164435740.1970890284906; Fri, 10 Oct 2025 08:12:44 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Elr-00009n-1Z; Fri, 10 Oct 2025 11:11:03 -0400 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 1v7Ell-00007q-Ci for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:57 -0400 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 1v7ElR-00047p-CW for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:10:57 -0400 Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-531-ChmhUmekM5qdlUFwRh93eg-1; Fri, 10 Oct 2025 11:10:32 -0400 Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-afcb72a8816so179533066b.0 for ; Fri, 10 Oct 2025 08:10:32 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d8c12a62sm257055966b.58.2025.10.10.08.10.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109034; 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=dXiTTUbNHDbzCZW92MIQx4Ti2Mz3wQylh/TTI5lqmoM=; b=HspXSoKizhwcpUbbOxBh/dHvSBRdGzbGK2BkhFSH24tRzg33dl0Y+kmdLFiV7G3rl2M9jf n8c0T8b+cVnnbnuyKXfBeiwi2/LNI2rz79lfkRi4V0ndDqB49iIJYHCpkRhq31QQdHw57Q kW57rgIuy7eoCjC/p07VArBDCSKU6lY= X-MC-Unique: ChmhUmekM5qdlUFwRh93eg-1 X-Mimecast-MFC-AGG-ID: ChmhUmekM5qdlUFwRh93eg_1760109032 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109031; x=1760713831; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=dXiTTUbNHDbzCZW92MIQx4Ti2Mz3wQylh/TTI5lqmoM=; b=NlIjhJM5qvUqa53A5ahaEgQsBkGqS8Unv49Vs6pJvEF6W/aqJWwrQLF8gjvCrRml4o 8hatMCbmJBGjgdGHffuqYzETfUx/nguftKcac82Sd4Gf1DYv1oYsPvks9Qx6t5zyf8c8 NkFeVc1XcVe/l+gNlB38ynULMw5rZKugfGGsRvsulDsZBeFnEzT0OHiXJgERw2tpsA2R aWu9OekGrkReC56BicWtsKRkLZPYOUBviS5+t9BnBjqoLj0gzkRpAe71j/yoFQRVr3uj iKM8ZUZHFlGWfzZ5orLHcZPt0a6IWBB/xxKvTI7A5xg8bInhp1mqu/1nQ4KQS7xYhqBV 7t2A== X-Gm-Message-State: AOJu0YxEZohtHSvbwLb73Onvfy0niamovbBdFgUvuV/Qscrv4ZqqB6Li QGOedpjnB+av9bKxPFAmKUzzMdDetLnfaDpA19JAt/SAONlbR5QEvfeLFMzIeT4AooRzaMOKy6X glj6QPaH4JYay9zKyoMGYfApgjsyKWndKo7xeJXRT6BQ/0XtkrK4dvT+80u+pYLM+UhsvwiKV4s 3UbQdR7WefBddfrZOdGMOPh0NRV0pRSu4RQTEQD648 X-Gm-Gg: ASbGncuYg/0JgtzWYVa/XzTEVXXM0KZusX5nMzFa9/CsvII3qBuuO5d7hqM13cR/lDf 8AWjxFb6k5iGxIIh2V5aXCUKFvC8AsAdYaNWZvZEhwJbbbzqXF/vmGNzE4Omc4CpNaF//ybo0EZ 5Sou1kc90B7RdxRQ2y8GCN6FK3xiuZJ/rYXmoMY0tPWcorr0whMo0hEY90g8kqQS4J0Xb07YbZL O7AWkZHmP5LrR2Q7li6JNZ0U71DFYdDYHAr0jMRi/nVHXEA8uSRmGMpLHSaRcKV19tDbZ9zTonT 2OniWwI/46aAbciUN3Doi3N1mMA+01L1PGecJINQBzaQkA0kTp+b6fKO19AhMq5Obu3xRhuuiVk sllCjQke6RyXjo0Z3NcbXZESryDiEGBYcGbsm1WxqrtFV X-Received: by 2002:a17:906:c10f:b0:b3c:f0a4:b324 with SMTP id a640c23a62f3a-b50aaa9fe39mr1191002066b.21.1760109030626; Fri, 10 Oct 2025 08:10:30 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFaT6YihrPLKX768RhgQzNMhDp+ObmC/dt7GsizT9K8qPSVfMTMpLjiAuWRXiQePn/74olbCA== X-Received: by 2002:a17:906:c10f:b0:b3c:f0a4:b324 with SMTP id a640c23a62f3a-b50aaa9fe39mr1190998766b.21.1760109030103; Fri, 10 Oct 2025 08:10:30 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 09/19] rust/qobject: add Deserializer (from_qobject) implementation Date: Fri, 10 Oct 2025 17:09:54 +0200 Message-ID: <20251010151006.791038-10-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109166389154100 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 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 2f0ab2e2821..3aadfb78dfd 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -161,6 +161,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 9f8fbd49f00..aff14a41589 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -39,6 +39,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..84a03bd9f1b --- /dev/null +++ b/rust/util/src/qobject/deserializer.rs @@ -0,0 +1,371 @@ +//! `QObject` deserializer +//! +//! This module implements a [`Deserializer`](serde::de::Deserializer) that +//! produces `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 80c496b8a63..e896aba5f3a 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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109176; cv=none; d=zohomail.com; s=zohoarc; b=hJPTSR+4twLlPlLx+cnrgQtOWrKmJTvEFFO8TDkLKarAsvke9kR4HGgXTVZROcUT1HDQrhxYnkeHY8lxRXpOQqXzhHz97uRHE0jRwEX6IzI1oBZDW1xmsTQegVNnFhHhnMdMMutMF6UKVn5i6zCoWZL0/OTMGz0eS0858kpt5+Q= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109176; 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=EPkG2xx4KAvF4Noc00ffAe3GOSuomsju0SDpOtx5xNM=; b=m8FZzDS2GiWcgLtfktTwojRWff5DKNeFjfO+JjT7F6cMkhBEPR7ADAYe9B4CdxnAChugalUNRgTINxeAVPmYOEvnmAmI4JsEcndci9LCLIBpXZmEDa3WmDn5QyLQB8/PILuDDx3K+IBVTgZOCC+dUWhGyJOzjtbK7eaAkfERWEI= 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 1760109176148468.21151315578095; Fri, 10 Oct 2025 08:12:56 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Em3-0000Kq-Cw; Fri, 10 Oct 2025 11:11:16 -0400 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 1v7Em2-0000KJ-M5 for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:14 -0400 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 1v7ElX-00048X-Pn for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:08 -0400 Received: from mail-ej1-f69.google.com (mail-ej1-f69.google.com [209.85.218.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-345-wWnrfa5NMhiRRcD_L0xDtw-1; Fri, 10 Oct 2025 11:10:34 -0400 Received: by mail-ej1-f69.google.com with SMTP id a640c23a62f3a-b3d21b18e8fso198593866b.2 for ; Fri, 10 Oct 2025 08:10:34 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d9526885sm253031366b.84.2025.10.10.08.10.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109036; 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=EPkG2xx4KAvF4Noc00ffAe3GOSuomsju0SDpOtx5xNM=; b=amSndQekBvvtZzIxIHtPImJUcZUsDhXwmI4sNSdNT42o1kxqmuusWBF4wStb/CWKKZ2GBt 4i+QukJcYTETdskTAeb5bWNTHRf5qu5XidGGzyErnWwzj81Fd9CuBZOMOkj+0ZYxB79oiC njePuVpJp/69597yzwEbuam6jsHM9R4= X-MC-Unique: wWnrfa5NMhiRRcD_L0xDtw-1 X-Mimecast-MFC-AGG-ID: wWnrfa5NMhiRRcD_L0xDtw_1760109034 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109033; x=1760713833; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=EPkG2xx4KAvF4Noc00ffAe3GOSuomsju0SDpOtx5xNM=; b=La6gAyrr9mXxQE7UFQzOhbt2nFDQtWUyZyZcS2gfYt6AiZbYw7ONGS4sxxm78z2W1j rh5cVuWTqIKmX56DpytiewRwMRyvnA8H2ULxc3hoY8z8ryVgpkZsb3Jp0O/+/BpJ6SVK 5nQkyYFh0Dt001gu7g/mmdTBzaLWcnAhWeZ7o9KFVw9KHPvDn5RRR0YU+R+0ne825RLx jDYQoigm/3W5GQ4tDOmM23C0DH3mOSr4BpeLF75hfj+goFoBXgH5G4sFwNgFLdzXp7pW zcx1HNsAnbA2hhsNYLzD6lCWhWPQOqYty/PBaaVKchZwSfMBqtVljemhlWlUutaFlxWU EMLQ== X-Gm-Message-State: AOJu0Yy4gJ2J4ZigwpG7SjZvGH5lVWNu8SaL3jOiwwtR2B46D4hFmmuV deqsjqMiGEBvxBJnzZ8qFeOVWLn7k2aXNaqlPUqavVlZir030luMqYFbz7aY02Ek/t/GT7vbmL9 f85QKmFR9NuSkiT7NALR8fA/+g11p0mcCjxYrWacGZniaV2hdLNclHx1ROqhTVYeVyHH/sRfjy0 a10BG9tVPKztK0VL2Tb22ppYSd67BFn2rde0V2I31H X-Gm-Gg: ASbGncv2YuBPPCJ1Hd+sGUr3iVNa8uouegEdLwjh6Bszdt/C110A+qk3QryJdX+tuj2 z4eAJUDF4CCn3W+rkYVl2CFxtvvmhIBajdgitvcaj+u5S8UNAbLuelhs/4G5F63+hI21y6GxdUf gowqQrtuE73X/esu1NhhWb+r60la5nIYSMQ1HEjhrCwvsCxLXn8gSjPnot6BwWxcpMMbtjZY5bL +9ttP9lJ7pMQBaeQD34RUPK9+uX89Rp2/FkacX4+y2gWZfX6UMa1DFt0JyH8BTV1LX251HJ1OGK Jif99TLMMq64na4PK+j+/LKP6B3El7VfSku9pmJ4vG+FLZN/BvvXJVpb4xj/v9mwtifqdp6H9cA m8fKTtbGrt6ae5/NubYO0mJp2bsrtolb9+MsAwR6xmA02 X-Received: by 2002:a17:906:4fcd:b0:b50:697e:ba3 with SMTP id a640c23a62f3a-b50acc1a958mr1310614666b.63.1760109033068; Fri, 10 Oct 2025 08:10:33 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHDAAstEQn10E5MsdgMsXmttGl8Yg1vLsUH+5bnWAxRACDNkifI2nueZYHnrwsMjlGLgzC8Kw== X-Received: by 2002:a17:906:4fcd:b0:b50:697e:ba3 with SMTP id a640c23a62f3a-b50acc1a958mr1310611066b.63.1760109032539; Fri, 10 Oct 2025 08:10:32 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 10/19] rust/util: replace Error::err_or_unit/err_or_else with Error::with_errp Date: Fri, 10 Oct 2025 17:09:55 +0200 Message-ID: <20251010151006.791038-11-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109178410154100 Content-Type: text/plain; charset="utf-8" Introduce a simpler function that hides the creation of the Error**. Signed-off-by: Paolo Bonzini --- rust/util/src/error.rs | 52 ++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/rust/util/src/error.rs b/rust/util/src/error.rs index bdbf2634170..5a0dd7786b5 100644 --- a/rust/util/src/error.rs +++ b/rust/util/src/error.rs @@ -14,8 +14,7 @@ //! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to = build //! a C return value while also propagating an error condition //! -//! * [`err_or_else`](crate::Error::err_or_else) and -//! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `R= esult` +//! * [`with_errp`](crate::Error::with_errp) can be used to build a `Resul= t` //! //! This module is most commonly used at the boundary between C and Rust c= ode; //! other code will usually access it through the @@ -208,35 +207,21 @@ pub unsafe fn propagate(self, errp: *mut *mut binding= s::Error) { } } =20 - /// Convert a C `Error*` into a Rust `Result`, using - /// `Ok(())` if `c_error` is NULL. Free the `Error*`. + /// Pass a C `Error*` to the closure, and convert the result + /// (either the return value of the closure, or the error) + /// into a Rust `Result`. /// /// # Safety /// - /// `c_error` must be `NULL` or valid; typically it was initialized - /// with `ptr::null_mut()` and passed by reference to a C function. - pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()>= { - // SAFETY: caller guarantees c_error is valid - unsafe { Self::err_or_else(c_error, || ()) } - } + /// One exit from `f`, `c_error` must be unchanged or point to a + /// valid C [`struct Error`](bindings::Error). + pub unsafe fn with_errp T>(= f: F) -> Result { + let mut c_error: *mut bindings::Error =3D ptr::null_mut(); =20 - /// Convert a C `Error*` into a Rust `Result`, calling `f()` to - /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. - /// - /// # Safety - /// - /// `c_error` must be `NULL` or point to a valid C [`struct - /// Error`](bindings::Error); typically it was initialized with - /// `ptr::null_mut()` and passed by reference to a C function. - pub unsafe fn err_or_else T>( - c_error: *mut bindings::Error, - f: F, - ) -> Result { - // SAFETY: caller guarantees c_error is valid - let err =3D unsafe { Option::::from_foreign(c_error) }; - match err { - None =3D> Ok(f()), - Some(err) =3D> Err(err), + // SAFETY: guaranteed by the postcondition of `f` + match (f(&mut c_error), unsafe { c_error.into_native() }) { + (result, None) =3D> Ok(result), + (_, Some(err)) =3D> Err(err), } } } @@ -421,13 +406,16 @@ fn test_ptr_or_propagate() { } =20 #[test] - fn test_err_or_unit() { + fn test_with_errp() { unsafe { - let result =3D Error::err_or_unit(ptr::null_mut()); - assert_match!(result, Ok(())); + let result =3D Error::with_errp(|_errp| true); + assert_match!(result, Ok(true)); =20 - let err =3D error_for_test(c"msg"); - let err =3D Error::err_or_unit(err.into_inner()).unwrap_err(); + let err =3D Error::with_errp(|errp| { + *errp =3D error_for_test(c"msg").into_inner(); + false + }) + .unwrap_err(); assert_eq!(&*format!("{err}"), "msg"); } } --=20 2.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109157; cv=none; d=zohomail.com; s=zohoarc; b=JNNDbfhntavmfFUVf5PMpa9444QmEBr5Ug5pDeUIEG4FRiZJjXHNOmE8s0KtX4i5ZiTuIHeFbTcFxhucQd1HbNRtI3QBZveHKj1PV91bW0auKNf8APkgSXQRtvYRnQ545BxlxIYf8Tw82sBndtsSlGNgg+Wtio8p+jmk/XuqfZE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109157; 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=WosTp1yK3Q8xfkSSHqzAC8nqLnmsf887SgMNENXzcxk=; b=NJZ1W8VH0JyAOsZllwYem34P/qTLaAKTuT8lTgBBCxptBV4MWw9AMfJ29HviwvOVXm5OA1UbKdZgGmfvpXtdT3CX/2TEq5mDeET6+UWjCPvxg94hx1lx40EjKlwk0FliNyiXinH5Hy3TXvVEUTs4WZOq9XgPmznaW/IK5WBPyCM= 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 1760109157565907.3927165648834; Fri, 10 Oct 2025 08:12:37 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Em5-0000M6-6x; Fri, 10 Oct 2025 11:11:18 -0400 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 1v7Em2-0000KI-Lc for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:14 -0400 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 1v7ElV-000492-PI for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:12 -0400 Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-657-QplrPtytM1GX_zAYZ4Zw9w-1; Fri, 10 Oct 2025 11:10:37 -0400 Received: by mail-ej1-f70.google.com with SMTP id a640c23a62f3a-b4626b2d07bso269963466b.1 for ; Fri, 10 Oct 2025 08:10:37 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d951d210sm254257866b.74.2025.10.10.08.10.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109038; 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=WosTp1yK3Q8xfkSSHqzAC8nqLnmsf887SgMNENXzcxk=; b=YYZlNyRJCwqovNqrai+bYl8ghiY5h+N/Ydbn49Cd2mwYtcLsIpzo22iGD7T1CKDIitSXoF PBW9kwh3YY7JXfPN1zgPA77cGOb/ImGqft/FIFiZvik2D6pAIWLwhFEJDkwrOw1AxX+uyU JXpO1nQOskJKBxMz7jZZJuSrRdP7Y4Y= X-MC-Unique: QplrPtytM1GX_zAYZ4Zw9w-1 X-Mimecast-MFC-AGG-ID: QplrPtytM1GX_zAYZ4Zw9w_1760109036 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109035; x=1760713835; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WosTp1yK3Q8xfkSSHqzAC8nqLnmsf887SgMNENXzcxk=; b=t4vbPP5O3VR97kNHHXcXjAxsRsUwgDZwhNqjWgUJyioSMJvCvlURdTgW20Kz1tOg4q fRWGYJ1HyNmDTxS85RimDik/jlvqxrVsfE+/pYx81r7C8s1qGq4gy7fXjPSZi7+j0dob 6Q+3RWeGKFkLJjyu4xjPiQfIYsRN9Rvs8wLpaXAemH80PzRKc2V3CVyEi2mDBSzrBQUx IZZzWJ3lmBowO9Q/lSdnXBk6HKGUtxd+FMvmBhKGQCQkwoqA/aPKDQjkE5sxwF8OK5bd a2pvLF6T9lj0uLOD33pfszE+CKxcRJvTFLk2auyh/fPlkxIKj42QzGaQ/1XEvk5nf7Pr 46GQ== X-Gm-Message-State: AOJu0Ywir3bBcp3ZJhaM6feMJwWkTqkCamvVPbO9xnowAFqUSaNH9ewD q5bI8wQQYlKzl7Xt4YNzbDC5alKC0ysGOIb1xaPITH0JrWMTlxSbql65eu+4Z/Oh9zpDK9KzyFl kBU1bgYvWRASLBhY3/jU0UqE6KSqS1mSVsEFklTmP31Q+WV2ax8ZqA5SokR2LDsOTZ32VERrC0w xklHTJbjhWNhIRIw9ENEmzgHwFXBPFEAkKWs9grrMf X-Gm-Gg: ASbGnctPzyXjoUg20wqpRoWV/b6FveC4+u3GhyEpwmrsg9gMBcEeq9wr6tYRiMNbxTt MYBGZFu497+XQjHIuEwGvlMKrXu4O9HmtHSNMQYZlPNbMN7FvyEeCNOuw/6vVVA/s0ph5YKhSd/ ijr6sO0xzZ6c04FMBY4l9991qGQXeRhQzqL0g8KEc7w4x8ehpvttk3LVQCa90pYOT1H1ADZexyz VVBTVCDvCyk9pF4xNv6cbpHhmygkHELoY49Y4R80boRi4V27iwCMDJUWM+DJitnUxNzZpnEFoSb 5z78wpcrl3AddXQaDeNqQlhSeznufv0PXGB2hD9CCE2TACHeKZXKrcjQDTFCx8mVvH8PHl4CK/4 UnVD4YCye+GV+c5cwa8cvi+XSFmCI9CpYbg9HM9LxMDqx X-Received: by 2002:a17:907:9448:b0:b40:a71b:151f with SMTP id a640c23a62f3a-b50ac1c4e7cmr1338884466b.30.1760109035247; Fri, 10 Oct 2025 08:10:35 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEc8ddHlRxqc2C3ZyaFil4eELHDUpY9sAIKvOHlmzwkIB8TO55EXcYfAaz0BzMmRZARW7u7ww== X-Received: by 2002:a17:907:9448:b0:b40:a71b:151f with SMTP id a640c23a62f3a-b50ac1c4e7cmr1338880766b.30.1760109034733; Fri, 10 Oct 2025 08:10:34 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 11/19] rust/qobject: add from/to JSON bindings for QObject Date: Fri, 10 Oct 2025 17:09:56 +0200 Message-ID: <20251010151006.791038-12-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, T_SPF_TEMPERROR=0.01 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: 1760109162749158500 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 --- 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 e896aba5f3a..292a3c9c238 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; @@ -111,6 +112,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 impl From<()> for QObject { --=20 2.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109176; cv=none; d=zohomail.com; s=zohoarc; b=WPPeF+IrfZYBGdqHPFPWQdqccP4eO80VfRHsFYSYDGTgpLS3OVfGtcHGH9Qv3OhNFPNutklla+yJSx+Z8jRU0Y5HCxh9I44TBusyEDaOQCFsGf1ANlFjT5hACPTDD8TbSplKW3ogbYfIzUQ+UDr5vFhW00Iuk/sqzYhjI2yGyXg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109176; 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=bsuDg+5N7j9JWWIAOco6bdUmkbDvZiJZ+pBxqlSdH5Q=; b=QVMO/3ljB/fqj3vUAQgcbOeVOu5tJMNPfGJQ8t4ESi7lBENbH90CBC5dBD81tmt4I6xWEGdIm/2SWoXSv2SHwbZmXhx0Oh2P3kX1wpQZCCSxPXyDLebSfmAVaJVamqzvcPC1J1S9/9OalFDrOM9h4/uuD3Hf6QJp+MhaaqUzNJo= 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 1760109176473703.3924018059429; Fri, 10 Oct 2025 08:12:56 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7EmB-0000Sj-Ni; Fri, 10 Oct 2025 11:11:23 -0400 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 1v7EmA-0000QS-Py for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:22 -0400 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 1v7ElZ-00049j-6F for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:21 -0400 Received: from mail-ed1-f71.google.com (mail-ed1-f71.google.com [209.85.208.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-629-SQZ9-khPOdS6AZGrh2nC9w-1; Fri, 10 Oct 2025 11:10:40 -0400 Received: by mail-ed1-f71.google.com with SMTP id 4fb4d7f45d1cf-633009e440aso2856885a12.1 for ; Fri, 10 Oct 2025 08:10:40 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-63a5c32249esm2455770a12.41.2025.10.10.08.10.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109041; 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=bsuDg+5N7j9JWWIAOco6bdUmkbDvZiJZ+pBxqlSdH5Q=; b=bKD7q7M/aUg2rlgQMokMTB0QgOy1tYx0G2qaDKhJKR9MC0hvMSWmqZ53en/4zgpxSWb181 26mflt4VBlnbYwgjiUVh+UxRWijtt28PbioUWpBSNpi9uchSjmNqLoxrQw0NY5eOKsj40H /H5d35CGDWy1GexX3SISlJQlPlp5pvY= X-MC-Unique: SQZ9-khPOdS6AZGrh2nC9w-1 X-Mimecast-MFC-AGG-ID: SQZ9-khPOdS6AZGrh2nC9w_1760109039 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109038; x=1760713838; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=bsuDg+5N7j9JWWIAOco6bdUmkbDvZiJZ+pBxqlSdH5Q=; b=Y0UHvCHW7SIqpyR+b+1lXLuPcUvsHrEn3D1MLxOCaujDRQmd+Nr8FCgstakurxs318 80vuUEVoPT0AAJ2iMezRN18sO66xKihafM+G6F/SYCRXA0nJ6O/KgAtpRqEZLzR2Akoh 8Gw5AiKAmVHUSvsxyg5aWje+vFnJH7yHLPKaB1l19xWoo+JiGa6Rcl/QhCWaqU1kdObK iTPD6JcM/mt0BIthZdyocMAkvcu/0cZ4I9UEM91AwUD73rOmZHQPHpheesZQSugcqgwK Ova26dYyWZH6+EzMCNYn8UuxcdnRB7YyH11ex9vHK/f44E8/dJVeyzkVupAJreHmUx4G y/3Q== X-Gm-Message-State: AOJu0YyiMoZAMkPJbsqrV71MmJyR6CFX1ynDnDSvgTrYjHYyfhfE/sS7 2yp4pxHwkejmCGw6Q9bPrwuEhPnU4zM7H95QpZZrmNwGa478b8K0+ke/WwpMnA6uOUesxchgABL VALzkhtB5WNKq8TSPewZXn2ypOLYG98BEPK3+RVroe4HNYszvKFwilp/Fy/LXJ6NGq5WYUtG3ED flfIC69sJnFKcogLE7QtEO9IvGp1HJIy7lMAS7WhPc X-Gm-Gg: ASbGncsMxiutOwo6UKpo3YaBd8PmjyKlRDEd58RmfXpXwMdOLaGdogRcEsMvx2yplGy rqqKTnxoqqCjUWHzaVKApR2Vel8/snZvQhXhYhGgxyTHqO4uIeTm3jFDp0AVaQD6ZsyEZFSvseG jhu9uH5mucH7W6D0VRrM+ocjlwzXk50VEa4eQnh+G4r/Pfz6OPVtIMzkAX39s0iBwL0u6yoOxbi kS4pFx9oB1QRoRtoJ1UjhvoW8wk5Bes3/MVgD0/ss1k0N5z88xdT0UG28wKoonRwoHicM84I67j uWJgj4/TMq9L4o1U2Df/bZ+PvVlkdQAmR44RTbjO4FUnmSi4M/THlsAMjUQY21qBJGU7UDYVknK MZxWI/9U5DH3u0Saal6Q1SzWe3Cjv2HOntqsO/yHf9pPJ X-Received: by 2002:aa7:d5cc:0:b0:61e:d2f0:5875 with SMTP id 4fb4d7f45d1cf-639ba74b30bmr12289439a12.1.1760109038498; Fri, 10 Oct 2025 08:10:38 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHb7DtcVGVaEg6pEnHRjhhov0dDCAdTcxK07IMyP5zw75Xmx/jCCdYI1yEsYxCAxqiUnkiHMQ== X-Received: by 2002:aa7:d5cc:0:b0:61e:d2f0:5875 with SMTP id 4fb4d7f45d1cf-639ba74b30bmr12289407a12.1.1760109038044; Fri, 10 Oct 2025 08:10:38 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 12/19] rust/qobject: add Display/Debug Date: Fri, 10 Oct 2025 17:09:57 +0200 Message-ID: <20251010151006.791038-13-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, T_SPF_TEMPERROR=0.01 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: 1760109180956158500 From: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini --- 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 292a3c9c238..38b98fdb1e8 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}, @@ -256,6 +257,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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109238; cv=none; d=zohomail.com; s=zohoarc; b=Ec0vPGbef6DtV1C171jsikhXcq4uLaIyeoikrHxcCw+EZVTkjL/2caSoLdgX7i+GrP6ZTTiPpX8jdkgjqVa3tWZzwD7aZF0xr1t5Uq+DTaV9aSw/fRS8iLOqcWgQTSyjkTUmZnM57vvRN6Pg3mRDsBnzSeZY1Vi7zpw3qXqTw9I= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109238; 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=hJgKnO4p5NCBfsiH7X9uVt6uTBPOKNgBfFsL5Wo3g90=; b=hkuCjFd/8lcG2SkpBybzQ7x22Vmt0h+f7yuOvKbUpg32GJX85YusmyUfd9rvS6k7u6n9s9Tdb3MenvNPwU+5syTr74bWgyoBZ8tU29jnL8uRyaJP+fClGjrm3/N/wSr4HwdzUAAfaVSJuKjuzd90E3QSY/FKXvbwtbnFs8eNnT8= 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 1760109238341171.49509012443036; Fri, 10 Oct 2025 08:13:58 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7EmO-0000Zy-S2; Fri, 10 Oct 2025 11:11:38 -0400 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 1v7EmD-0000Wv-Qk for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:27 -0400 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 1v7Elc-0004B3-Fs for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:25 -0400 Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-104-KNCh28XKOwKgEpxh0tonwQ-1; Fri, 10 Oct 2025 11:10:43 -0400 Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-b47cf7544e0so217080866b.1 for ; Fri, 10 Oct 2025 08:10:43 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d8c12a08sm256553266b.52.2025.10.10.08.10.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109045; 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=hJgKnO4p5NCBfsiH7X9uVt6uTBPOKNgBfFsL5Wo3g90=; b=P7vcDPwSNpu05Pjdok2nxsi8MvmUXsfLoJ7L56Avr340JBpMUvOT3I7IcHkvzxvn7/Jn5u coUSapY+ujEVwQW9DhgNYpZ6KKiPrVYiy+WJ+X3OHUyOAOSHXXnu8EERaNvCLtv8aBmzkJ mFVDH28JY1FFkjtGgzRue7YNXTlMcuw= X-MC-Unique: KNCh28XKOwKgEpxh0tonwQ-1 X-Mimecast-MFC-AGG-ID: KNCh28XKOwKgEpxh0tonwQ_1760109042 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109041; x=1760713841; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hJgKnO4p5NCBfsiH7X9uVt6uTBPOKNgBfFsL5Wo3g90=; b=OfGysu9/3LIUrbjBV4nkkYYjST7bYCo4+2zxti8lhOq71BkfxkiXTZdCkGuWbhGI4u CGHjo4YAio+r0lu+/41hWPrmwOLOJp2G6aR8o7lzyCGOk3Q8ZmwLgCQqKdpthyohdcB6 uMdL6OMNTRaZ5H1HSuIVi+Zivjt/rpqIc0aHcGSTEs2OocJ7pmYS9LsDx3bcxzJJTndt BJTk1x4MgDXjxfQlVcXYDjFQyt88m0+s/bPqs5y45EfZTF7zVazMf/AQbNZRUE7fNOMu 2zS01ZEiiPe7e+U/9FLc68akxe4Wc3vxfREUeoMjni5kOuxpe5LX/Uq9oxyoNKFHbMZO /NYg== X-Gm-Message-State: AOJu0YzOAI7NQoRsMPXXEfl9NzMLg+P3w9Ef4iUDFcXm7xe66YUTuEto Yr65tU6SZTPKjMKtdkSUhjUSg7agjmpcLtRwba0dDfuopbSQq4qkle9IfL+EKpvtKpdpmE+QIrX 5GsxCCNkvRwOjmlIW4ifa6aZJ7khcau9Iq2UQcfR+LnAarisLn4B6bFgoH1IJhDhWf4GB9tsgrd yY+/swUZ3AUrhX3zcPTGEbM41qjW0KDOH3J2DN6evq X-Gm-Gg: ASbGncsAPi55akiXmzCRfLWZ5Tx2QRrhPqHfwQR34TLarfY79QVCOhRbDViMFV8bRXE 3Ri4TInUde1Jrkd9VwQGxTXMoBc0Ah21UmWuQUV11GSjKo6wRZKwucLpXI2LnnT3J2Fc4uyd05n 25V+wRnNohO6V9fRtKF9itjxt55dHMEQ6J4uJ8CzWb2rqEVVX+si4vrfovqHP7Y/YCK95k9PVTB BvZxnbXmc+B9ZdoshlYGfMeOETjCGHHmyXwjJbvwdESaCERr2JEF2SceSDHwK4qUDImfZZN1wL/ oTd3TJRft1jNi6+6iwGGnpBAPqjg60Z5UNQ72dXGH2TAX7zQV3E8WOjux9FKjLkxBQYtKeBMEO1 G2+qnx6aQQ5TEcpTR66HvtMBUpNyP78GSakmiylRIfbHA X-Received: by 2002:a17:906:4788:b0:b0c:fdb7:4df5 with SMTP id a640c23a62f3a-b50aa08f7f4mr1389339266b.18.1760109041473; Fri, 10 Oct 2025 08:10:41 -0700 (PDT) X-Google-Smtp-Source: AGHT+IE491ow71Xn+kTdRFhbgU/RYKqRUnCR6cj5ICQnAJbi9K9r4MnXO8OtjBc2PBq3vRKmHl1bIg== X-Received: by 2002:a17:906:4788:b0:b0c:fdb7:4df5 with SMTP id a640c23a62f3a-b50aa08f7f4mr1389335966b.18.1760109040950; Fri, 10 Oct 2025 08:10:40 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 13/19] scripts/qapi: add QAPISchemaIfCond.rsgen() Date: Fri, 10 Oct 2025 17:09:58 +0200 Message-ID: <20251010151006.791038-14-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109241719158500 From: Marc-Andr=C3=A9 Lureau Generate Rust #[cfg(...)] guards from QAPI 'if' conditions. 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 --- scripts/qapi/common.py | 16 ++++++++++++++++ scripts/qapi/schema.py | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index d7c8aa3365c..f16b9568bb9 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -199,6 +199,22 @@ 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 + if isinstance(ifcond, list): + return ', '.join([cfg(c) for c in ifcond]) + oper, operands =3D next(iter(ifcond.items())) + operands =3D cfg(operands) + return f'{oper}({operands})' + + 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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109367; cv=none; d=zohomail.com; s=zohoarc; b=fFvjfohyMcnccaNsdVgz0Gxt5AvASf1so5WwGFh1YScyt5u0sxYIuvMve4hxoTC2LmEBD1A+S8KSl9E6e6wx99Uapj5UGuSdO0BwjGtW0C0whzMJGaThq4xWzuvMYNbE+uh9BElwZKEzXNK73HOTdt7CXFbg4ho+o0FkW+MlXhY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109367; 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=o7ouAlusqvuJYEEC4ofBOBun0PcWoe56XJ+M6Wb3AdY=; b=N7OckestA4+ux3TOM8GtOxIn242KNOK+SabqlIrK9fwbGO3vYsfU00PNsHaD1IeAlB2JgxfxMyb0V9aGghqO4SOWvG9MG8DeFzSXFF7ebt4MBOYbJcB0XXFXYkJiqllkSC4AFjwsjGbz3D239N7U0WQWAjZlhG9W5ckEVfIPxBg= 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 1760109367518274.5784433066748; Fri, 10 Oct 2025 08:16:07 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7EmW-0000fS-H8; Fri, 10 Oct 2025 11:11:45 -0400 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 1v7EmI-0000a0-UX for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:33 -0400 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 1v7Elf-0004BP-An for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:30 -0400 Received: from mail-ej1-f69.google.com (mail-ej1-f69.google.com [209.85.218.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-513-bQIlov37PIuB7Clp2LCfDQ-1; Fri, 10 Oct 2025 11:10:46 -0400 Received: by mail-ej1-f69.google.com with SMTP id a640c23a62f3a-b2d1072a9c4so194909366b.3 for ; Fri, 10 Oct 2025 08:10:46 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d8c12978sm253940466b.44.2025.10.10.08.10.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109048; 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=o7ouAlusqvuJYEEC4ofBOBun0PcWoe56XJ+M6Wb3AdY=; b=Gy+fpuvFGYEcEXx3Yuth3sdOwsE16VVcIaxIH5QHJ/v1hT4EvCy9vUdqnGNvyR1PDfTulz sUxR8cxq4M/DWQ/iR+bKmlVOoIXDCU1qUlFlWn2wVpeHgpkwTXxRKiD2i+Iam2dE3k6AWP y+rN61XjRYNnK8iWh4Jt4WLNmkYZiSY= X-MC-Unique: bQIlov37PIuB7Clp2LCfDQ-1 X-Mimecast-MFC-AGG-ID: bQIlov37PIuB7Clp2LCfDQ_1760109045 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109044; x=1760713844; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=o7ouAlusqvuJYEEC4ofBOBun0PcWoe56XJ+M6Wb3AdY=; b=wNZlGPwigEZj3HkVU6zYgvgf4/A5BkBvDj2MQ4l4sx7KXVkoT8fASaY4Ov01ZapcFf GnfJQvL6kLkMp+Ru2PPDgSjcQYRvW8r+tEBGvhVAQ/FDtciCC5V9WQzejBpM/h+K+ncw 71PzHzeRVIuE5ali1IxIu3jS8IOOouSWwI9WRpwxPwMwc0xqf4JSNtramP0hH1g+ucYU iWHT1zmfA1gZT8dbn2BSkhv4omUC+f2B6gxRRqi7x/l8aiH0LCsdCzym2zB7XafBOC9w hFpVPIrxXkFRlQnwXU5ZWL9XRxOoS4FFQ9hmBWM18ItkMtiJktg8veW2AxOuGRGJvSWg RxEA== X-Gm-Message-State: AOJu0YyRVd+dG0UtoICJjvelWmJIrTHHiqm0AdbYBR0AnbMLcjC/hGYF CO701/F5pW10X8/In5vzgutBUjHeCbDyfZVOxpQbK+DrVIbWIgDsmOIlQizYFzcRac3RIYoZggB YU4E8TcWpC5sfW0pYMN6gLzoNw+Nnt7MioGcSAOD1Qq3Yt2N4Se6Hc2xmaFHxMopu835QfuUJ5p +AgvsVYcTKjWzLI4DkE4vQ7TvPXaXUt0JtL1f+UvXc X-Gm-Gg: ASbGncvfgbe61YN4sUSTiH56C9WforfyK/8HYrLU0FSAeT/Il29wbcr/ozLGAFlp8eb QGbQhxtA8d/r7lExD9+hkVP8CsHxSZN3gxn5AoOadjCt/I06CEqGDq+Av0+slnrq4e9VAe+0fCr e9qdRuPq7PeRlWiVngwtTQMCMH0tkTQreSBZ8JXIwLKSEdV+Iovpm6LO1FpTuXvrG09u3HT+qdC GEpcizntoChHh/N4jYfve4wsPxwhinG90gaUU+x5vQiE7SxHww4uAbUn+FRWzVmfmMGmytGDIG8 k4MEmddWv9pK7OwhaM4AM26FOuE88OrRrjSXR+LfdpIWeLSgnbXUTRszdgDd6yq4YJv63AyLhIM sPdz8XElPJHpvg3luolu2z36lg7wgk8cq0appUj67z92f X-Received: by 2002:a17:906:ee85:b0:b4e:f7cc:6346 with SMTP id a640c23a62f3a-b50aa08f852mr1145094266b.15.1760109043931; Fri, 10 Oct 2025 08:10:43 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHzA8t1uomIKaixAqDtUL/CYFXN/e0VnSTUUNA3/03C1Pr3W1AeWXJOgW2ERfTTqkFLfGj75Q== X-Received: by 2002:a17:906:ee85:b0:b4e:f7cc:6346 with SMTP id a640c23a62f3a-b50aa08f852mr1145090266b.15.1760109043261; Fri, 10 Oct 2025 08:10:43 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 14/19] scripts/qapi: generate high-level Rust bindings Date: Fri, 10 Oct 2025 17:09:59 +0200 Message-ID: <20251010151006.791038-15-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109375493158500 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 are simply aliased from FFI - has_foo/foo members are mapped to Option - lists are represented as Vec - structures have Rust versions, with To/From FFI conversions - alternate are represented as Rust enum - unions are represented in a similar way as in C: a struct S with a "u" member (since S may have extra 'base' fields). However, the discriminant isn't a member of S, since Rust enum already include it. Signed-off-by: Marc-Andr=C3=A9 Lureau Link: https://lore.kernel.org/r/20210907121943.3498701-21-marcandre.lureau@= redhat.com Signed-off-by: Paolo Bonzini --- meson.build | 4 +- scripts/qapi/backend.py | 27 ++- scripts/qapi/main.py | 4 +- scripts/qapi/rs.py | 181 +++++++++++++++++++ scripts/qapi/rs_types.py | 365 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 577 insertions(+), 4 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 afaefa01722..ce914217c52 100644 --- a/meson.build +++ b/meson.build @@ -3571,12 +3571,14 @@ 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/features.p= y', 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..305b62b514c 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 @@ -36,7 +37,7 @@ def generate(self, """ =20 =20 -class QAPICBackend(QAPIBackend): +class QAPICodeBackend(QAPIBackend): # pylint: disable=3Dtoo-few-public-methods =20 def generate(self, @@ -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, builtins) diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index 0e2a6ae3f07..4ad75e213f5 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -12,7 +12,7 @@ import sys from typing import Optional =20 -from .backend import QAPIBackend, QAPICBackend +from .backend import QAPIBackend, QAPICodeBackend from .common import must_match from .error import QAPIError from .schema import QAPISchema @@ -27,7 +27,7 @@ def invalid_prefix_char(prefix: str) -> Optional[str]: =20 def create_backend(path: str) -> QAPIBackend: if path is None: - return QAPICBackend() + return QAPICodeBackend() =20 module_path, dot, class_name =3D path.rpartition('.') if not dot: diff --git a/scripts/qapi/rs.py b/scripts/qapi/rs.py new file mode 100644 index 00000000000..2a9bbcb9f54 --- /dev/null +++ b/scripts/qapi/rs.py @@ -0,0 +1,181 @@ +# 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 +from typing import NamedTuple, Optional + +from .common import POINTER_SUFFIX +from .gen import QAPIGen +from .schema import QAPISchemaModule, QAPISchemaVisitor + + +# see to_upper_case()/to_lower_case() below +snake_case =3D re.compile(r'((?<=3D[a-z0-9])[A-Z]|(?!^)[A-Z](?=3D[a-z]))') + + +rs_name_trans =3D str.maketrans('.-', '__') + + +# Map @name to a valid Rust identifier. +# If @protect, avoid returning certain ticklish identifiers (like +# keywords) by prepending raw identifier prefix 'r#'. +def rs_name(name: str, protect: bool =3D True) -> str: + name =3D name.translate(rs_name_trans) + if name[0].isnumeric(): + name =3D '_' + name + if not protect: + return 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 rs_type(c_type: str, + qapi_ns: str =3D 'qapi::', + optional: bool =3D False, + box: bool =3D False) -> str: + (is_pointer, _, is_list, c_type) =3D rs_ctype_parse(c_type) + to_rs =3D { + 'QNull': '()', + 'QObject': 'QObject', + 'any': 'QObject', + 'bool': 'bool', + 'char': 'i8', + 'double': 'f64', + 'int': 'i64', + 'int16': 'i16', + 'int16_t': 'i16', + 'int32': 'i32', + 'int32_t': 'i32', + 'int64': 'i64', + 'int64_t': 'i64', + 'int8': 'i8', + 'int8_t': 'i8', + 'number': 'f64', + 'size': 'u64', + 'str': 'String', + 'uint16': 'u16', + 'uint16_t': 'u16', + 'uint32': 'u32', + 'uint32_t': 'u32', + 'uint64': 'u64', + 'uint64_t': 'u64', + 'uint8': 'u8', + 'uint8_t': 'u8', + 'String': 'QapiString', + } + if is_pointer: + to_rs.update({ + 'char': 'String', + }) + + if is_list: + c_type =3D c_type[:-4] + + ret =3D to_rs.get(c_type, qapi_ns + c_type) + if is_list: + ret =3D 'Vec<%s>' % ret + elif is_pointer and c_type not in to_rs and box: + ret =3D 'Box<%s>' % ret + if optional: + ret =3D 'Option<%s>' % ret + return ret + + +class CType(NamedTuple): + is_pointer: bool + is_const: bool + is_list: bool + c_type: str + + +def rs_ctype_parse(c_type: str) -> CType: + is_pointer =3D False + if c_type.endswith(POINTER_SUFFIX): + is_pointer =3D True + c_type =3D c_type[:-len(POINTER_SUFFIX)] + is_list =3D c_type.endswith('List') + is_const =3D False + if c_type.startswith('const '): + is_const =3D True + c_type =3D c_type[6:] + + c_type =3D rs_name(c_type) + return CType(is_pointer, is_const, is_list, c_type) + + +def to_camel_case(value: str) -> str: + # special case for last enum value + if value =3D=3D '_MAX': + return value + raw_id =3D False + if value.startswith('r#'): + raw_id =3D True + value =3D value[2:] + value =3D ''.join('_' + word if word[0].isdigit() + else word[:1].upper() + word[1:] + for word in filter(None, re.split("[-_]+", value))) + if raw_id: + return 'r#' + value + return value + + +def to_upper_case(value: str) -> str: + return snake_case.sub(r'_\1', value).upper() + + +def to_lower_case(value: str) -> str: + return snake_case.sub(r'_\1', value).lower() + + +class QAPIGenRs(QAPIGen): + pass + + +class QAPISchemaRsVisitor(QAPISchemaVisitor): + + def __init__(self, prefix: str, what: str): + super().__init__() + self._prefix =3D prefix + self._what =3D what + self._gen =3D QAPIGenRs(self._prefix + self._what + '.rs') + self._main_module: Optional[str] =3D None + + def visit_module(self, name: Optional[str]) -> None: + if name is None: + return + if QAPISchemaModule.is_user_module(name): + if self._main_module is None: + self._main_module =3D name + + def write(self, output_dir: str) -> None: + self._gen.write(output_dir) + + pathname =3D os.path.join(output_dir, self._gen.fname) + try: + subprocess.check_call(['rustfmt', pathname]) + except FileNotFoundError: + pass diff --git a/scripts/qapi/rs_types.py b/scripts/qapi/rs_types.py new file mode 100644 index 00000000000..436adcf5be6 --- /dev/null +++ b/scripts/qapi/rs_types.py @@ -0,0 +1,365 @@ +# 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 +""" + +from typing import List, Optional, Set + +from .common import mcgen +from .rs import ( + QAPISchemaRsVisitor, + rs_name, + rs_type, + to_camel_case, + to_lower_case, + to_upper_case, +) +from .schema import ( + QAPISchema, + QAPISchemaAlternateType, + QAPISchemaArrayType, + 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=3Drs_type(variants.tag_member.type.c_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(to_upper_case(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), False) + if type_name =3D=3D 'q_empty': + ret +=3D mcgen(''' + %(cfg)s + %(var_name)s, +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name) + else: + c_type =3D var.type.c_unboxed_type() + if c_type.endswith('_wrapper'): + c_type =3D c_type[6:-8] # remove q_obj*-wrapper + ret +=3D mcgen(''' + %(cfg)s + %(var_name)s(%(rs_type)s), +''', + cfg=3Dvar.ifcond.rsgen(), + var_name=3Dvar_name, + rs_type=3Drs_type(c_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(to_lower_case(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) + if isinstance(memb, QAPISchemaArrayType): + return has_recursive_type(memb.element_type, name, visited) + return False + + +def gen_struct_members(members: List[QAPISchemaObjectTypeMember], + name: str) -> str: + ret =3D '' + for memb in members: + is_recursive =3D has_recursive_type(memb.type, name, set()) + typ =3D rs_type(memb.type.c_type(), '', + optional=3Dmemb.optional, box=3Dis_recursive) + ret +=3D mcgen(''' + %(cfg)s + pub %(rs_name)s: %(rs_type)s, +''', + cfg=3Dmemb.ifcond.rsgen(), + rs_type=3Dtyp, + rs_name=3Drs_name(to_lower_case(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: + # append automatically generated _max value + enum_members =3D members + [QAPISchemaEnumMember('_MAX', None)] + + ret =3D mcgen(''' + +%(cfg)s +#[repr(u32)] +#[derive(Copy, Clone, Debug, PartialEq, common::TryInto)] +pub enum %(rs_name)s { +''', + cfg=3Difcond.rsgen(), + rs_name=3Drs_name(name)) + + for member in enum_members: + ret +=3D mcgen(''' + %(cfg)s + %(c_enum)s, +''', + cfg=3Dmember.ifcond.rsgen(), + c_enum=3Drs_name(to_upper_case(member.name))) + # picked the first, since that's what malloc0 does + default =3D rs_name(to_upper_case(enum_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 + is_recursive =3D has_recursive_type(var.type, name, set()) + ret +=3D mcgen(''' + %(cfg)s + %(mem_name)s(%(rs_type)s), +''', + cfg=3Dvar.ifcond.rsgen(), + rs_type=3Drs_type(var.type.c_unboxed_type(), '', + box=3Dis_recursive), + mem_name=3Drs_name(to_camel_case(var.name))) + + ret +=3D mcgen(''' +} +''') + return ret + + +class QAPISchemaGenRsTypeVisitor(QAPISchemaRsVisitor): + + def __init__(self, prefix: str) -> None: + super().__init__(prefix, 'qapi-types') + + def visit_begin(self, schema: QAPISchema) -> None: + # don't visit the empty type + objects_seen.add(schema.the_empty_object_type.name) + self._gen.preamble_add( + mcgen(''' +// @generated by qapi-gen, DO NOT EDIT + +#![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: + if 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: + 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, + builtins: bool) -> None: + # pylint: disable=3Dunused-argument + # TODO: builtins? + vis =3D QAPISchemaGenRsTypeVisitor(prefix) + schema.visit(vis) + vis.write(output_dir) --=20 2.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109460; cv=none; d=zohomail.com; s=zohoarc; b=Vc4/bw64eBUzvrvwQwMzPtnAw8b5eUXhpE8jbpQjZ4O0+uxKNEo7M7xgsFn+4ZrH9YPmW9PNN5rc0algachvzgJGi2cuaSHMfD4cCUAzLKZgJj/6tqPt5VrDgvyPaSPsRrjrJfayn3OHfXwQCy7W9DC7LND//WLXPMKyzhyK9AU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109460; 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=vpOG7lTDLWHSKWSO4AEYXx9n/+p7b8N9rehUHPIdpTs=; b=kMndbb0cPGFS4j/wXIz/EsLZMa9m6yS+WMSPCTGPK5QyKOVy/zYvndRcUWISAJD1BkSGfM4fGXCW7eWnOGqukubqlgL+sdM+l/oo13ZiPUoMFAtDjYZMxXMzJTEGNahTrbSU0NLVz/5VN2hEXV/wCvS8gIEH7nl14NNFI2h8F30= 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 1760109460841521.8784494888613; Fri, 10 Oct 2025 08:17:40 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7EmW-0000fj-R8; Fri, 10 Oct 2025 11:11:44 -0400 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 1v7EmI-0000a1-V3 for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:33 -0400 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 1v7Elh-0004CA-Re for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:29 -0400 Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-375-bFJzWbDTM86pdD_3eJLcZQ-1; Fri, 10 Oct 2025 11:10:47 -0400 Received: by mail-ej1-f70.google.com with SMTP id a640c23a62f3a-b4ea3f1c12eso290765966b.0 for ; Fri, 10 Oct 2025 08:10:47 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d5cad86csm245763566b.17.2025.10.10.08.10.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109048; 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=vpOG7lTDLWHSKWSO4AEYXx9n/+p7b8N9rehUHPIdpTs=; b=Y/3YwBgwql5o60FSREk77c+T6mQe3s2WKa9BEUJbnJwV5VwVUz38Ta3MA0EyJYATnUifWs m+J9DIIUaZdHWWcdfOX5nLlR8wn5GLRFFmozLY1HaFzI1NOUikAeN2hX9EwvJIcPXeLl4m d6Npz81tOtelobtgfQk3hIVcjHf4yug= X-MC-Unique: bFJzWbDTM86pdD_3eJLcZQ-1 X-Mimecast-MFC-AGG-ID: bFJzWbDTM86pdD_3eJLcZQ_1760109046 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109045; x=1760713845; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vpOG7lTDLWHSKWSO4AEYXx9n/+p7b8N9rehUHPIdpTs=; b=cFXcl/Qz25hF/Vp8q3SyHU3mXIo2HRUAmuyFmD5K4BzjNqIR4q/0V2TnymAmoObdJc ijKZCfv7G5g0yusUS7rrp7nR1+6+bVfM+SjnFu29im/MIRg8xxJ2m1aLywWfF1EVoYw0 Eixh+gLi5OI9wixZe9wJm4/CcsXvNtNTbTiJ03oorll+0C1v9StYH2yMN5IO4jk9gQeR W1f43il9+UVqhgfLY5jKUWktGxZ+aZXmHaR6uBGys6JVRj50yKcnZk+4FMfN0i8GXyuk OFhKN75bRZzQ7Vctber/mibIhXQfS1rhpZTVDN6a/7UppvE1f2eN5ziSSx0g+m6EfGlY lNVA== X-Gm-Message-State: AOJu0YxSyUcI2Rv9nTCK0J36y43xzXM4fQWIVTmy/BXv4kOVIwHpensd PvLGvzNmKatNKhEKIT00unG6bhWfZ/BumuVXECnMQ3/m1tgRdeJ08OQQSWgZxVwxS+vHdfXXjY3 BsR3lkA9DURJzhIxkv1gt8YhVBiYjLtszbM7PDRHGFxjdPEF4AYG/LsTW+NKTzMCXCKCMqBhJBF 9lf2pf/x5wU5YjHgsRhMjpcdgL16rCOnWWlQasFvlx X-Gm-Gg: ASbGncsNoyWq+ZjNGV6WFU3pucv6WVnhnhY/Hjkm+QdfBo/ilkwg1CzymVQgT8MiiZm GzCCYPBD1b6XJdbwD+C4LUVtwRvLiPlDz+AhBnbRjMy3PcHemXA4BYh+0+Xmzpp3zB6cV7hBGQC HVj8m81TSZ3OaLEC7CQvG8EED9wFgulbB7Gcqe43O7MYgx782XRRV8H9D98BAQ/m7Gj02R27PFj dbXZqKaEM+ykEHKvkLw7rCypitEG6ttWmvCvnlflLE92uwJcbRAYerx8uwasSs2y8ZvUoaFsGDd T0FTllLSGKHZMJCtXgdKaJISsg96Mpqca/X71xUMFi4yClc8rvpQsEb8+w/UdLhP9a1kcIFgPc5 IFRI+j7yL9wcPwaEC1Fbr/jHxQK8uI7kdE1Wqy5UxCNEG X-Received: by 2002:a17:907:9710:b0:b2e:34f1:9dbf with SMTP id a640c23a62f3a-b50aa48d314mr1255300366b.1.1760109045078; Fri, 10 Oct 2025 08:10:45 -0700 (PDT) X-Google-Smtp-Source: AGHT+IF/QfEEMSOOvhANB/Xy1Xu0rDgd9YPCAmSSq26VUi3QhqkM5jdYj96nXAU/kTVoZdddJ0dJkw== X-Received: by 2002:a17:907:9710:b0:b2e:34f1:9dbf with SMTP id a640c23a62f3a-b50aa48d314mr1255296766b.1.1760109044642; Fri, 10 Oct 2025 08:10:44 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 15/19] scripts/qapi: add serde attributes Date: Fri, 10 Oct 2025 17:10:00 +0200 Message-ID: <20251010151006.791038-16-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109462167154100 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 | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/scripts/qapi/rs_types.py b/scripts/qapi/rs_types.py index 436adcf5be6..f53b419dc2f 100644 --- a/scripts/qapi/rs_types.py +++ b/scripts/qapi/rs_types.py @@ -31,6 +31,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, @@ -77,11 +78,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 @@ -89,21 +92,25 @@ 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: c_type =3D var.type.c_unboxed_type() if c_type.endswith('_wrapper'): c_type =3D c_type[6:-8] # remove q_obj*-wrapper 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=3Drs_type(c_type, '')) + rs_type=3Drs_type(c_type, ''), + rename=3Dvar.name) =20 ret +=3D mcgen(''' } @@ -159,9 +166,11 @@ def gen_struct_members(members: List[QAPISchemaObjectT= ypeMember], optional=3Dmemb.optional, box=3Dis_recursive) 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(to_lower_case(memb.name))) return ret @@ -182,17 +191,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(): @@ -214,6 +229,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(''' @@ -232,7 +248,8 @@ def gen_rs_enum(name: str, =20 %(cfg)s #[repr(u32)] -#[derive(Copy, Clone, Debug, PartialEq, common::TryInto)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, + common::TryInto)] pub enum %(rs_name)s { ''', cfg=3Difcond.rsgen(), @@ -241,10 +258,12 @@ def gen_rs_enum(name: str, for member in enum_members: ret +=3D mcgen(''' %(cfg)s + #[serde(rename =3D "%(member_name)s")] %(c_enum)s, ''', cfg=3Dmember.ifcond.rsgen(), - c_enum=3Drs_name(to_upper_case(member.name))) + c_enum=3Drs_name(to_upper_case(member.name)), + member_name=3Dmember.name) # picked the first, since that's what malloc0 does default =3D rs_name(to_upper_case(enum_members[0].name)) ret +=3D mcgen(''' @@ -275,7 +294,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(), @@ -323,6 +343,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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109519; cv=none; d=zohomail.com; s=zohoarc; b=g/GFy77qTVWn+vkqrr1csQyfR4kBXSvKpBvn2Gh1+6+1ulXRAcQ9kqLjX2VPTFNPhpW84waaO5A+dAd6KHFauL8ZQ0d0VP6xA1TDMBNNKdL3u5CPFvUIU1JDJnM97Ah5Rk4fPqkGfxuftt8Mt+PDmZR+yZH1TjgQ62PA2vsgM24= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109519; 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=UBV2+VUG7I4eiMBuAImYuaKckuDbq+FYw4y9UTZE5Xs=; b=AEPB8wIx6f9cA1ZUeNipjNUuy8hxMYhTRzyBi6d9D1mMygSwM+TMghMMq6ykaFEkCHUOot7xC+chy0IBejwOCSLSd9/SoRwsG3J7jG3djFKdkswg9iC9yyEMZXtCTubxbhMYHz0yZ3oL3b2YfzYKQrOM1PYAYuMrBNw50DncErY= 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 1760109519225303.91520765058124; Fri, 10 Oct 2025 08:18:39 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7EmZ-0000iE-TK; Fri, 10 Oct 2025 11:11:48 -0400 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 1v7EmU-0000dP-T2 for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:43 -0400 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 1v7Elj-0004C0-Kc for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:33 -0400 Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-264-WAvaDppNM4u1UgMQ5iwt4A-1; Fri, 10 Oct 2025 11:10:49 -0400 Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-b3d21b18e8fso198615666b.2 for ; Fri, 10 Oct 2025 08:10:49 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d900e2bcsm249020466b.64.2025.10.10.08.10.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109051; 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=UBV2+VUG7I4eiMBuAImYuaKckuDbq+FYw4y9UTZE5Xs=; b=NPfp8pmt6oCYgaRwOK9OlYVm9O+aTqnqhQlfjY4hpQ/IsuAF7kVl7VcQT/UKiwF95NN8YS wgZrkBdEcgVsvkD0Xek0Lyb/m4lP93EkVyznxZL09tjAdu4sQ81XRSBHr4GKm4AeN91Jcm Rj7b5GoVo9e0+ViTV8cSbSl4FDvq1e4= X-MC-Unique: WAvaDppNM4u1UgMQ5iwt4A-1 X-Mimecast-MFC-AGG-ID: WAvaDppNM4u1UgMQ5iwt4A_1760109049 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109048; x=1760713848; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UBV2+VUG7I4eiMBuAImYuaKckuDbq+FYw4y9UTZE5Xs=; b=BSb5P/uV7Y0zT2S37EzqbLjg+8vmqBxR/SZJY0G1j/S6eVaE6ADkwuI+0KabhZ1YMU trOxkAItHMo/bxs2u8zF0cE99a9KkKA4HFturfS1W50zCpul6RvPQlP+qF627Pp8x0GH pANb331lz3/XfVYUGeOEkKowTeaRSwaVGw1bAQUesmSexN7rt7KOsvTLzm6/yo92q6uj toKP+5zTI3vJhbRMqVgr1AflOO9TA+ca9CXaAcS5BKFwHHVYRc+sH14lfaVeKYaLQp7V clRgIfd/Z+tUGzlv64e8qv2oPr7KnGQOx8MUJeUhayYN/d8fhXAHswNwSOkfxHLoNcuy f20A== X-Gm-Message-State: AOJu0YyhsVQQSatFjqnoSJCyYyiIuybRtnKnLIc9wXcBzp0CD/FD76Hb RpbgSquygvKB2YE/eM4iVkXladbQz4qr49LVArrZMd+F2Monhbm4GDLRSR741logLQQ6SLy4oSV /qDMKKsFPJmjQRKPa4FnyxlmFo1NtaYpmzr8iVaXMhmXkF9cz5TWm8AZK0cMSck8ued6fx7Prak 7Yt7Nxd7wEeknWZpA8QV6oovbKdQ5fK5PGdMp9RsDU X-Gm-Gg: ASbGncsVJynnzYr3qm1xHUFplj3J2n3v3YYYNan4VukDUsYiWMbJTefjWfqXX1W9x4L ySBjibpn3K3q83yXkW7iU/BmmsXg6QmYDqgH41BPNeGwRgoRfDlGyQEzV01mURXi2zF6p7sDkzD 7Fugpe/D4ztwgCHwzcPSl1ZeBAusGB8EYVrbr0OLfBVq3AuWG4TKJFB1/jrYEix48VngFR3AACT ZfScFH63RWBoM79OrsQx8/0VgpjX2bFq7XSnwREARlNLt9cfl8uUKqf+t27RZlG5TZumKMQSIeJ XBNmGumQFFWswIdAxs17hnzcKae5kGkhXxHQa4qhkr3wlNQR1dRIJuEkQlOdcTPYDXHI3BgmFAC lNMP+CBssvGoKvoHsYC04Y2ZpX3nlPsJ0QX2bFkEx4u7X X-Received: by 2002:a17:906:c104:b0:b46:3dab:7dac with SMTP id a640c23a62f3a-b50ac6d274amr1159286066b.45.1760109048153; Fri, 10 Oct 2025 08:10:48 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGQJLLWmczUilmoE67+BLTRUznp0F4fknCSl/G2nWxPnjBPjf/d+Xi1YW8KApL96Vz/lh0K0Q== X-Received: by 2002:a17:906:c104:b0:b46:3dab:7dac with SMTP id a640c23a62f3a-b50ac6d274amr1159283066b.45.1760109047675; Fri, 10 Oct 2025 08:10:47 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 16/19] scripts/qapi: strip trailing whitespaces Date: Fri, 10 Oct 2025 17:10:01 +0200 Message-ID: <20251010151006.791038-17-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109520831154100 From: Marc-Andr=C3=A9 Lureau This help workaround a rustfmt issue. Signed-off-by: Marc-Andr=C3=A9 Lureau Link: https://lore.kernel.org/r/20210907121943.3498701-16-marcandre.lureau@= redhat.com Signed-off-by: Paolo Bonzini --- scripts/qapi/gen.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 0c9b8db3b02..c9721545ea7 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -58,7 +58,11 @@ def add(self, text: str) -> None: self._body +=3D text =20 def get_content(self) -> str: - return self._top() + self._preamble + self._body + self._bottom() + content =3D self._top() + self._preamble + self._body + self._bott= om() + # delete trailing white-spaces (working around + # https://github.com/rust-lang/rustfmt/issues/4248) + content =3D re.sub(r'\s+$', '\n', content, 0, re.M) + return content =20 def _top(self) -> str: # pylint: disable=3Dno-self-use --=20 2.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109275; cv=none; d=zohomail.com; s=zohoarc; b=UAMFzl4p0SMpfmjgtt8EdARk4QA3Wbux2DlXwGzYOmixEg+qvd7Jv+2AF9ELVAp+rwSWfR9gc9mDnhOC9oWhgYhw+qhyzTqgCED7+Ep/WZsdfsU8PkT7Q13oagekwMHdhId2kRhIGwA02X1SA47T6U3lu8eURuhpte/Anp+eUKk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109275; 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=qLAzjRg81thfU2aBxeIBMEWinBk0XD4qCND6oGCuTHg=; b=Rr4LONWeVEBvjzPxWk0zevpclni/zQrzPdm5uhC4L5/nNShnxVH5ZKawRUib2PokZpNx/PcusqZebSnxT8r5ygDzIqiUvKU8sM24+l1ktsnNxsISwBeqgpwxSUW4TUyG3iEix4pJm0KZZB79+An5DvdNeGmWNRPxfjShCuRBI8c= 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 1760109275393579.5370882005467; Fri, 10 Oct 2025 08:14:35 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7EmZ-0000hG-3g; Fri, 10 Oct 2025 11:11:47 -0400 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 1v7EmX-0000g6-S7 for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:45 -0400 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 1v7Ell-0004Cg-Ep for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:44 -0400 Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-494-DzONFOBAOj6rcHdnEwOWVg-1; Fri, 10 Oct 2025 11:10:52 -0400 Received: by mail-ej1-f70.google.com with SMTP id a640c23a62f3a-b4af88aba57so205999066b.3 for ; Fri, 10 Oct 2025 08:10:52 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d8c12978sm253961966b.44.2025.10.10.08.10.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109053; 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=qLAzjRg81thfU2aBxeIBMEWinBk0XD4qCND6oGCuTHg=; b=FBKKWJARpYzarLA7ixMWQoqafmis43RUqd1FCdhutJyndXXQ7+GAssfTZfGk5Rk2GbsZy+ 5iWvkv7ct1V21XkNWOHjcblawx3WNK+Oq3vVegrChDCY67ksjxb8fm8FVj/JiRWBXT9cwp azm++0cR/pelsrIyz8LHfuIuuvf4MUU= X-MC-Unique: DzONFOBAOj6rcHdnEwOWVg-1 X-Mimecast-MFC-AGG-ID: DzONFOBAOj6rcHdnEwOWVg_1760109051 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109051; x=1760713851; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=qLAzjRg81thfU2aBxeIBMEWinBk0XD4qCND6oGCuTHg=; b=V/dsYAL2dxmsTYELjBofOuIfHrGblHiNvuiUd9wyrmCMduVsLzVDYB786SKQiumPaB cFsgv7g4zDw36BBtXgVKBzE9I66CE/hEqyKTiSleSBbSq8SYrS8T4eI6FYT/ezYLa4i7 NE5mAFKaEIO0FPik1GiOrMd61n7GDdy4h0eZ57l1AiutAOc+mgt2YjMh83USunWImGy8 UboP9NM99v6ELsFMu6ccDlZWIbyvruygfCLmyUi7rh4ecO10KcMImiLUkxjD2xmr+DI1 efa34tVpAW6UNtkLmJ3pNS8KexU/7/pT1LayZMaQuQVzCAtkrWUkatVSEg+mfrMvX608 fDbA== X-Gm-Message-State: AOJu0YxJCI8qvhbpV9ecGRtQEhoT19dml6GXMVpdaDR4m9oLfGRNpWJO k0jPHyP8wFQUskqZCH1kyTEhSBEqD3vYeXrdZ6dtz41CzM14IBrW97dlDGn4wxB1Mu3ElbIXnXp cJL4Y81tzlpg5y6FrG/p4mrf+QQ9j+QwU29VE1/kM09xdrbf9jxydjZ1THTIWPciOyMb28itAEO S6ibDtfrIMh6/XgWzfSCdA1QcXSDBP9wWYrXxgOvqY X-Gm-Gg: ASbGnctsbH9BwyJWMYthkOaJqFH5AloQHjN0LVr1RaQAhUC6RN0PxCiTdP76CDTC0Zq d3P5i4MACilcK06XUeet2wUSeGBpVvroTpHUSKsNGTZV1CqbuAjXhH8uSRiQcqrtSPTXvPUVel0 YppNld+OYcgKY8Ek0TmyFBKCA02EUVBhMINPNZgrHfB4tvjwIB33lIXqdc2z4A0sumADBJ2FonS MbS9vqJkapWKLPcGmBeIxFusJa6NBrKpeB498Yan1nWkz2iAAzqoNGZHnEkbNrrVdURAPMVn/fv 8F+AsySacd48eUD8HmBvuKmuH+NO1VFrK8yHfQO5meyJI6D8tNchg+hXySNI9Iow9l6ZjD3B4/l dLsxDJLntgG/UWsECwQXkDWSLvtK48LUHUmFqMjWS4s16 X-Received: by 2002:a17:906:c144:b0:b49:b3ca:52b4 with SMTP id a640c23a62f3a-b50aab9d64amr1227764766b.23.1760109050848; Fri, 10 Oct 2025 08:10:50 -0700 (PDT) X-Google-Smtp-Source: AGHT+IG5BpqB1n7IwGrSa3hxTlnzREuidDabK7inECo3W37egXG6QpbbHe8MINppXAIxIGHcsa7Kpg== X-Received: by 2002:a17:906:c144:b0:b49:b3ca:52b4 with SMTP id a640c23a62f3a-b50aab9d64amr1227761266b.23.1760109050433; Fri, 10 Oct 2025 08:10:50 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 17/19] scripts/rustc_args: add --no-strict-cfg Date: Fri, 10 Oct 2025 17:10:02 +0200 Message-ID: <20251010151006.791038-18-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, T_SPF_TEMPERROR=0.01 autolearn=no 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: 1760109277877154100 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 63b0748e0d3..c70b95b8bed 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -116,7 +116,7 @@ def generate_lint_flags(cargo_toml: CargoTOML, strict_l= ints: bool) -> Iterable[s 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: @@ -125,8 +125,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") @@ -194,6 +195,13 @@ def main() -> None: help=3D"apply stricter checks (for nightly Rust)", default=3DFalse, ) + 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) @@ -224,7 +232,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.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109435; cv=none; d=zohomail.com; s=zohoarc; b=OhCR/wdXWlVXsIKDm5syFAY4vCSrSCzKqt7d42BlCoMP6HE/vLC5ork5wsiEpeff9hoxNbVTG1QmZN+CYV9zSGfN4zseaJ/o0c8hgK4urq9lJfCBE0uRBIZS6Th4RXw7a4P6sP54P/7m8uHcRG6m+VTTqrq+qMOCF8p4YKbvoww= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109435; 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=LpWZD6bl9mxamrbUvrIDgAoUaMeQiRW3KeWUfjgV+yg=; b=W3fy2qL2Vlmg2RCVnFGdyTOn7TAiGSoFczrbeTG0K8yn9fruD38nVHhs5V21F57unByKf7FujoLgUFKotHT4Qk6UidDjz5L3RwW/B7PxW/CnNI3MnP97wvLMkCDWG8nhZiu4KpQ2nPCWtODEt2LGuP03Rjilj7QzPCDEqDWl+kM= 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 1760109435016253.11799464998273; Fri, 10 Oct 2025 08:17:15 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7EmZ-0000hH-6W; Fri, 10 Oct 2025 11:11:47 -0400 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 1v7EmW-0000fm-Qj for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:44 -0400 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 1v7Eln-0004Dd-Gq for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:43 -0400 Received: from mail-ed1-f69.google.com (mail-ed1-f69.google.com [209.85.208.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-554-FrjfzzswPhGfupaSXTTyPg-1; Fri, 10 Oct 2025 11:10:55 -0400 Received: by mail-ed1-f69.google.com with SMTP id 4fb4d7f45d1cf-639e4930021so1834302a12.1 for ; Fri, 10 Oct 2025 08:10:55 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-63a5c3218a8sm2740690a12.40.2025.10.10.08.10.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109056; 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=LpWZD6bl9mxamrbUvrIDgAoUaMeQiRW3KeWUfjgV+yg=; b=T6lrXMTUSogylmqdD/b/Ny2f8sggqxiLLw36v+FHbJSu57uluzRjRSlZW5QbOGKUlY/hLz rL1oFbAkoIX3MFve5AFnAOgcYPHyNAT+EwrlMYHn+Kd7JN7y0iuI//gOIoB3fz82bOl/nN mh8J96Q68CRMmukcSzakWqOy8v01xeE= X-MC-Unique: FrjfzzswPhGfupaSXTTyPg-1 X-Mimecast-MFC-AGG-ID: FrjfzzswPhGfupaSXTTyPg_1760109054 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109053; x=1760713853; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LpWZD6bl9mxamrbUvrIDgAoUaMeQiRW3KeWUfjgV+yg=; b=Ro+Ijm21ZlYp+46QWYaPrco9Gd6db+HTXoNeoRU3x4Yhmi7LdEKO3QKWlajcteesBK Wexk/QOALzgYkqa5VYZfKi0MPA0KnR9yAoG9t+nt76UFsgL8AkIQ9KFh7Jhqkdp4zXH5 wCmruCpzyHx5SaXWJDRFUIr5Jyp8Gm202i3OdnuIiV3yGjpnp0JdKWmiONHnSmTRXmsQ ySKIbLmIoG2XSsgQgaTkvA8lLpyUiVWtv+T9ZcQAk/muYixBSp2fy9ITa5aK3MHyM9og WVcCw3tic6FSNsEZlZ8vn/WIgTbSjMYSSQRupVMJ27XEU3bfJEdLdTM+ywSM6b7fQtWW C1OQ== X-Gm-Message-State: AOJu0Yzp/DkCQ0fS+hhZK3A+Vw5KA1fpciXV/2evLyKW6J4WHTQ0829C Jgpf8ZM5bOoAMqUr/PF3BuJbdfTZL0CuE9V24ws+IKgwACIZUbHdLOmU2Fsw+Tl8F4dhkiw9DbO inpm9Yj3VwiXokmqRWiZ8VBNixoq+yzwdkZT8trHB0LRkwo/S2XQWbfcSn6PVwe6eJWUy2WlmUP 6u5MHVjxcR4yt2wqTAbw+ftqghfBnJ5yJlE7v2t0Wt X-Gm-Gg: ASbGncty7udyPbRWIm5PhZKK5bYdE5SodAhRa8BN0kveNVqOnD53I5zIGDpQpH4mAyj KbFCbaHsGoUGXGP48XMoaK1LGCOLbV2XTzmVnSn3c2FWT8B2P2r/qR4GTiWisgvLLJwXJgNDESP TE/vgKSAltfjswsTNnDnJtq5D3UUmYVF4XGcGjVpiFgWdtSVCqTJBIjdyDpUM6038xzBO5Ei3tJ 3bgpRblsd2l4qUDmdaiQz9EEnF8+n/xo4B7GfWmwq8iYYMLUc3AQFCuGqRFKQFjpEk+N/APprWH wT5MvhdRegtRCculsqMyCN0eV9nFAJUa35DFZn2ih59DTKDRGaKxtmBtYGG8brp6YWSzWgCyDZf j3gKL62y6yXVzci/wWAlJ1r8PLrYcbEVK891Igbd7jDqe X-Received: by 2002:a05:6402:350c:b0:628:5b8c:64b7 with SMTP id 4fb4d7f45d1cf-639d5b6485emr11750089a12.6.1760109053536; Fri, 10 Oct 2025 08:10:53 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFIGbPCD3SRGp+vxEHJt2mklDMdVAiicewi0AqChjNxpxNbRQ0Kq2pNXDtlBryYHAhTrP/oGg== X-Received: by 2002:a05:6402:350c:b0:628:5b8c:64b7 with SMTP id 4fb4d7f45d1cf-639d5b6485emr11750053a12.6.1760109053094; Fri, 10 Oct 2025 08:10:53 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 18/19] rust/util: build QAPI types Date: Fri, 10 Oct 2025 17:10:03 +0200 Message-ID: <20251010151006.791038-19-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_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: 1760109437834154100 From: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini --- qapi/meson.build | 6 ++++++ rust/Cargo.lock | 1 + rust/util/Cargo.toml | 1 + rust/util/meson.build | 16 ++++++++++++++++ 4 files changed, 24 insertions(+) diff --git a/qapi/meson.build b/qapi/meson.build index a46269b5a0c..888a9adf7ff 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -130,3 +130,9 @@ foreach output : qapi_outputs util_ss.add(qapi_files[i]) i =3D i + 1 endforeach + +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 7c9f85d5728..1074c926b71 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -446,6 +446,7 @@ dependencies =3D [ "glib-sys", "libc", "serde", + "serde_derive", ] =20 [[package]] 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 aff14a41589..fe869ab485e 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -64,3 +64,19 @@ 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', + dependencies: [common_rs, util_rs, serde_rs], +) + +qapi_rs =3D declare_dependency(link_with: [_qapi_rs]) --=20 2.51.0 From nobody Fri Nov 14 18:19:13 2025 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=1760109400; cv=none; d=zohomail.com; s=zohoarc; b=AYYDBroX3tXWC37aJW5JqcDYeNISW3hjwGRONcEVWz/X5XeVdg3JfbiCdegq9tGWc4eG9rvcaNVXUh8G0NwukcbmEzbAkRTWnqbASlumSod743TF2EZobvvbuiGJtmfdlefS/e1RIZOgvLAG9emWgEtHaECaZMCx1y9UuFWmaTM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1760109400; 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=F1yOkbzuojTrlKk+buHl+5dXDqrmMnV6cjefuiyhlyQ=; b=A0cx5Fbw9FVwG+SKoRCRmfSPaMAg6bAE5/HQ6d8PngAjU2geqTQ5tmy3tjdTJ2au5h5rTGvUFIw83EDPsQlU34HNC9/uByiZ0X83F69Qy/Ymp4b3wPBHURHz9zjjie0UHufd/ERsvCCRPS5E3E17pGe+z0oCb0sHEC/zI+AKh+o= 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 1760109400415267.8657798063157; Fri, 10 Oct 2025 08:16:40 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1v7Emm-0000o5-6H; Fri, 10 Oct 2025 11:12:01 -0400 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 1v7Emj-0000md-5W for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:57 -0400 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 1v7Els-0004En-V3 for qemu-devel@nongnu.org; Fri, 10 Oct 2025 11:11:54 -0400 Received: from mail-ej1-f72.google.com (mail-ej1-f72.google.com [209.85.218.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-55-3jDyQ7E7PwaQoV0Zd-VeEg-1; Fri, 10 Oct 2025 11:11:00 -0400 Received: by mail-ej1-f72.google.com with SMTP id a640c23a62f3a-b4626b2d07bso270001066b.1 for ; Fri, 10 Oct 2025 08:10:59 -0700 (PDT) Received: from [192.168.10.48] ([151.49.231.162]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b55d95257c8sm259085866b.77.2025.10.10.08.10.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Oct 2025 08:10:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1760109061; 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=F1yOkbzuojTrlKk+buHl+5dXDqrmMnV6cjefuiyhlyQ=; b=a3y+omdhqycTwHx9EmsAmYpKG5rv1JLlHkjeK+oTdO9GE1L4vsoemt7fb2H9H9zSNhcWuO Cr3fPNaTLVOmdjtcl1Gb2HHloNWVwtd18M6Ttg9mUCVRaE6PixYYFMhBlze729NVVRZ09P 14+u0bUcyoEf4Ug+SyB4MiRNldVujSo= X-MC-Unique: 3jDyQ7E7PwaQoV0Zd-VeEg-1 X-Mimecast-MFC-AGG-ID: 3jDyQ7E7PwaQoV0Zd-VeEg_1760109059 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760109058; x=1760713858; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=F1yOkbzuojTrlKk+buHl+5dXDqrmMnV6cjefuiyhlyQ=; b=nCh8QhKlLPdMmDAOE3+xYepzPTbZGahgK/XYc/OX9LllLjWHqFa0PFFFnOOzFiL9Q1 L6uL9J/ANl9GMvfJtYO/mcCi3ECVkozn6WbCLuI/HM9kaHE2LTJrz7dRHbvQdc3M5W4g +MPb6jbPyIRYV584soxVeUKjINqpfpbXeljImVK4M9rhzTc1rMh+TQJxncptQHSzlEZs 7PONjPFITW3L4FaZYSgSzKWv2d74ODUJMJkW/RKtBT9oRhBol9vRN3E2O9dWJyDK/yIN m20tn4Tm5V4apWL0v6rfg4i56c52WEe351ocScyFVnQ5s6u5hy0V2RGcTh9jcMF3HCBQ +DSg== X-Gm-Message-State: AOJu0YzpNSkzCBkZVP6HR6q1d0ZBFXm0wnwYSSGDP1hf7aJ+FDf04fYr OUGTOe4CWKKkBHuOB5gY3nX3NYtgt5X3xEfMAXW4nF3H+Fhfq9xP2dzYDXwVWRUcWci4UA85kzb 3K/nkQj2L0XqPmc0KVk+3ZARrmCYMff03IpVrGAIDc+Q0+QvFSeoiaDgLxV+vQFxSQqgoIRsEuR UriKaAyX1IDxXnzNzfmFDQF1zbx3AZA7Njeh3UaTXP X-Gm-Gg: ASbGncuFmYv48bt5ou6Ct7CJTTDyQ5DKw7wDZ5Yi1ORz2Pg6JX928cyXIpxp1CwiSpH Ej9JwWVL+Lvf2a9LtVzGizOh8tm+MWY3jsN1Mxx5Ly9ajxXKvczmo8M0XTvm/zFOWKhAAV1qDy2 vKHRtDZ+b5f6kZiIjGURaMAawFDpSCYUYPhe2X/NbKrcL9elmoKYbBTZW1W4wMDmupIqsoyCW8+ 2YwMaK/zeCrgfF6Gc9oLcLevVEpmHpZk8mdx0uu0D2GeK/TREoEP5LNetxCMPxBopEAVF2gvGPQ ykRweW9qzADusTPYgUGP0C49NSx2WMPZDA54h5W4waYNBoh5M1R2snok11ZIN0nHe30cDHHO6Am RAsXZKmlby19chTwSCRiFz+MiJrqKSFhj0OPinN6+3e0B X-Received: by 2002:a17:906:6a14:b0:afe:764d:6b22 with SMTP id a640c23a62f3a-b50aa48c4cfmr1202296666b.9.1760109058013; Fri, 10 Oct 2025 08:10:58 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFVVmh/0bAHP7hdXC/LRNn8SnraAOcqmHRskIba6Qi+nb/2xvuxezyMS07uKWG40QALxvHtfg== X-Received: by 2002:a17:906:6a14:b0:afe:764d:6b22 with SMTP id a640c23a62f3a-b50aa48c4cfmr1202276966b.9.1760109055296; Fri, 10 Oct 2025 08:10:55 -0700 (PDT) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, marcandre.lureau@redhat.com, qemu-rust@nongnu.org Subject: [PATCH 19/19] rust/tests: QAPI integration tests Date: Fri, 10 Oct 2025 17:10:04 +0200 Message-ID: <20251010151006.791038-20-pbonzini@redhat.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251010151006.791038-1-pbonzini@redhat.com> References: <20251010151006.791038-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 client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -24 X-Spam_score: -2.5 X-Spam_bar: -- X-Spam_report: (-2.5 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.441, 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_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, T_SPF_TEMPERROR=0.01 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: 1760109401414154100 From: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Paolo Bonzini --- rust/tests/meson.build | 25 +- rust/tests/tests/integration.rs | 2 + rust/tests/tests/qapi.rs | 444 ++++++++++++++++++++++++++++++++ 3 files changed, 468 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 00688c66fb1..663c1e1aafc 100644 --- a/rust/tests/meson.build +++ b/rust/tests/meson.build @@ -1,11 +1,30 @@ +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'), + files( + 'tests/integration.rs', + 'tests/vmstate_tests.rs', + 'tests/qapi.rs', + ), override_options: ['rust_std=3D2021', 'build.rust_std=3D2021'], - rust_args: ['--test'], + 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.51.0