From aacf1b1d47824de2c924874e3d6cdad1d8b5ba2b Mon Sep 17 00:00:00 2001 From: Přemysl Eric Janouch
Date: Thu, 7 Nov 2024 08:57:28 +0100 Subject: lxdrgen-go: improve usability Turning union tags into read-only methods of actual types: - eliminates duplicated JSON unmarshalling of tags, - makes AppendTo/ConsumeFrom symmetrical in nature, - eliminates duplicated AppendTo code, - eliminates trivial AppendTo methods for subtypes without fields, - gives us an opportunity to use a more specific interface than "any" (the type being anonymous is an acknowledged inconvenience). Implementing our own json.Marshalers some time ago (for performance reasons) has made this easier to implement. Also rename "Interface" fields to more suitable "Variant". --- tools/lxdrgen-go.awk | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) (limited to 'tools/lxdrgen-go.awk') diff --git a/tools/lxdrgen-go.awk b/tools/lxdrgen-go.awk index fb27c85..ac6cb8b 100644 --- a/tools/lxdrgen-go.awk +++ b/tools/lxdrgen-go.awk @@ -1,6 +1,6 @@ # lxdrgen-go.awk: Go backend for lxdrgen.awk. # -# Copyright (c) 2022, Přemysl Eric Janouch
+# Copyright (c) 2022 - 2024, Přemysl Eric Janouch
# SPDX-License-Identifier: 0BSD # # This backend also enables proxying to other endpoints using JSON. @@ -301,9 +301,11 @@ function codegen_marshal(type, f, marshal) { "\t}\n" } -function codegen_struct_field_marshal(d, cg, camel, f, marshal) { +function codegen_struct_field_marshal(d, cg, isaccessor, camel, f, marshal) { camel = snaketocamel(d["name"]) f = "s." camel + if (isaccessor) + f = f "()" if (!d["isarray"]) { append(cg, "marshal", "\tb = append(b, `,\"" decapitalize(camel) "\":`...)\n" \ @@ -333,7 +335,7 @@ function codegen_struct_field_marshal(d, cg, camel, f, marshal) { } function codegen_struct_field(d, cg, camel, f, serialize, deserialize) { - codegen_struct_field_marshal(d, cg) + codegen_struct_field_marshal(d, cg, 0) camel = snaketocamel(d["name"]) f = "s." camel @@ -384,15 +386,11 @@ function codegen_struct_field(d, cg, camel, f, serialize, deserialize) { } } -function codegen_struct_tag(d, cg, camel, f) { - codegen_struct_field_marshal(d, cg) +function codegen_struct_tag(d, cg) { + codegen_struct_field_marshal(d, cg, 1) - camel = snaketocamel(d["name"]) - f = "s." camel - append(cg, "fields", "\t" camel " " CodegenGoType[d["type"]] \ - " `json:\"" decapitalize(camel) "\"`\n") - append(cg, "serialize", sprintf(CodegenSerialize[d["type"]], f)) - # Do not deserialize here, that is already done by the containing union. + # Do not serialize or deserialize here, + # that is already done by the containing union. } function codegen_struct(name, cg, gotype) { @@ -450,13 +448,18 @@ function codegen_union_struct(name, casename, cg, scg, structname, init) { structname = name snaketocamel(casename) codegen_struct(structname, scg) - init = CodegenGoType[structname] "{" cg["tagname"] \ - ": " decapitalize(cg["tagname"]) "}" + print "func (u *" CodegenGoType[structname] ") " cg["tagname"] "() " \ + CodegenGoType[cg["tagtype"]] " {" + print "\treturn " CodegenGoType[cg["tagtype"]] snaketocamel(casename) + print "}" + print "" + + init = CodegenGoType[structname] "{}" append(cg, "unmarshal", "\tcase " CodegenGoType[cg["tagtype"]] snaketocamel(casename) ":\n" \ "\t\ts := " init "\n" \ "\t\terr = json.Unmarshal(data, &s)\n" \ - "\t\tu.Interface = &s\n") + "\t\tu.Variant = &s\n") append(cg, "serialize", "\tcase *" CodegenGoType[structname] ":\n" \ indent(sprintf(CodegenSerialize[structname], "union"))) @@ -464,20 +467,23 @@ function codegen_union_struct(name, casename, cg, scg, structname, init) { "\tcase " CodegenGoType[cg["tagtype"]] snaketocamel(casename) ":\n" \ "\t\ts := " init "\n" \ indent(sprintf(CodegenDeserialize[structname], "s")) \ - "\t\tu.Interface = &s\n") + "\t\tu.Variant = &s\n") } function codegen_union(name, cg, exhaustive, gotype, tagvar) { gotype = PrefixCamel name + # This must be a struct, so that UnmarshalJSON can create concrete types. print "type " gotype " struct {" - print "\tInterface any" + print "\tVariant interface {" + print "\t\t" cg["tagname"] "() " CodegenGoType[cg["tagtype"]] + print "\t}" print "}" print "" # This cannot be a pointer method, it wouldn't work recursively. CodegenIsMarshaler[name] = 1 print "func (u " gotype ") MarshalJSON() ([]byte, error) {" - print "\treturn u.Interface.(json.Marshaler).MarshalJSON()" + print "\treturn u.Variant.(json.Marshaler).MarshalJSON()" print "}" print "" @@ -499,13 +505,15 @@ function codegen_union(name, cg, exhaustive, gotype, tagvar) { print "}" print "" - # XXX: Consider changing the interface into an AppendTo/ConsumeFrom one, - # that would eliminate these type case switches entirely. - # On the other hand, it would make it possible to send unsuitable structs. + # XXX: Consider rather testing the type for having an AppendTo method, + # which would eliminate this type case switch entirely. print "func (u *" gotype ") AppendTo(data []byte) ([]byte, bool) {" print "\tok := true" - print "\tswitch union := u.Interface.(type) {" + print sprintf(CodegenSerialize[cg["tagtype"]], + "u.Variant." cg["tagname"] "()") \ + "\tswitch union := u.Variant.(type) {" print cg["serialize"] "\tdefault:" + print "\t\t_ = union" print "\t\treturn nil, false" print "\t}" print "\treturn data, ok" -- cgit v1.2.3-70-g09d2