I don't know enough about Rust/serde to give advice. I do know how to make a fool of myself by asking dumb questions.
Paolo Bonzini <pbonz...@redhat.com> writes: > On Wed, Jun 11, 2025 at 10:57 AM Paolo Bonzini <pbonz...@redhat.com> wrote: >> Yes. If using serde the implementation of the traits is very small, >> and basically the same for all types. If not using serde, it would >> need some (or most) of the infrastructure in Marc-André's original >> series. > > Looking more at it, the Rust->QObject and QObject->Rust parts *can* be > done with serde (following the model of serde_json's Value > (de)serializer) but the Rust<->C part has a problem. > > To recap, Rust->C is the serialization and corresponds to output > visitors. C->Rust is the deserialization and corresponds to input > visitors. > > For serialization, serde has a push model where the generated code > looks like this: > > let mut state = > Serializer::serialize_struct(serializer, "S", 2); > SerializeStruct::serialize_field(&mut state, "a", &self.a)?; > SerializeStruct::serialize_field(&mut state, "b", &self.b)?; > SerializeStruct::end(state) > > whereas QAPI has a pull model where visit_type_* drives the process > and requests the fields one by one. > > For deserialization, serde has a pull model where the generated code > asks for the field names one by one: > > fn visit_map<__A>(self, mut __map: __A) > while let Some(key) = > MapAccess::next_key::<__Field>(&mut __map)? { > match __key { ... } > } > } > > whereas QAPI has a push model where visit_type_* again drives the > process and sends fields one by one. > > For commands this is not a problem because the real underlying > transformation is QObject->QObject and the intermediate steps (to and > from QObject) can use serde. Are you talking about commands implemented in Rust? The existing data flow is roughly like this (I'm simplifying): 1. Parse JSON text into request QObject, pass to QMP core 2. Extract command name string and argument QDict 3. Look up generated command marshaller / unmarshaller, pass argument QDict to it 4. Unmarshall argument QDict with the QObject input visitor and generated visit_type_ARG() 5. Pass the C arguments to the handwritten command handler, receive the C return value 6. Marshall the return value into a QObject with the QObject output visitor and generated visit_type_RET(), return it to QMP core 7. Insert it into a response QObject 8. Unparse response QObject into JSON text How would a Serde flow look like? > However, QOM property getters/setters (especially, but not > exclusively, for properties with compound types) remain a problem > since these use callbacks with a Visitor* argument. object_property_set() takes the new property value wrapped in an input visitor. The property setter extracts it using visit_type_FOOs() with this input visitor as it sees fit. Ideally, it uses exactly visit_type_PROPTYPE(). object_property_get() takes an output visitor to be wrapped it around the property value. The property getter inserts it using visit_type_FOOs() with this output visitor as it sees fit. Ideally, it uses exactly visit_type_PROPTYPE(). We sometimes use a QObject input / output visitor, and sometimes a string input / output visitor. The latter come with restrictions, and are evolutionary dead ends. The QObject visitors wrap a QObject, the string visitors wrap a string (d'oh). > I see three > possibilities: > > 1) everything is done through an intermediate QObject step (e.g. for a > setter: Visitor->QObject with an input visitor, and QObject->Rust with > serde deserialization). > + easy, Rust only sees serde > + QMP commands use a single conversion step > - inefficient > > 2) everything is done through an intermediate C step (e.g. for a > setter: Visitor->C with a visit_type_* function, and C->Rust with > generated code that does not need to use serde). There is still a > double conversion step, but it's more efficient than option 1 > + one framework (visitor) > - double conversion for the QMP commands > - lots of generated code > > 3) generating a Rust visit_type_* implementation as well, either in > qapi-gen (3a) or through a procedural macro (3b). This should not be > hard to write but it would remove a lot of the advantages from using > serde. > + efficient > + preserves single conversion for QMP commands > - two frameworks I'm afraid this is too terse for ignorant me. > I am leaning towards option 1, i.e. keep using serde but only cover > conversions to and from QObject. The reason is that one future usecase > for Rust in QEMU is the UEFI variable store; that one also has some > Rust<->JSON conversions and could be served by either QObject or > serde_json. Either way, it'd be nice for the UEFI variable store to > remain within the Rust serde ecosystem and allow sharing code between > QEMU and Coconut SVSM. But I'm not so sure... > > Paolo