Browse Source

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 <[email protected]>
Maisem Ali 2 years ago
parent
commit
2e19790f61
4 changed files with 31 additions and 4 deletions
  1. 1 0
      cmd/derper/depaware.txt
  2. 1 0
      cmd/tailscale/depaware.txt
  3. 1 1
      cmd/tailscaled/depaware.txt
  4. 28 3
      types/views/views.go

+ 1 - 0
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+

+ 1 - 0
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+

+ 1 - 1
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+

+ 28 - 3
types/views/views.go

@@ -10,11 +10,12 @@ import (
 	"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)