From nobody Sat Jan 31 20:40:34 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1767877927; cv=none; d=zohomail.com; s=zohoarc; b=Ck7g3dFSjYir7AMeP2OfpdsVfC9IaRgGFrkaLJaqqhIzyldSNWMl5SOS1VGPQ8RNdUUQiyKVFT1WdAtYmoOTDMiPG2RlBXyP+bu4olF4aKLknDV2QzJIyLGvLGjyH00yk1oGoLGGaqVEeAm1YNArBUW1AmUeF25RxY6FusUZOIQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1767877927; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=IXUBvQcaMsRwI/b+BszJt6nG9jA/4L5XWgG62HdNUNU=; b=Tmzmy1VdtAh37l6cIYc2KdgoQIrbTFSNpDcHGu+gCzUO+IwhXuRwbAV2q9+gWu1X6d2I4FHSXUE2PayJbErxXTQJj43+G68+uc9q2++satFmFOIqBSSuhcVPknjk5k6WmIABP6dd/OTD959REbTLkZtEnDlaFqUTa2heTG+0Dtc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1767877927246562.3046350785336; Thu, 8 Jan 2026 05:12:07 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vdpnF-0007bL-2k; Thu, 08 Jan 2026 08:11:13 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpnC-0007aI-LU for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:10 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vdpn9-0007zz-1T for qemu-devel@nongnu.org; Thu, 08 Jan 2026 08:11:10 -0500 Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-33-54cSPiGTMjmCCos_NXjlbg-1; Thu, 08 Jan 2026 08:11:05 -0500 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-430fe16b481so1698356f8f.3 for ; Thu, 08 Jan 2026 05:11:04 -0800 (PST) Received: from [192.168.10.48] ([151.61.26.160]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-432bd5ee870sm16250175f8f.36.2026.01.08.05.11.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 05:11:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767877866; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IXUBvQcaMsRwI/b+BszJt6nG9jA/4L5XWgG62HdNUNU=; b=bFFVQf6afoshEiar3hyvTrhGmGoCb333hrFrpYOuiHdkqh2capLzyRdqdOIjDJ4f3fHKYx jCTPUuZhJ4ID9l3T4aUEzOtUTHdr2HLIIMVu2gP4/rwqwz+1TjYCPEKaxZ6Kvhplszga0n L4Lsa41lQxgW5YCCnbS3QUiLA95+V4Q= X-MC-Unique: 54cSPiGTMjmCCos_NXjlbg-1 X-Mimecast-MFC-AGG-ID: 54cSPiGTMjmCCos_NXjlbg_1767877864 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1767877863; x=1768482663; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=IXUBvQcaMsRwI/b+BszJt6nG9jA/4L5XWgG62HdNUNU=; b=l3VKIsKPQmbLUcXFuvrYvnVM9e66gZcyT9irp7geYxTgKayjiJN/M8ILiyCW3zz3et sfGu/7jdfgbpbF/ID4htaTzm5YLugeg72AqW3z2NJnzFQ693wlyrkkx0Ggc9+zPFWtSE 59yUc6vmDoPIQKwgjoE0OG6GN1ra8NyJ+fF43D4elLlM8Pru3sDwX6PFSFZhJIMjzuQX ehMHdg3NNPb5XgjaN018ZywmDRRLwpy4hhjGKXwEs3nYHinxqnBETcDUILxBS3yL2b4L xv57xUgEjcRvBKlxo5N+9smvyDQ+NZ5/FEdaEf3MhlUccR3d+hPRXUs5Ccaw7X4IAkAw CjXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767877863; x=1768482663; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=IXUBvQcaMsRwI/b+BszJt6nG9jA/4L5XWgG62HdNUNU=; b=jeUxM7s93pOfPO1Id1Bnr07dqO+xMZe6XTze5KVhpR9Uo8gbsMTNe8Iw/enk5Xpyos CBXN9vr/UeNmkXfeyKqU1f4pjHdJ/BnnT+9fiIyh2KuhIWwNzHieXr4/aM7omnlT6Myd iTJlZ/yW/yv3Vskcu5CHC47ERK0e5S9p2YWA8UmjLQHYReifMCw4mW8xW8hLWtasctHA HVwHqo/Fj3J2VF1e6f9T1JiPlmPbSZvRXkhgZi80+PszPsiUnEjqF2PrR22Er8+vLX02 xDC2CLgGk4+F3x51sH/F7B4C9JKxEm4lCfVoMZawjGPHjqb8zp1FQiy+S7S9iJCJmT9s B1uw== X-Gm-Message-State: AOJu0YypZzSrCm8cW9jvHYkR4aLsNnEwexvk4ZJ2fZu55GhcqZFTGgoO seg8e0bpNwbgpV/oInsA9hqwkM+jllB8lvxpCSUun2My445PlkOyyCVW2F2SQawcXg9f81xbz3c GuqjD9R0R9OW17of50DZ4kd3icpY+p8RvYlnnDd7NdhdvW3cJynQerxlhKZmaDH86OSyY7ZX1LM ofolOxSW+acrWCgA5VBIsrQi90Y0Ipskz3XOiS0sbK X-Gm-Gg: AY/fxX7/qaJqItoWERJ7FEzQ1lhnpWSMkYj6ZWhP2Gp7mowOWO5hQesFbd8XGSHrn9a eiGx8CBySddCA2eG/PDfT1NW3LfN5NnpdznChvQcGXQglUXfyuVsbd/yhFkmZjLzgfbrAzY57VI 675yVWl7o1nmuZQoJUXH0a1/UwY12dflFxTTVrTHmtvMJ7G+Ip2P/I/sngaFncuRI4+HZwSKsaN IvXpl4bT7GWIaDVi9ufpV0ssg4qBPgOvoflZqDEgV2BrJ465/rz+XI1y8O6Cu/8orWu6eEHYDHt yYGhOc4OwfCQ8KjU9QRsjO/WPn/M6ycvEEdrP3O+js3zSwtbwB2wqbSvsPITsiNva7A1guWD0Sx E7UmbV+m1NtfrXpZSDS3sYjCdaXS/p6kC0k5H3dsTo7+yoGEKcR5GJJS3o3AzxwUp8aaxXTphJ2 vAFkFr15HXHgo6AA== X-Received: by 2002:a05:6000:240f:b0:431:864:d4a9 with SMTP id ffacd0b85a97d-432c3772035mr6376945f8f.8.1767877862939; Thu, 08 Jan 2026 05:11:02 -0800 (PST) X-Google-Smtp-Source: AGHT+IHMG9a3naAy2kMCjipFCfNO9dOnznFl+BkKm3U0MQO1tS54K1Cs53sXUMyf9MSr/updpapoLA== X-Received: by 2002:a05:6000:240f:b0:431:864:d4a9 with SMTP id ffacd0b85a97d-432c3772035mr6376911f8f.8.1767877862360; Thu, 08 Jan 2026 05:11:02 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: armbru@redhat.com, =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , qemu-rust@nongnu.org, Zhao Liu Subject: [PATCH v2 06/16] rust/qobject: add Deserializer (from_qobject) implementation Date: Thu, 8 Jan 2026 14:10:33 +0100 Message-ID: <20260108131043.490084-7-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260108131043.490084-1-pbonzini@redhat.com> References: <20260108131043.490084-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1767877930076158500 This allows creating any serializable data structure from QObject. The purpose of all the code is to typecheck each variant in the serde data model and check that it's one of the corresponding QObject data types. Co-authored-by: Marc-Andr=C3=A9 Lureau Signed-off-by: Marc-Andr=C3=A9 Lureau Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 1 + rust/util/meson.build | 1 + rust/util/src/qobject/deserializer.rs | 371 ++++++++++++++++++++++++++ rust/util/src/qobject/error.rs | 8 +- rust/util/src/qobject/mod.rs | 2 + 5 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 rust/util/src/qobject/deserializer.rs diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 79c26d9d165..c7f3a496a25 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -162,6 +162,7 @@ module status ``util::error`` stable ``util::log`` proof of concept ``util::module`` complete +``util::qobject`` stable ``util::timer`` stable =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 diff --git a/rust/util/meson.build b/rust/util/meson.build index 4621932fdcb..92d27957eda 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -40,6 +40,7 @@ _util_rs =3D static_library( {'.': _util_bindings_inc_rs, 'qobject': [ 'src/qobject/mod.rs', + 'src/qobject/deserializer.rs', 'src/qobject/deserialize.rs', 'src/qobject/error.rs', 'src/qobject/serializer.rs', diff --git a/rust/util/src/qobject/deserializer.rs b/rust/util/src/qobject/= deserializer.rs new file mode 100644 index 00000000000..b0f8aa23773 --- /dev/null +++ b/rust/util/src/qobject/deserializer.rs @@ -0,0 +1,371 @@ +//! `QObject` deserializer +//! +//! This module implements a [`Deserializer`](serde::de::Deserializer) that +//! consumes `QObject`s, allowing them to be turned into deserializable da= ta +//! structures (such as primitive data types, or structs that implement +//! `Deserialize`). + +use std::ffi::CStr; + +use serde::de::{ + self, value::StrDeserializer, DeserializeSeed, Expected, IntoDeseriali= zer, MapAccess, + SeqAccess, Unexpected, Visitor, +}; + +use super::{ + error::{Error, Result}, + match_qobject, QObject, +}; +use crate::bindings; + +impl QObject { + #[cold] + fn invalid_type(&self, exp: &dyn Expected) -> E + where + E: serde::de::Error, + { + serde::de::Error::invalid_type(self.unexpected(), exp) + } + + #[cold] + fn unexpected(&self) -> Unexpected<'_> { + match_qobject! { (self) =3D> + () =3D> Unexpected::Unit, + bool(b) =3D> Unexpected::Bool(b), + i64(n) =3D> Unexpected::Signed(n), + u64(n) =3D> Unexpected::Unsigned(n), + f64(n) =3D> Unexpected::Float(n), + CStr(s) =3D> s.to_str().map_or_else( + |_| Unexpected::Other("string with invalid UTF-8"), + Unexpected::Str), + QList(_) =3D> Unexpected::Seq, + QDict(_) =3D> Unexpected::Map, + } + } +} + +fn visit_qlist_ref<'de, V>(qlist: &'de bindings::QList, visitor: V) -> Res= ult +where + V: Visitor<'de>, +{ + struct QListDeserializer(*mut bindings::QListEntry, usize); + + impl<'de> SeqAccess<'de> for QListDeserializer { + type Error =3D Error; + + fn next_element_seed(&mut self, seed: T) -> Result> + where + T: DeserializeSeed<'de>, + { + if self.0.is_null() { + return Ok(None); + } + + let e =3D unsafe { &*self.0 }; + // increment the reference count because deserialize consumes = `value`. + let value =3D unsafe { QObject::cloned_from_raw(e.value.cast_c= onst()) }; + let result =3D seed.deserialize(value); + self.0 =3D unsafe { e.next.tqe_next }; + self.1 +=3D 1; + result.map(Some) + } + } + + let mut deserializer =3D QListDeserializer(unsafe { qlist.head.tqh_fir= st }, 0); + let seq =3D visitor.visit_seq(&mut deserializer)?; + if deserializer.0.is_null() { + Ok(seq) + } else { + Err(serde::de::Error::invalid_length( + deserializer.1, + &"fewer elements in array", + )) + } +} + +fn visit_qdict_ref<'de, V>(qdict: &'de bindings::QDict, visitor: V) -> Res= ult +where + V: Visitor<'de>, +{ + struct QDictDeserializer(*mut bindings::QDict, *const bindings::QDictE= ntry); + + impl<'de> MapAccess<'de> for QDictDeserializer { + type Error =3D Error; + + fn next_key_seed(&mut self, seed: T) -> Result> + where + T: DeserializeSeed<'de>, + { + if self.1.is_null() { + return Ok(None); + } + + let e =3D unsafe { &*self.1 }; + let key =3D unsafe { CStr::from_ptr(e.key) }; + let key_de =3D StrDeserializer::new(key.to_str()?); + seed.deserialize(key_de).map(Some) + } + + fn next_value_seed(&mut self, seed: T) -> Result + where + T: DeserializeSeed<'de>, + { + if self.1.is_null() { + panic!("next_key must have returned None"); + } + + let e =3D unsafe { &*self.1 }; + // increment the reference count because deserialize consumes = `value`. + let value =3D unsafe { QObject::cloned_from_raw(e.value) }; + let result =3D seed.deserialize(value); + self.1 =3D unsafe { bindings::qdict_next(self.0, self.1) }; + result + } + } + + let qdict =3D (qdict as *const bindings::QDict).cast_mut(); + let e =3D unsafe { bindings::qdict_first(qdict) }; + let mut deserializer =3D QDictDeserializer(qdict, e); + let map =3D visitor.visit_map(&mut deserializer)?; + if deserializer.1.is_null() { + Ok(map) + } else { + Err(serde::de::Error::invalid_length( + unsafe { bindings::qdict_size(qdict) }, + &"fewer elements in map", + )) + } +} + +fn visit_qnum_ref<'de, V>(qnum: QObject, visitor: V) -> Result +where + V: Visitor<'de>, +{ + match_qobject! { (qnum) =3D> + i64(n) =3D> visitor.visit_i64(n), + u64(n) =3D> visitor.visit_u64(n), + f64(n) =3D> visitor.visit_f64(n), + _ =3D> Err(qnum.invalid_type(&"number")), + } +} + +macro_rules! deserialize_number { + ($method:ident) =3D> { + fn $method(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visit_qnum_ref(self, visitor) + } + }; +} + +impl<'de> serde::Deserializer<'de> for QObject { + type Error =3D Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + () =3D> visitor.visit_unit(), + bool(v) =3D> visitor.visit_bool(v), + i64(n) =3D> visitor.visit_i64(n), + u64(n) =3D> visitor.visit_u64(n), + f64(n) =3D> visitor.visit_f64(n), + CStr(cstr) =3D> visitor.visit_str(cstr.to_str()?), + QList(qlist) =3D> visit_qlist_ref(qlist, visitor), + QDict(qdict) =3D> visit_qdict_ref(qdict, visitor), + } + } + + deserialize_number!(deserialize_i8); + deserialize_number!(deserialize_i16); + deserialize_number!(deserialize_i32); + deserialize_number!(deserialize_i64); + deserialize_number!(deserialize_i128); + deserialize_number!(deserialize_u8); + deserialize_number!(deserialize_u16); + deserialize_number!(deserialize_u32); + deserialize_number!(deserialize_u64); + deserialize_number!(deserialize_u128); + deserialize_number!(deserialize_f32); + deserialize_number!(deserialize_f64); + + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + () =3D> visitor.visit_none(), + _ =3D> visitor.visit_some(self), + } + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + CStr(cstr) =3D> visitor.visit_enum(cstr.to_str()?.into_deseria= lizer()), + _ =3D> Err(self.invalid_type(&"string")), + } + } + + fn deserialize_newtype_struct(self, _name: &'static str, visitor: V= ) -> Result + where + V: Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + bool(v) =3D> visitor.visit_bool(v), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + CStr(cstr) =3D> visitor.visit_str(cstr.to_str()?), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + CStr(cstr) =3D> visitor.visit_str(cstr.to_str()?), + QList(qlist) =3D> visit_qlist_ref(qlist, visitor), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_byte_buf(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_bytes(visitor) + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + () =3D> visitor.visit_unit(), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -= > Result + where + V: Visitor<'de>, + { + self.deserialize_unit(visitor) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + QList(qlist) =3D> visit_qlist_ref(qlist, visitor), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_tuple(self, _len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + self.deserialize_seq(visitor) + } + + fn deserialize_map(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + QDict(qdict) =3D> visit_qdict_ref(qdict, visitor), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_struct( + self, + _name: &'static str, + _fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + match_qobject! { (self) =3D> + QList(qlist) =3D> visit_qlist_ref(qlist, visitor), + QDict(qdict) =3D> visit_qdict_ref(qdict, visitor), + _ =3D> Err(self.invalid_type(&visitor)), + } + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.deserialize_str(visitor) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_unit() + } +} + +pub fn from_qobject(value: QObject) -> Result +where + T: de::DeserializeOwned, +{ + T::deserialize(value) +} diff --git a/rust/util/src/qobject/error.rs b/rust/util/src/qobject/error.rs index 5212e65c4f7..2d7c180187a 100644 --- a/rust/util/src/qobject/error.rs +++ b/rust/util/src/qobject/error.rs @@ -6,7 +6,7 @@ str::Utf8Error, }; =20 -use serde::ser; +use serde::{de, ser}; =20 #[derive(Debug)] pub enum Error { @@ -23,6 +23,12 @@ fn custom(msg: T) -> Self { } } =20 +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error::Custom(msg.to_string()) + } +} + impl From for Error { fn from(_: NulError) -> Self { Error::NulEncountered diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs index c0c16f28049..b95dddddedc 100644 --- a/rust/util/src/qobject/mod.rs +++ b/rust/util/src/qobject/mod.rs @@ -7,6 +7,7 @@ #![deny(clippy::unwrap_used)] =20 mod deserialize; +mod deserializer; mod error; mod serialize; mod serializer; @@ -20,6 +21,7 @@ }; =20 use common::assert_field_type; +pub use deserializer::from_qobject; pub use error::{Error, Result}; pub use serializer::to_qobject; =20 --=20 2.52.0