qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
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 :|



  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).