From: Paolo Bonzini <pbonzini@redhat.com>
To: qemu-devel@nongnu.org
Cc: qemu-rust@nongnu.org, armbru@redhat.com, marcandre.lureau@redhat.com
Subject: [PATCH 05/14] rust: add Serializer (to_qobject) implementation for QObject
Date: Wed, 1 Oct 2025 10:00:42 +0200 [thread overview]
Message-ID: <20251001080051.1043944-6-pbonzini@redhat.com> (raw)
In-Reply-To: <20251001075005.1041833-1-pbonzini@redhat.com>
This allows creating QObject from any serializable data structure.
Co-authored-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
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 5b99d38c903..fb152766003 100644
--- a/rust/util/meson.build
+++ b/rust/util/meson.build
@@ -39,6 +39,8 @@ _util_rs = 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=2021', 'build.rust_std=2021'],
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<T: Display>(msg: T) -> Self {
+ Error::Custom(msg.to_string())
+ }
+}
+
+impl From<NulError> for Error {
+ fn from(_: NulError) -> Self {
+ Error::NulEncountered
+ }
+}
+
+impl From<Utf8Error> 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) => formatter.write_str(msg),
+ Error::KeyMustBeAString => formatter.write_str("key must be a string"),
+ Error::InvalidUtf8 => formatter.write_str("invalid UTF-8 in string"),
+ Error::NulEncountered => formatter.write_str("NUL character in string"),
+ Error::NumberOutOfRange => formatter.write_str("number out of range"),
+ }
+ }
+}
+
+impl std::error::Error for Error {}
+
+pub type Result<T> = std::result::Result<T, Error>;
diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs
index 2e3cb247b27..cd034185748 100644
--- a/rust/util/src/qobject/mod.rs
+++ b/rust/util/src/qobject/mod.rs
@@ -6,7 +6,9 @@
#![deny(clippy::unwrap_used)]
+mod error;
mod serialize;
+mod serializer;
use std::{
cell::UnsafeCell,
@@ -17,6 +19,8 @@
};
use common::assert_field_type;
+pub use error::{Error, Result};
+pub use serializer::to_qobject;
use crate::bindings;
diff --git a/rust/util/src/qobject/serializer.rs b/rust/util/src/qobject/serializer.rs
new file mode 100644
index 00000000000..59200683f5d
--- /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<QObject>,
+}
+
+impl serde::ser::SerializeTupleVariant for SerializeTupleVariant {
+ type Ok = QObject;
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ self.vec.push(to_qobject(value)?);
+ Ok(())
+ }
+
+ fn end(self) -> Result<QObject> {
+ let SerializeTupleVariant { name, vec, .. } = self;
+
+ // TODO: insert elements one at a time
+ let list = QObject::from_iter(vec);
+
+ // serde by default represents enums as a single-entry object, with
+ // the variant stored in the key ("external tagging"). Internal tagging
+ // is implemented by the struct that requests it, not by the serializer.
+ let map = [(name, list)];
+ Ok(QObject::from_iter(map))
+ }
+}
+
+pub struct SerializeStructVariant {
+ name: CString,
+ vec: Vec<(CString, QObject)>,
+}
+
+impl serde::ser::SerializeStructVariant for SerializeStructVariant {
+ type Ok = QObject;
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ self.vec.push((CString::new(key)?, to_qobject(value)?));
+ Ok(())
+ }
+
+ fn end(self) -> Result<QObject> {
+ // TODO: insert keys one at a time
+ let SerializeStructVariant { name, vec, .. } = self;
+ let list = QObject::from_iter(vec);
+
+ // serde by default represents enums as a single-entry object, with
+ // the variant stored in the key ("external tagging"). Internal tagging
+ // is implemented by the struct that requests it, not by the serializer.
+ let map = [(name, list)];
+ Ok(QObject::from_iter(map))
+ }
+}
+
+pub struct SerializeVec {
+ vec: Vec<QObject>,
+}
+
+impl serde::ser::SerializeSeq for SerializeVec {
+ type Ok = QObject;
+ type Error = Error;
+
+ fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ self.vec.push(to_qobject(value)?);
+ Ok(())
+ }
+
+ fn end(self) -> Result<QObject> {
+ // TODO: insert elements one at a time
+ let SerializeVec { vec, .. } = self;
+ let list = QObject::from_iter(vec);
+ Ok(list)
+ }
+}
+
+impl serde::ser::SerializeTuple for SerializeVec {
+ type Ok = QObject;
+ type Error = Error;
+
+ fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ serde::ser::SerializeSeq::serialize_element(self, value)
+ }
+
+ fn end(self) -> Result<QObject> {
+ serde::ser::SerializeSeq::end(self)
+ }
+}
+
+impl serde::ser::SerializeTupleStruct for SerializeVec {
+ type Ok = QObject;
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ serde::ser::SerializeSeq::serialize_element(self, value)
+ }
+
+ fn end(self) -> Result<QObject> {
+ serde::ser::SerializeSeq::end(self)
+ }
+}
+
+struct MapKeySerializer;
+
+impl serde::Serializer for MapKeySerializer {
+ type Ok = CString;
+ type Error = Error;
+
+ type SerializeSeq = Impossible<CString, Error>;
+ type SerializeTuple = Impossible<CString, Error>;
+ type SerializeTupleStruct = Impossible<CString, Error>;
+ type SerializeTupleVariant = Impossible<CString, Error>;
+ type SerializeMap = Impossible<CString, Error>;
+ type SerializeStruct = Impossible<CString, Error>;
+ type SerializeStructVariant = Impossible<CString, Error>;
+
+ #[inline]
+ fn serialize_unit_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ ) -> Result<CString> {
+ Ok(CString::new(variant)?)
+ }
+
+ #[inline]
+ fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<CString>
+ where
+ T: ?Sized + Serialize,
+ {
+ value.serialize(self)
+ }
+
+ fn serialize_bool(self, _value: bool) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_i8(self, _value: i8) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_i16(self, _value: i16) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_i32(self, _value: i32) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_i64(self, _value: i64) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_i128(self, _value: i128) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_u8(self, _value: u8) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_u16(self, _value: u16) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_u32(self, _value: u32) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_u64(self, _value: u64) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_u128(self, _value: u128) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_f32(self, _value: f32) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_f64(self, _value: f64) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ #[inline]
+ fn serialize_char(self, _value: char) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ #[inline]
+ fn serialize_str(self, value: &str) -> Result<CString> {
+ Ok(CString::new(value)?)
+ }
+
+ fn serialize_bytes(self, value: &[u8]) -> Result<CString> {
+ Ok(CString::new(value)?)
+ }
+
+ fn serialize_unit(self) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_unit_struct(self, _name: &'static str) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_newtype_variant<T>(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _value: &T,
+ ) -> Result<CString>
+ where
+ T: ?Sized + Serialize,
+ {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_none(self) -> Result<CString> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_some<T>(self, _value: &T) -> Result<CString>
+ where
+ T: ?Sized + Serialize,
+ {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ _name: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeTupleStruct> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeTupleVariant> {
+ Err(Error::KeyMustBeAString)
+ }
+
+ fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+ 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<Self::SerializeStructVariant> {
+ Err(Error::KeyMustBeAString)
+ }
+}
+
+pub struct SerializeMap {
+ vec: Vec<(CString, QObject)>,
+ next_key: Option<CString>,
+}
+
+impl serde::ser::SerializeMap for SerializeMap {
+ type Ok = QObject;
+ type Error = Error;
+
+ fn serialize_key<T>(&mut self, key: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ self.next_key = Some(key.serialize(MapKeySerializer)?);
+ Ok(())
+ }
+
+ fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ let key = self.next_key.take();
+ // Panic because this indicates a bug in the program rather than an
+ // expected failure.
+ let key = key.expect("serialize_value called before serialize_key");
+ self.vec.push((key, to_qobject(value)?));
+ Ok(())
+ }
+
+ fn end(self) -> Result<QObject> {
+ // TODO: insert keys one at a time
+ let SerializeMap { vec, .. } = self;
+ Ok(QObject::from_iter(vec))
+ }
+}
+
+impl serde::ser::SerializeStruct for SerializeMap {
+ type Ok = QObject;
+ type Error = Error;
+
+ fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+ where
+ T: ?Sized + Serialize,
+ {
+ serde::ser::SerializeMap::serialize_entry(self, key, value)
+ }
+
+ fn end(self) -> Result<QObject> {
+ 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 = QObject;
+ type Error = Error;
+ type SerializeSeq = SerializeVec;
+ type SerializeTuple = SerializeVec;
+ type SerializeTupleStruct = SerializeVec;
+ type SerializeTupleVariant = SerializeTupleVariant;
+ type SerializeMap = SerializeMap;
+ type SerializeStruct = SerializeMap;
+ type SerializeStructVariant = SerializeStructVariant;
+
+ #[inline]
+ fn serialize_bool(self, value: bool) -> Result<QObject> {
+ Ok(value.into())
+ }
+
+ #[inline]
+ fn serialize_i8(self, value: i8) -> Result<QObject> {
+ self.serialize_i64(i64::from(value))
+ }
+
+ #[inline]
+ fn serialize_i16(self, value: i16) -> Result<QObject> {
+ self.serialize_i64(i64::from(value))
+ }
+
+ #[inline]
+ fn serialize_i32(self, value: i32) -> Result<QObject> {
+ self.serialize_i64(i64::from(value))
+ }
+
+ fn serialize_i64(self, value: i64) -> Result<QObject> {
+ Ok(value.into())
+ }
+
+ fn serialize_i128(self, value: i128) -> Result<QObject> {
+ if let Ok(value) = u64::try_from(value) {
+ Ok(value.into())
+ } else if let Ok(value) = i64::try_from(value) {
+ Ok(value.into())
+ } else {
+ Err(Error::NumberOutOfRange)
+ }
+ }
+
+ #[inline]
+ fn serialize_u8(self, value: u8) -> Result<QObject> {
+ self.serialize_u64(u64::from(value))
+ }
+
+ #[inline]
+ fn serialize_u16(self, value: u16) -> Result<QObject> {
+ self.serialize_u64(u64::from(value))
+ }
+
+ #[inline]
+ fn serialize_u32(self, value: u32) -> Result<QObject> {
+ self.serialize_u64(u64::from(value))
+ }
+
+ #[inline]
+ fn serialize_u64(self, value: u64) -> Result<QObject> {
+ Ok(value.into())
+ }
+
+ fn serialize_u128(self, value: u128) -> Result<QObject> {
+ if let Ok(value) = u64::try_from(value) {
+ Ok(value.into())
+ } else {
+ Err(Error::NumberOutOfRange)
+ }
+ }
+
+ #[inline]
+ fn serialize_f32(self, float: f32) -> Result<QObject> {
+ self.serialize_f64(f64::from(float))
+ }
+
+ #[inline]
+ fn serialize_f64(self, float: f64) -> Result<QObject> {
+ Ok(float.into())
+ }
+
+ #[inline]
+ fn serialize_char(self, value: char) -> Result<QObject> {
+ let mut s = String::new();
+ s.push(value);
+ Ok(CString::new(s)?.into())
+ }
+
+ #[inline]
+ fn serialize_str(self, value: &str) -> Result<QObject> {
+ Ok(CString::new(value)?.into())
+ }
+
+ fn serialize_bytes(self, value: &[u8]) -> Result<QObject> {
+ // Serialize into a vector of numeric QObjects
+ let it = value.iter().map(|&b| u64::from(b));
+ Ok(QObject::from_iter(it))
+ }
+
+ #[inline]
+ fn serialize_unit(self) -> Result<QObject> {
+ Ok(().into())
+ }
+
+ #[inline]
+ fn serialize_unit_struct(self, _name: &'static str) -> Result<QObject> {
+ self.serialize_unit()
+ }
+
+ #[inline]
+ fn serialize_unit_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ ) -> Result<QObject> {
+ self.serialize_str(variant)
+ }
+
+ #[inline]
+ fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<QObject>
+ where
+ T: ?Sized + Serialize,
+ {
+ value.serialize(self)
+ }
+
+ fn serialize_newtype_variant<T>(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ value: &T,
+ ) -> Result<QObject>
+ where
+ T: ?Sized + Serialize,
+ {
+ // serde by default represents enums as a single-entry object, with
+ // the variant stored in the key ("external tagging"). Internal tagging
+ // is implemented by the struct that requests it, not by the serializer.
+ let map = [(CString::new(variant)?, to_qobject(value)?)];
+ Ok(QObject::from_iter(map))
+ }
+
+ #[inline]
+ fn serialize_none(self) -> Result<QObject> {
+ self.serialize_unit()
+ }
+
+ #[inline]
+ fn serialize_some<T>(self, value: &T) -> Result<QObject>
+ where
+ T: ?Sized + Serialize,
+ {
+ value.serialize(self)
+ }
+
+ fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
+ Ok(SerializeVec {
+ vec: Vec::with_capacity(len.unwrap_or(0)),
+ })
+ }
+
+ fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ _name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleStruct> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleVariant> {
+ Ok(SerializeTupleVariant {
+ name: CString::new(variant)?,
+ vec: Vec::with_capacity(len),
+ })
+ }
+
+ fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+ Ok(SerializeMap {
+ vec: Vec::new(),
+ next_key: None,
+ })
+ }
+ fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+ self.serialize_map(Some(len))
+ }
+
+ fn serialize_struct_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ variant: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeStructVariant> {
+ Ok(SerializeStructVariant {
+ name: CString::new(variant)?,
+ vec: Vec::new(),
+ })
+ }
+}
+
+pub fn to_qobject<T>(input: T) -> Result<QObject>
+where
+ T: Serialize,
+{
+ input.serialize(Serializer)
+}
--
2.51.0
next prev parent reply other threads:[~2025-10-01 8:03 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-01 7:49 [PATCH 00/11] rust: migration: add high-level migration wrappers Paolo Bonzini
2025-10-01 7:52 ` [PATCH 01/11] rust: bql: add BqlRefCell::get_mut() Paolo Bonzini
2025-10-13 8:49 ` Zhao Liu
2025-10-01 7:52 ` [PATCH 02/11] rust: migration: do not pass raw pointer to VMStateDescription::fields Paolo Bonzini
2025-10-13 8:20 ` Zhao Liu
2025-10-01 7:52 ` [PATCH 03/11] rust: migration: do not store raw pointers into VMStateSubsectionsWrapper Paolo Bonzini
2025-10-13 8:46 ` Zhao Liu
2025-10-01 7:52 ` [PATCH 04/11] rust: migration: validate termination of subsection arrays Paolo Bonzini
2025-10-13 8:46 ` Zhao Liu
2025-10-01 7:52 ` [PATCH 05/11] rust: migration: extract vmstate_fields_ref Paolo Bonzini
2025-10-13 8:55 ` Zhao Liu
2025-10-01 7:52 ` [PATCH 06/11] rust: move VMState from bql to migration Paolo Bonzini
2025-10-13 8:57 ` Zhao Liu
2025-10-01 7:52 ` [PATCH 07/11] rust: migration: add high-level migration wrappers Paolo Bonzini
2025-10-01 7:52 ` [PATCH 08/11] rust: qemu-macros: add ToMigrationState derive macro Paolo Bonzini
2025-10-01 7:52 ` [PATCH 09/11] timer: constify some functions Paolo Bonzini
2025-10-01 7:52 ` [PATCH 10/11] rust: migration: implement ToMigrationState for Timer Paolo Bonzini
2025-10-01 7:52 ` [PATCH 11/11] rust: migration: implement ToMigrationState as part of impl_vmstate_bitsized Paolo Bonzini
2025-10-01 8:00 ` [PATCH preview 00/14] rust: QObject and QAPI bindings Paolo Bonzini
2025-10-01 8:00 ` [PATCH 01/14] qobject: make refcount atomic Paolo Bonzini
2025-10-13 7:51 ` Zhao Liu
2025-10-01 8:00 ` [PATCH 02/14] rust: add basic QObject bindings Paolo Bonzini
2025-10-01 8:00 ` [PATCH 03/14] subprojects: add serde Paolo Bonzini
2025-10-01 8:00 ` [PATCH 04/14] rust: add Serialize implementation for QObject Paolo Bonzini
2025-10-01 8:00 ` Paolo Bonzini [this message]
2025-10-01 8:00 ` [PATCH 06/14] rust: add Deserialize " Paolo Bonzini
2025-10-01 8:00 ` [PATCH 07/14] rust: add Deserializer (from_qobject) " Paolo Bonzini
2025-10-01 8:00 ` [PATCH 08/14] rust/qobject: add Display/Debug Paolo Bonzini
2025-10-01 8:00 ` [PATCH 09/14] scripts/qapi: add QAPISchemaIfCond.rsgen() Paolo Bonzini
2025-10-01 8:00 ` [PATCH 10/14] scripts/qapi: generate high-level Rust bindings Paolo Bonzini
2025-10-01 8:00 ` [PATCH 11/14] scripts/qapi: strip trailing whitespaces Paolo Bonzini
2025-10-01 8:00 ` [PATCH 12/14] scripts/rustc_args: add --no-strict-cfg Paolo Bonzini
2025-10-01 8:00 ` [PATCH 13/14] rust/util: build QAPI types Paolo Bonzini
2025-10-01 8:00 ` [PATCH 14/14] rust: start qapi tests Paolo Bonzini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251001080051.1043944-6-pbonzini@redhat.com \
--to=pbonzini@redhat.com \
--cc=armbru@redhat.com \
--cc=marcandre.lureau@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-rust@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).