From 8841fd58c95cd335c5cf531ad08e978ca9c87c76 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Thu, 31 Aug 2023 13:02:58 -0700 Subject: [PATCH] cmd/viewer: support v2 JSON methods This links in github.com/go-json-experiment/json into tailscaled. After this change, the tailscaled binary on GOOS=linux and GOARCH=amd64 increases by ~85KiB. The v2 marshal/unmarshal methods avoids a O(n^2) behavior with deeply nested v1 MarshalJSON and UnmarshalJSON calls, since each call requires the encoding/json package to rescan the entire JSON value. Our data structures are not so deep that the O(n^2) behavior becomes notable, but this does provide about a ~20% performance benefit. Updates tailscale/corp#14379 Signed-off-by: Joe Tsai --- cmd/derper/depaware.txt | 10 +- cmd/netlogfmt/main.go | 9 +- cmd/tailscale/depaware.txt | 8 +- cmd/tailscaled/depaware.txt | 8 +- cmd/viewer/viewer.go | 36 +++- go.mod | 2 +- go.sum | 4 +- tailcfg/tailcfg.go | 2 +- tailcfg/tailcfg_view.go | 420 ++++++++++++++++++++++++++++++++++++ util/codegen/codegen.go | 22 +- 10 files changed, 502 insertions(+), 19 deletions(-) diff --git a/cmd/derper/depaware.txt b/cmd/derper/depaware.txt index 238e0cf76..a618bf068 100644 --- a/cmd/derper/depaware.txt +++ b/cmd/derper/depaware.txt @@ -15,6 +15,12 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw W 💣 github.com/dblohm7/wingoes from tailscale.com/util/winutil github.com/fxamacker/cbor/v2 from tailscale.com/tka + github.com/go-json-experiment/json from tailscale.com/tailcfg + github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/internal/jsonflags from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/internal/jsonopts from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/internal/jsonwire from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/jsontext from github.com/go-json-experiment/json+ github.com/golang/groupcache/lru from tailscale.com/net/dnscache github.com/golang/protobuf/proto from github.com/matttproud/golang_protobuf_extensions/pbutil+ L github.com/google/nftables from tailscale.com/util/linuxfw @@ -191,7 +197,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa golang.org/x/time/rate from tailscale.com/cmd/derper+ bufio from compress/flate+ bytes from bufio+ - cmp from slices + cmp from slices+ compress/flate from compress/gzip+ compress/gzip from internal/profile+ container/list from crypto/tls+ @@ -220,7 +226,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa embed from crypto/internal/nistec+ encoding from encoding/json+ encoding/asn1 from crypto/x509+ - encoding/base32 from tailscale.com/tka + encoding/base32 from tailscale.com/tka+ encoding/base64 from encoding/json+ encoding/binary from compress/gzip+ encoding/hex from crypto/x509+ diff --git a/cmd/netlogfmt/main.go b/cmd/netlogfmt/main.go index 98c958305..e1a5c2cb2 100644 --- a/cmd/netlogfmt/main.go +++ b/cmd/netlogfmt/main.go @@ -42,6 +42,7 @@ import ( "github.com/dsnet/try" jsonv2 "github.com/go-json-experiment/json" + "github.com/go-json-experiment/json/jsontext" "tailscale.com/types/logid" "tailscale.com/types/netlogtype" "tailscale.com/util/cmpx" @@ -75,13 +76,13 @@ func main() { func processStream(r io.Reader) (err error) { defer try.Handle(&err) - dec := jsonv2.NewDecoder(os.Stdin) + dec := jsontext.NewDecoder(os.Stdin) for { processValue(dec) } } -func processValue(dec *jsonv2.Decoder) { +func processValue(dec *jsontext.Decoder) { switch dec.PeekKind() { case '[': processArray(dec) @@ -92,7 +93,7 @@ func processValue(dec *jsonv2.Decoder) { } } -func processArray(dec *jsonv2.Decoder) { +func processArray(dec *jsontext.Decoder) { try.E1(dec.ReadToken()) // parse '[' for dec.PeekKind() != ']' { processValue(dec) @@ -100,7 +101,7 @@ func processArray(dec *jsonv2.Decoder) { try.E1(dec.ReadToken()) // parse ']' } -func processObject(dec *jsonv2.Decoder) { +func processObject(dec *jsontext.Decoder) { var hasTraffic bool var rawMsg []byte try.E1(dec.ReadToken()) // parse '{' diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index 16e3121ce..4c12ceecd 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -15,6 +15,12 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep W 💣 github.com/dblohm7/wingoes from tailscale.com/util/winutil/authenticode+ W 💣 github.com/dblohm7/wingoes/pe from tailscale.com/util/winutil/authenticode github.com/fxamacker/cbor/v2 from tailscale.com/tka + github.com/go-json-experiment/json from tailscale.com/tailcfg + github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/internal/jsonflags from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/internal/jsonopts from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/internal/jsonwire from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/jsontext from github.com/go-json-experiment/json+ L 💣 github.com/godbus/dbus/v5 from github.com/coreos/go-systemd/v22/dbus github.com/golang/groupcache/lru from tailscale.com/net/dnscache L github.com/google/nftables from tailscale.com/util/linuxfw @@ -205,7 +211,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep archive/tar from tailscale.com/clientupdate bufio from compress/flate+ bytes from bufio+ - cmp from slices + cmp from slices+ compress/flate from compress/gzip+ compress/gzip from net/http+ compress/zlib from image/png+ diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index 4e49741a5..0eab53aaa 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -84,6 +84,12 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de W github.com/dblohm7/wingoes/internal from github.com/dblohm7/wingoes/com W 💣 github.com/dblohm7/wingoes/pe from tailscale.com/util/osdiag+ github.com/fxamacker/cbor/v2 from tailscale.com/tka + github.com/go-json-experiment/json from tailscale.com/tailcfg + github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/internal/jsonflags from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/internal/jsonopts from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/internal/jsonwire from github.com/go-json-experiment/json+ + github.com/go-json-experiment/json/jsontext from github.com/go-json-experiment/json+ W 💣 github.com/go-ole/go-ole from github.com/go-ole/go-ole/oleutil+ W 💣 github.com/go-ole/go-ole/oleutil from tailscale.com/wgengine/winnet L 💣 github.com/godbus/dbus/v5 from tailscale.com/net/dns+ @@ -418,7 +424,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de archive/tar from tailscale.com/clientupdate bufio from compress/flate+ bytes from bufio+ - cmp from slices + cmp from slices+ compress/flate from compress/gzip+ compress/gzip from golang.org/x/net/http2+ W compress/zlib from debug/pe diff --git a/cmd/viewer/viewer.go b/cmd/viewer/viewer.go index b9c01a9b8..5ce55dabb 100644 --- a/cmd/viewer/viewer.go +++ b/cmd/viewer/viewer.go @@ -64,6 +64,30 @@ func (v *{{.ViewName}}) UnmarshalJSON(b []byte) error { return nil } +{{if .SupportJSONV2}} +// Verify that {{.ViewName}} implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*{{.ViewName}})(nil) + _ (jsonv2.UnmarshalerV2) = (*{{.ViewName}})(nil) +) + +func (v {{.ViewName}}) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *{{.ViewName}}) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x {{.StructName}} + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} +{{end}} + {{end}} {{define "valueField"}}func (v {{.ViewName}}) {{.FieldName}}() {{.FieldType}} { return v.ж.{{.FieldName}} } {{end}} @@ -126,6 +150,10 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, thi } it.Import("encoding/json") it.Import("errors") + if *jsonv2Methods { + it.ImportNamed("github.com/go-json-experiment/json", "jsonv2") + it.Import("github.com/go-json-experiment/json/jsontext") + } args := struct { StructName string @@ -138,9 +166,12 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, thi MapValueType string MapValueView string MapFn string + + SupportJSONV2 bool }{ - StructName: typ.Obj().Name(), - ViewName: typ.Obj().Name() + "View", + StructName: typ.Obj().Name(), + ViewName: typ.Obj().Name() + "View", + SupportJSONV2: *jsonv2Methods, } writeTemplate := func(name string) { @@ -321,6 +352,7 @@ var ( flagTypes = flag.String("type", "", "comma-separated list of types; required") flagBuildTags = flag.String("tags", "", "compiler build tags to apply") flagCloneFunc = flag.Bool("clonefunc", false, "add a top-level Clone func") + jsonv2Methods = flag.Bool("jsonv2method", false, "add marshal/unmarshal methods for JSONv2") flagCloneOnlyTypes = flag.String("clone-only-type", "", "comma-separated list of types (a subset of --type) that should only generate a go:generate clone line and not actual views") ) diff --git a/go.mod b/go.mod index 86e202ec8..31024f61d 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/evanw/esbuild v0.14.53 github.com/frankban/quicktest v1.14.5 github.com/fxamacker/cbor/v2 v2.4.0 - github.com/go-json-experiment/json v0.0.0-20230321051131-ccbac49a6929 + github.com/go-json-experiment/json v0.0.0-20230831193458-5df0a50228ea github.com/go-logr/zapr v1.2.4 github.com/go-ole/go-ole v1.2.6 github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 diff --git a/go.sum b/go.sum index 3d6a4720b..8d42fec30 100644 --- a/go.sum +++ b/go.sum @@ -299,8 +299,8 @@ github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhc github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-json-experiment/json v0.0.0-20230321051131-ccbac49a6929 h1:GdbUZo0+623j+pKRhwwdf1q28IUgRc7asx3TjF9b7VQ= -github.com/go-json-experiment/json v0.0.0-20230321051131-ccbac49a6929/go.mod h1:AHV+bpNGVGD0DCHMBhhTYtT7yeBYD9Yk92XAjB7vOgo= +github.com/go-json-experiment/json v0.0.0-20230831193458-5df0a50228ea h1:EFOg7Pq/YiznRzn/7udE/cuT1swauYdQ5ljDK93jBNc= +github.com/go-json-experiment/json v0.0.0-20230831193458-5df0a50228ea/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 501a6bf62..4ea43c650 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -3,7 +3,7 @@ package tailcfg -//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan,Location,UserProfile --clonefunc +//go:generate go run tailscale.com/cmd/viewer --type=User,Node,Hostinfo,NetInfo,Login,DNSConfig,RegisterResponse,RegisterResponseAuth,RegisterRequest,DERPHomeParams,DERPRegion,DERPMap,DERPNode,SSHRule,SSHAction,SSHPrincipal,ControlDialPlan,Location,UserProfile --clonefunc --jsonv2method import ( "bytes" diff --git a/tailcfg/tailcfg_view.go b/tailcfg/tailcfg_view.go index 0b9250412..de82e13db 100644 --- a/tailcfg/tailcfg_view.go +++ b/tailcfg/tailcfg_view.go @@ -11,6 +11,8 @@ import ( "net/netip" "time" + jsonv2 "github.com/go-json-experiment/json" + "github.com/go-json-experiment/json/jsontext" "tailscale.com/types/dnstype" "tailscale.com/types/key" "tailscale.com/types/opt" @@ -66,6 +68,28 @@ func (v *UserView) UnmarshalJSON(b []byte) error { return nil } +// Verify that UserView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*UserView)(nil) + _ (jsonv2.UnmarshalerV2) = (*UserView)(nil) +) + +func (v UserView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *UserView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x User + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v UserView) ID() UserID { return v.ж.ID } func (v UserView) LoginName() string { return v.ж.LoginName } func (v UserView) DisplayName() string { return v.ж.DisplayName } @@ -128,6 +152,28 @@ func (v *NodeView) UnmarshalJSON(b []byte) error { return nil } +// Verify that NodeView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*NodeView)(nil) + _ (jsonv2.UnmarshalerV2) = (*NodeView)(nil) +) + +func (v NodeView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *NodeView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x Node + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v NodeView) ID() NodeID { return v.ж.ID } func (v NodeView) StableID() StableNodeID { return v.ж.StableID } func (v NodeView) Name() string { return v.ж.Name } @@ -263,6 +309,28 @@ func (v *HostinfoView) UnmarshalJSON(b []byte) error { return nil } +// Verify that HostinfoView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*HostinfoView)(nil) + _ (jsonv2.UnmarshalerV2) = (*HostinfoView)(nil) +) + +func (v HostinfoView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *HostinfoView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x Hostinfo + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v HostinfoView) IPNVersion() string { return v.ж.IPNVersion } func (v HostinfoView) FrontendLogID() string { return v.ж.FrontendLogID } func (v HostinfoView) BackendLogID() string { return v.ж.BackendLogID } @@ -389,6 +457,28 @@ func (v *NetInfoView) UnmarshalJSON(b []byte) error { return nil } +// Verify that NetInfoView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*NetInfoView)(nil) + _ (jsonv2.UnmarshalerV2) = (*NetInfoView)(nil) +) + +func (v NetInfoView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *NetInfoView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x NetInfo + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v NetInfoView) MappingVariesByDestIP() opt.Bool { return v.ж.MappingVariesByDestIP } func (v NetInfoView) HairPinning() opt.Bool { return v.ж.HairPinning } func (v NetInfoView) WorkingIPv6() opt.Bool { return v.ж.WorkingIPv6 } @@ -469,6 +559,28 @@ func (v *LoginView) UnmarshalJSON(b []byte) error { return nil } +// Verify that LoginView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*LoginView)(nil) + _ (jsonv2.UnmarshalerV2) = (*LoginView)(nil) +) + +func (v LoginView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *LoginView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x Login + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v LoginView) ID() LoginID { return v.ж.ID } func (v LoginView) Provider() string { return v.ж.Provider } func (v LoginView) LoginName() string { return v.ж.LoginName } @@ -530,6 +642,28 @@ func (v *DNSConfigView) UnmarshalJSON(b []byte) error { return nil } +// Verify that DNSConfigView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*DNSConfigView)(nil) + _ (jsonv2.UnmarshalerV2) = (*DNSConfigView)(nil) +) + +func (v DNSConfigView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *DNSConfigView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x DNSConfig + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v DNSConfigView) Resolvers() views.SliceView[*dnstype.Resolver, dnstype.ResolverView] { return views.SliceOfViews[*dnstype.Resolver, dnstype.ResolverView](v.ж.Resolvers) } @@ -611,6 +745,28 @@ func (v *RegisterResponseView) UnmarshalJSON(b []byte) error { return nil } +// Verify that RegisterResponseView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*RegisterResponseView)(nil) + _ (jsonv2.UnmarshalerV2) = (*RegisterResponseView)(nil) +) + +func (v RegisterResponseView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *RegisterResponseView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x RegisterResponse + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v RegisterResponseView) User() UserView { return v.ж.User.View() } func (v RegisterResponseView) Login() Login { return v.ж.Login } func (v RegisterResponseView) NodeKeyExpired() bool { return v.ж.NodeKeyExpired } @@ -677,6 +833,28 @@ func (v *RegisterResponseAuthView) UnmarshalJSON(b []byte) error { return nil } +// Verify that RegisterResponseAuthView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*RegisterResponseAuthView)(nil) + _ (jsonv2.UnmarshalerV2) = (*RegisterResponseAuthView)(nil) +) + +func (v RegisterResponseAuthView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *RegisterResponseAuthView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x RegisterResponseAuth + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v RegisterResponseAuthView) Provider() string { return v.ж.Provider } func (v RegisterResponseAuthView) LoginName() string { return v.ж.LoginName } func (v RegisterResponseAuthView) Oauth2Token() *Oauth2Token { @@ -743,6 +921,28 @@ func (v *RegisterRequestView) UnmarshalJSON(b []byte) error { return nil } +// Verify that RegisterRequestView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*RegisterRequestView)(nil) + _ (jsonv2.UnmarshalerV2) = (*RegisterRequestView)(nil) +) + +func (v RegisterRequestView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *RegisterRequestView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x RegisterRequest + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v RegisterRequestView) Version() CapabilityVersion { return v.ж.Version } func (v RegisterRequestView) NodeKey() key.NodePublic { return v.ж.NodeKey } func (v RegisterRequestView) OldNodeKey() key.NodePublic { return v.ж.OldNodeKey } @@ -835,6 +1035,28 @@ func (v *DERPHomeParamsView) UnmarshalJSON(b []byte) error { return nil } +// Verify that DERPHomeParamsView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*DERPHomeParamsView)(nil) + _ (jsonv2.UnmarshalerV2) = (*DERPHomeParamsView)(nil) +) + +func (v DERPHomeParamsView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *DERPHomeParamsView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x DERPHomeParams + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v DERPHomeParamsView) RegionScore() views.Map[int, float64] { return views.MapOf(v.ж.RegionScore) } @@ -889,6 +1111,28 @@ func (v *DERPRegionView) UnmarshalJSON(b []byte) error { return nil } +// Verify that DERPRegionView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*DERPRegionView)(nil) + _ (jsonv2.UnmarshalerV2) = (*DERPRegionView)(nil) +) + +func (v DERPRegionView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *DERPRegionView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x DERPRegion + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v DERPRegionView) RegionID() int { return v.ж.RegionID } func (v DERPRegionView) RegionCode() string { return v.ж.RegionCode } func (v DERPRegionView) RegionName() string { return v.ж.RegionName } @@ -951,6 +1195,28 @@ func (v *DERPMapView) UnmarshalJSON(b []byte) error { return nil } +// Verify that DERPMapView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*DERPMapView)(nil) + _ (jsonv2.UnmarshalerV2) = (*DERPMapView)(nil) +) + +func (v DERPMapView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *DERPMapView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x DERPMap + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v DERPMapView) HomeParams() DERPHomeParamsView { return v.ж.HomeParams.View() } func (v DERPMapView) Regions() views.MapFn[int, *DERPRegion, DERPRegionView] { @@ -1012,6 +1278,28 @@ func (v *DERPNodeView) UnmarshalJSON(b []byte) error { return nil } +// Verify that DERPNodeView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*DERPNodeView)(nil) + _ (jsonv2.UnmarshalerV2) = (*DERPNodeView)(nil) +) + +func (v DERPNodeView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *DERPNodeView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x DERPNode + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v DERPNodeView) Name() string { return v.ж.Name } func (v DERPNodeView) RegionID() int { return v.ж.RegionID } func (v DERPNodeView) HostName() string { return v.ж.HostName } @@ -1086,6 +1374,28 @@ func (v *SSHRuleView) UnmarshalJSON(b []byte) error { return nil } +// Verify that SSHRuleView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*SSHRuleView)(nil) + _ (jsonv2.UnmarshalerV2) = (*SSHRuleView)(nil) +) + +func (v SSHRuleView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *SSHRuleView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x SSHRule + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v SSHRuleView) RuleExpires() *time.Time { if v.ж.RuleExpires == nil { return nil @@ -1154,6 +1464,28 @@ func (v *SSHActionView) UnmarshalJSON(b []byte) error { return nil } +// Verify that SSHActionView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*SSHActionView)(nil) + _ (jsonv2.UnmarshalerV2) = (*SSHActionView)(nil) +) + +func (v SSHActionView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *SSHActionView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x SSHAction + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v SSHActionView) Message() string { return v.ж.Message } func (v SSHActionView) Reject() bool { return v.ж.Reject } func (v SSHActionView) Accept() bool { return v.ж.Accept } @@ -1230,6 +1562,28 @@ func (v *SSHPrincipalView) UnmarshalJSON(b []byte) error { return nil } +// Verify that SSHPrincipalView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*SSHPrincipalView)(nil) + _ (jsonv2.UnmarshalerV2) = (*SSHPrincipalView)(nil) +) + +func (v SSHPrincipalView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *SSHPrincipalView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x SSHPrincipal + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v SSHPrincipalView) Node() StableNodeID { return v.ж.Node } func (v SSHPrincipalView) NodeIP() string { return v.ж.NodeIP } func (v SSHPrincipalView) UserLogin() string { return v.ж.UserLogin } @@ -1290,6 +1644,28 @@ func (v *ControlDialPlanView) UnmarshalJSON(b []byte) error { return nil } +// Verify that ControlDialPlanView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*ControlDialPlanView)(nil) + _ (jsonv2.UnmarshalerV2) = (*ControlDialPlanView)(nil) +) + +func (v ControlDialPlanView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *ControlDialPlanView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x ControlDialPlan + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v ControlDialPlanView) Candidates() views.Slice[ControlIPCandidate] { return views.SliceOf(v.ж.Candidates) } @@ -1344,6 +1720,28 @@ func (v *LocationView) UnmarshalJSON(b []byte) error { return nil } +// Verify that LocationView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*LocationView)(nil) + _ (jsonv2.UnmarshalerV2) = (*LocationView)(nil) +) + +func (v LocationView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *LocationView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x Location + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v LocationView) Country() string { return v.ж.Country } func (v LocationView) CountryCode() string { return v.ж.CountryCode } func (v LocationView) City() string { return v.ж.City } @@ -1404,6 +1802,28 @@ func (v *UserProfileView) UnmarshalJSON(b []byte) error { return nil } +// Verify that UserProfileView implements jsonv2 interfaces. +var ( + _ (jsonv2.MarshalerV2) = (*UserProfileView)(nil) + _ (jsonv2.UnmarshalerV2) = (*UserProfileView)(nil) +) + +func (v UserProfileView) MarshalJSONV2(enc *jsontext.Encoder, opts jsonv2.Options) error { + return jsonv2.MarshalEncode(enc, v.ж, opts) +} + +func (v *UserProfileView) UnmarshalJSONV2(dec *jsontext.Decoder, opts jsonv2.Options) error { + if v.ж != nil { + return errors.New("already initialized") + } + var x UserProfile + if err := jsonv2.UnmarshalDecode(dec, &x, opts); err != nil { + return err + } + v.ж = &x + return nil +} + func (v UserProfileView) ID() UserID { return v.ж.ID } func (v UserProfileView) LoginName() string { return v.ж.LoginName } func (v UserProfileView) DisplayName() string { return v.ж.DisplayName } diff --git a/util/codegen/codegen.go b/util/codegen/codegen.go index cf848b1d2..e560bfb00 100644 --- a/util/codegen/codegen.go +++ b/util/codegen/codegen.go @@ -74,12 +74,20 @@ func NewImportTracker(thisPkg *types.Package) *ImportTracker { // ImportTracker provides a mechanism to track and build import paths. type ImportTracker struct { thisPkg *types.Package - packages map[string]bool + packages map[string]string // package paths to package names; empty name to use default } func (it *ImportTracker) Import(pkg string) { - if pkg != "" && !it.packages[pkg] { - mak.Set(&it.packages, pkg, true) + _, ok := it.packages[pkg] + if pkg != "" && !ok { + mak.Set(&it.packages, pkg, "") + } +} + +func (it *ImportTracker) ImportNamed(pkg, name string) { + _, ok := it.packages[pkg] + if pkg != "" && !ok { + mak.Set(&it.packages, pkg, name) } } @@ -100,8 +108,12 @@ func (it *ImportTracker) QualifiedName(t types.Type) string { // Write prints all the tracked imports in a single import block to w. func (it *ImportTracker) Write(w io.Writer) { fmt.Fprintf(w, "import (\n") - for s := range it.packages { - fmt.Fprintf(w, "\t%q\n", s) + for pkgPath, pkgName := range it.packages { + if pkgName == "" { + fmt.Fprintf(w, "\t%q\n", pkgPath) + } else { + fmt.Fprintf(w, "\t%s %q\n", pkgName, pkgPath) + } } fmt.Fprintf(w, ")\n\n") }