From: "Daniel P. Berrangé" <berrange@redhat.com>
To: Andrea Bolognani <abologna@redhat.com>
Cc: Victor Toso <victortoso@redhat.com>,
qemu-devel@nongnu.org, Eric Blake <eblake@redhat.com>,
Markus Armbruster <armbru@redhat.com>,
John Snow <jsnow@redhat.com>
Subject: Re: [RFC PATCH v2 5/8] qapi: golang: Generate qapi's event types in Go
Date: Tue, 5 Jul 2022 17:47:25 +0100 [thread overview]
Message-ID: <YsRrHbNAZCjmQUcL@redhat.com> (raw)
In-Reply-To: <CABJz62MhUS4LNOWNwPdf0vwwL2JMUzkmLrPBotezchyomGg58Q@mail.gmail.com>
On Tue, Jul 05, 2022 at 08:45:54AM -0700, Andrea Bolognani wrote:
> On Fri, Jun 17, 2022 at 02:19:29PM +0200, Victor Toso wrote:
> > This patch handles QAPI event types and generates data structures in
> > Go that handles it.
> >
> > We also define a Event interface and two helper functions MarshalEvent
> > and UnmarshalEvent.
> >
> > At the moment of this writing, this patch generates 51 structures (50
> > events)
> >
> > Example:
> >
> > qapi:
> > | { 'event': 'MEMORY_DEVICE_SIZE_CHANGE',
> > | 'data': { '*id': 'str', 'size': 'size', 'qom-path' : 'str'} }
> >
> > go:
> > | type MemoryDeviceSizeChangeEvent struct {
> > | EventTimestamp Timestamp `json:"-"`
> > | Id *string `json:"id,omitempty"`
> > | Size uint64 `json:"size"`
> > | QomPath string `json:"qom-path"`
> > | }
> >
> > usage:
> > | input := `{"event":"MEMORY_DEVICE_SIZE_CHANGE",` +
> > | `"timestamp":{"seconds":1588168529,"microseconds":201316},` +
> > | `"data":{"id":"vm0","size":1073741824,"qom-path":"/machine/unattached/device[2]"}}`
> > | e, err := UnmarshalEvent([]byte(input)
> > | if err != nil {
> > | panic(err)
> > | }
> > | if e.GetName() == `MEMORY_DEVICE_SIZE_CHANGE` {
> > | m := e.(*MemoryDeviceSizeChangeEvent)
> > | // m.QomPath == "/machine/unattached/device[2]"
> > | }
> > func (s *AcpiDeviceOstEvent) GetTimestamp() Timestamp {
> > return s.EventTimestamp
> > }
>
> Does this even need a getter? The struct member is public, and Go
> code seems to favor direct member access.
It satisfies the 'Event' interface, so you can fetch timestamp
without needing to know the specific event struct you're dealing
with.
>
> > type Timestamp struct {
> > Seconds int64 `json:"seconds"`
> > Microseconds int64 `json:"microseconds"`
> > }
> >
> > type Event interface {
> > GetName() string
> > GetTimestamp() Timestamp
> > }
> >
> > func UnmarshalEvent(data []byte) (Event, error) {
> > base := struct {
> > Name string `json:"event"`
> > EventTimestamp Timestamp `json:"timestamp"`
> > }{}
> > if err := json.Unmarshal(data, &base); err != nil {
> > return nil, errors.New(fmt.Sprintf("Failed to decode event: %s", string(data)))
> > }
> >
> > switch base.Name {
> >
> > case "ACPI_DEVICE_OST":
> > event := struct {
> > Data AcpiDeviceOstEvent `json:"data"`
> > }{}
> >
> > if err := json.Unmarshal(data, &event); err != nil {
> > return nil, errors.New(fmt.Sprintf("Failed to unmarshal: %s", string(data)))
> > }
> > event.Data.EventTimestamp = base.EventTimestamp
> > return &event.Data, nil
> >
> > // more cases here
> > }
> > return nil, errors.New("Failed to recognize event")
> > }
>
> While I understand the need to have some way to figure out exactly
> what type of event we're looking at before being able to unmarshal
> the JSON data, I don't like how we force users to go through a
> non-standard API for it.
>
> Here's how I think we should do it:
>
> func GetEventType(data []byte) (Event, error) {
> type event struct {
> Name string `json:"event"`
> }
>
> tmp := event{}
> if err := json.Unmarshal(data, &tmp); err != nil {
> return nil, errors.New(fmt.Sprintf("Failed to decode event:
> %s", string(data)))
> }
>
> switch tmp.Name {
> case "ACPI_DEVICE_OST":
> return &AcpiDeviceOstEvent{}, nil
>
> // more cases here
> }
>
> return nil, errors.New("Failed to recognize event")
> }
>
> func (s *AcpiDeviceOstEvent) UnmarshalJSON(data []byte) error {
> type eventData struct {
> Info ACPIOSTInfo `json:"info"`
> }
> type event struct {
> Name string `json:"event"`
> EventTimestamp Timestamp `json:"timestamp"`
> Data eventData `json:"data"`
> }
>
> tmp := event{}
> err := json.Unmarshal(data, &tmp)
> if err != nil {
> return err
> }
> if tmp.Name != "ACPI_DEVICE_OST" {
> return errors.New("name")
> }
>
> s.EventTimestamp = tmp.EventTimestamp
> s.Info = tmp.Data.Info
> return nil
> }
>
> This way, client code can look like
>
> in := `{"event":"ACPI_DEVICE_OST","timestamp":{"seconds":1265044230,"microseconds":450486},"data":{"info":{"device":"d1","slot":"0","slot-type":"DIMM","source":1,"status":0}}}`
>
> e, err := qapi.GetEventType([]byte(in))
> if err != nil {
> panic(err)
> }
> // e is of type AcpiDeviceOstEvent
>
> err = json.Unmarshal([]byte(in), &e)
> if err != nil {
> panic(err)
> }
>
> where only the first part (figuring out the type of the event) needs
> custom APIs, and the remaining code looks just like your average JSON
> handling.
I don't think this kind of detail really needs to be exposed to
clients. Also parsing the same json doc twice just feels wrong.
I think using the dummy union structs is the right approach, but
I'd just call it 'EventFromJSON' rather than 'UnmarshalJSON' to
make it clear this is different from a normal 'UnmarshalJSON'
method.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
next prev parent reply other threads:[~2022-07-05 17:07 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-17 12:19 [RFC PATCH v2 0/8] qapi: add generator for Golang interface Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 1/8] qapi: golang: Generate qapi's enum types in Go Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 2/8] qapi: golang: Generate qapi's alternate " Victor Toso
2022-07-05 15:45 ` Andrea Bolognani
2022-08-17 14:04 ` Victor Toso
2022-08-19 16:27 ` Andrea Bolognani
2022-08-22 6:59 ` Victor Toso
2022-08-29 11:27 ` Markus Armbruster
2022-08-29 13:31 ` Victor Toso
2022-09-02 14:49 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 3/8] qapi: golang: Generate qapi's struct " Victor Toso
2022-06-17 14:41 ` Daniel P. Berrangé
2022-06-17 15:23 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 4/8] qapi: golang: Generate qapi's union " Victor Toso
2022-07-05 15:45 ` Andrea Bolognani
2022-07-05 16:35 ` Daniel P. Berrangé
2022-07-06 9:28 ` Andrea Bolognani
2022-07-06 9:37 ` Daniel P. Berrangé
2022-07-06 9:48 ` Daniel P. Berrangé
2022-07-06 12:20 ` Andrea Bolognani
2022-08-17 16:25 ` Victor Toso
2022-08-19 7:20 ` Victor Toso
2022-08-19 15:25 ` Andrea Bolognani
2022-08-22 6:33 ` Victor Toso
2022-08-17 16:06 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 5/8] qapi: golang: Generate qapi's event " Victor Toso
2022-07-05 15:45 ` Andrea Bolognani
2022-07-05 16:47 ` Daniel P. Berrangé [this message]
2022-07-06 14:53 ` Andrea Bolognani
2022-07-06 15:07 ` Daniel P. Berrangé
2022-07-06 16:22 ` Andrea Bolognani
2022-08-18 7:55 ` Victor Toso
2022-08-18 7:47 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 6/8] qapi: golang: Generate qapi's command " Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 7/8] qapi: golang: Add CommandResult type to Go Victor Toso
2022-07-05 15:46 ` Andrea Bolognani
2022-07-05 16:49 ` Daniel P. Berrangé
2022-08-17 15:00 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 8/8] qapi: golang: document skip function visit_array_types Victor Toso
2022-06-27 7:15 ` [RFC PATCH v2 0/8] qapi: add generator for Golang interface Markus Armbruster
2022-06-27 12:48 ` Victor Toso
2022-06-27 15:29 ` Markus Armbruster
2022-08-18 8:04 ` Victor Toso
2022-07-05 15:46 ` Andrea Bolognani
2022-08-17 14:24 ` Victor Toso
2022-08-29 11:53 ` Markus Armbruster
2022-08-29 14:05 ` Victor Toso
2024-11-07 10:43 ` Daniel P. Berrangé
2024-11-07 12:36 ` Markus Armbruster
2024-11-07 13:06 ` Daniel P. Berrangé
2024-11-07 13:35 ` Daniel P. Berrangé
2024-11-07 14:18 ` Markus Armbruster
2024-11-08 9:43 ` Victor Toso
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=YsRrHbNAZCjmQUcL@redhat.com \
--to=berrange@redhat.com \
--cc=abologna@redhat.com \
--cc=armbru@redhat.com \
--cc=eblake@redhat.com \
--cc=jsnow@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=victortoso@redhat.com \
/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).