Il mer 18 giu 2025, 16:25 Markus Armbruster <arm...@redhat.com> ha scritto:
> 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. > No dumb questions, only dumb answers. > 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? > Yes. I will intersperse your text with the corresponding Rust/serde implementation. 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 > Same so far since this is C code. 4. Unmarshall argument QDict with the QObject input visitor and > generated visit_type_ARG() > Unmarshall with QObject Deserializer, which talks to a serde-generated Deserialize implementation. 5. Pass the C arguments to the handwritten command handler, receive the > C return value > Same. 6. Marshall the return value into a QObject with the QObject output > visitor and generated visit_type_RET(), return it to QMP core > Marshall with the QObject Serializer, which talks to a serde-generated Serialize implementation. 7. Insert it into a response QObject > 8. Unparse response QObject into JSON text > Same. How would a Serde flow look like? > As described above, visitors are bypassed and the marshalling/unmarshalling works directly at the QObject level. Implementation-wise the main difference is that 1) the type code (Serialize/Deserialize) is not the same for serialization and desetialization, unlike visit_type_*() 2) the code generation is done by serde instead of qapi-gen and we'd be mostly oblivious to how it works. The Serializer and Deserializer should be about 1500 lines of Rust + tests, and they would do the functionality of the QObject input and output visitors. > 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). > Yep. The string visitor is why we cannot just change getters and setters to use QObject. In this case, without writing a visit_type_*() implementation that can write to a Rust struct, an intermediate QObject would be the only way to turn a Visitor into a Rust data type. So I can imagine three ways to operate: * Keep using serde like for commands: in the callback that is invoked by object_property_set() do Visitor->QObject->setter (yes that means double conversion when the source visitor is and QObject visitor) or for the getter case, getter->QObject->Visitor. This has the minimum amount of code added to qapi-gen. * Generate a visit_type_*() implementation that emits a Rust struct (i.e. one that maps for example 'str' to a String and not a *mut c_char) and forgo serde completely. Use this generated implementation everywhere: QOM getters and setters, as well as QMP commands. This is how C code works. * Generate rust->C (e.g. String->*mut c_char) and C->rust converters from qapi-gen; use the existing C visit_type_*() to extract data from visitors and then apply said converters to turn the data into a Rust struct, and likewise in the other direction. This was the way Marc-André's prototype worked. I'm afraid this is too terse for ignorant me. > I tried to translate that. :) Paolo > > 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 > >