qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v1 3/9] qapi: golang: Generate qapi's struct types in Go


From: Victor Toso
Subject: Re: [PATCH v1 3/9] qapi: golang: Generate qapi's struct types in Go
Date: Fri, 29 Sep 2023 15:29:34 +0200

Hi,

On Thu, Sep 28, 2023 at 03:06:23PM +0100, Daniel P. Berrangé wrote:
> On Wed, Sep 27, 2023 at 01:25:38PM +0200, Victor Toso wrote:
> > This patch handles QAPI struct types and generates the equivalent
> > types in Go. The following patch adds extra logic when a member of the
> > struct has a Type that can take JSON Null value (e.g: StrOrNull in
> > QEMU)
> > 
> > The highlights of this implementation are:
> > 
> > 1. Generating an Go struct that requires a @base type, the @base type
> >    fields are copied over to the Go struct. The advantage of this
> >    approach is to not have embed structs in any of the QAPI types.
> >    Note that embedding a @base type is recursive, that is, if the
> >    @base type has a @base, all of those fields will be copied over.
> > 
> > 2. About the Go struct's fields:
> > 
> >    i) They can be either by Value or Reference.
> > 
> >   ii) Every field that is marked as optional in the QAPI specification
> >       are translated to Reference fields in its Go structure. This
> >       design decision is the most straightforward way to check if a
> >       given field was set or not. Exception only for types that can
> >       take JSON Null value.
> > 
> >  iii) Mandatory fields are always by Value with the exception of QAPI
> >       arrays, which are handled by Reference (to a block of memory) by
> >       Go.
> > 
> >   iv) All the fields are named with Uppercase due Golang's export
> >       convention.
> > 
> >    v) In order to avoid any kind of issues when encoding or decoding,
> >       to or from JSON, we mark all fields with its @name and, when it is
> >       optional, member, with @omitempty
> > 
> > Example:
> > 
> > qapi:
> >  | { 'struct': 'BlockdevCreateOptionsFile',
> >  |   'data': { 'filename': 'str',
> >  |             'size': 'size',
> >  |             '*preallocation': 'PreallocMode',
> >  |             '*nocow': 'bool',
> >  |             '*extent-size-hint': 'size'} }
> > 
> > go:
> > | type BlockdevCreateOptionsFile struct {
> > |     Filename       string        `json:"filename"`
> > |     Size           uint64        `json:"size"`
> > |     Preallocation  *PreallocMode `json:"preallocation,omitempty"`
> > |     Nocow          *bool         `json:"nocow,omitempty"`
> > |     ExtentSizeHint *uint64       `json:"extent-size-hint,omitempty"`
> > | }
> 
> Note, 'omitempty' shouldn't be used on pointer fields, only
> scalar fields. The pointer fields are always omitted when nil.

'omitempty' should be used with pointer fields unless you want to
Marshal JSON Null, which is not the expected output.

'omitempty' is used when QAPI member is said to be optional. This
is true for all optional members with the exception of two
Alternates: StrOrNull and BlockdevRefOrNull, as we _do_ want to
express JSON Null with them.

```Go
    package main

    import (
        "encoding/json"
        "fmt"
    )

    type Test1 struct {
        Foo *bool `json:"foo"`
        Bar *bool `json:"bar,omitempty"`
        Esc bool  `json:"esc"`
        Lar bool  `json:"lar,omitempty"`
    }

    type Test2 struct {
        Foo *uint64 `json:"foo"`
        Bar *uint64 `json:"bar,omitempty"`
        Esc uint64  `json:"esc"`
        Lar uint64  `json:"lar,omitempty"`
    }

    func printIt(s any) {
        if b, err := json.Marshal(s); err != nil {
            panic(err)
        } else {
            fmt.Println(string(b))
        }
    }

    func main() {
        printIt(Test1{})
        printIt(Test2{})
    }
```

```console
    toso@tapioca /tmp> go run main.go
    {"foo":null,"esc":false}
    {"foo":null,"esc":0}
```

Cheers,
Victor

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]