From 2e19790f611cd15d603eac35bae46cb3c707ef85 Mon Sep 17 00:00:00 2001 From: Maisem Ali Date: Thu, 29 Jun 2023 08:06:57 -0700 Subject: [PATCH] types/views: add JSON marshal/unmarshal and AsMap to Map This allows cloning a Map as well as marshaling the Map as JSON. Updates tailscale/corp#12754 Signed-off-by: Maisem Ali --- cmd/derper/depaware.txt | 1 + cmd/tailscale/depaware.txt | 1 + cmd/tailscaled/depaware.txt | 2 +- types/views/views.go | 31 ++++++++++++++++++++++++++++--- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/cmd/derper/depaware.txt b/cmd/derper/depaware.txt index 5ee7a81ca..9bc358cc3 100644 --- a/cmd/derper/depaware.txt +++ b/cmd/derper/depaware.txt @@ -182,6 +182,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+ golang.org/x/exp/constraints from golang.org/x/exp/slices + golang.org/x/exp/maps from tailscale.com/types/views golang.org/x/exp/slices from tailscale.com/net/tsaddr+ L golang.org/x/net/bpf from github.com/mdlayher/netlink+ golang.org/x/net/dns/dnsmessage from net+ diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index 9a443042c..0432e4b7a 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -173,6 +173,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep golang.org/x/crypto/pbkdf2 from software.sslmate.com/src/go-pkcs12 golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+ golang.org/x/exp/constraints from golang.org/x/exp/slices + golang.org/x/exp/maps from tailscale.com/types/views golang.org/x/exp/slices from tailscale.com/net/tsaddr+ golang.org/x/net/bpf from github.com/mdlayher/netlink+ golang.org/x/net/dns/dnsmessage from net+ diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index f16a8c555..f9d82623f 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -379,7 +379,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+ LD golang.org/x/crypto/ssh from tailscale.com/ssh/tailssh+ golang.org/x/exp/constraints from golang.org/x/exp/slices+ - golang.org/x/exp/maps from tailscale.com/wgengine + golang.org/x/exp/maps from tailscale.com/wgengine+ golang.org/x/exp/slices from tailscale.com/ipn/ipnlocal+ golang.org/x/net/bpf from github.com/mdlayher/genetlink+ golang.org/x/net/dns/dnsmessage from net+ diff --git a/types/views/views.go b/types/views/views.go index edcd85189..512a48732 100644 --- a/types/views/views.go +++ b/types/views/views.go @@ -10,11 +10,12 @@ "errors" "net/netip" + "golang.org/x/exp/maps" "golang.org/x/exp/slices" "tailscale.com/net/tsaddr" ) -func unmarshalJSON[T any](b []byte, x *[]T) error { +func unmarshalSliceFromJSON[T any](b []byte, x *[]T) error { if *x != nil { return errors.New("already initialized") } @@ -64,7 +65,7 @@ type SliceView[T ViewCloner[T, V], V StructView[T]] struct { func (v SliceView[T, V]) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) } // UnmarshalJSON implements json.Unmarshaler. -func (v *SliceView[T, V]) UnmarshalJSON(b []byte) error { return unmarshalJSON(b, &v.ж) } +func (v *SliceView[T, V]) UnmarshalJSON(b []byte) error { return unmarshalSliceFromJSON(b, &v.ж) } // IsNil reports whether the underlying slice is nil. func (v SliceView[T, V]) IsNil() bool { return v.ж == nil } @@ -119,7 +120,7 @@ func (v Slice[T]) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements json.Unmarshaler. func (v *Slice[T]) UnmarshalJSON(b []byte) error { - return unmarshalJSON(b, &v.ж) + return unmarshalSliceFromJSON(b, &v.ж) } // IsNil reports whether the underlying slice is nil. @@ -332,6 +333,30 @@ func (m Map[K, V]) GetOk(k K) (V, bool) { return v, ok } +// MarshalJSON implements json.Marshaler. +func (m Map[K, V]) MarshalJSON() ([]byte, error) { + return json.Marshal(m.ж) +} + +// UnmarshalJSON implements json.Unmarshaler. +// It should only be called on an uninitialized Map. +func (m *Map[K, V]) UnmarshalJSON(b []byte) error { + if m.ж != nil { + return errors.New("already initialized") + } + return json.Unmarshal(b, &m.ж) +} + +// AsMap returns a shallow-clone of the underlying map. +// If V is a pointer type, it is the caller's responsibility to make sure +// the values are immutable. +func (m *Map[K, V]) AsMap() map[K]V { + if m == nil { + return nil + } + return maps.Clone(m.ж) +} + // MapRangeFn is the func called from a Map.Range call. // Implementations should return false to stop range. type MapRangeFn[K comparable, V any] func(k K, v V) (cont bool)