summaryrefslogtreecommitdiffhomepage
path: root/types/prefs/map.go
blob: 6bf1948b87ab4810f0926fc1074523c6a55d1b94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause

package prefs

import (
	"maps"
	"net/netip"

	jsonv2 "github.com/go-json-experiment/json"
	"github.com/go-json-experiment/json/jsontext"
	"golang.org/x/exp/constraints"
	"tailscale.com/types/opt"
	"tailscale.com/types/ptr"
	"tailscale.com/types/views"
)

// MapKeyType is a constraint allowing types that can be used as [Map] and [StructMap] keys.
// To satisfy this requirement, a type must be comparable and must encode as a JSON string.
// See [jsonv2.Marshal] for more details.
type MapKeyType interface {
	~string | constraints.Integer | netip.Addr | netip.Prefix | netip.AddrPort
}

// Map is a preference type that holds immutable key-value pairs.
type Map[K MapKeyType, V ImmutableType] struct {
	preference[map[K]V]
}

// MapOf returns a map configured with the specified value and [Options].
func MapOf[K MapKeyType, V ImmutableType](v map[K]V, opts ...Options) Map[K, V] {
	return Map[K, V]{preferenceOf(opt.ValueOf(v), opts...)}
}

// MapWithOpts returns an unconfigured [Map] with the specified [Options].
func MapWithOpts[K MapKeyType, V ImmutableType](opts ...Options) Map[K, V] {
	return Map[K, V]{preferenceOf(opt.Value[map[K]V]{}, opts...)}
}

// View returns a read-only view of m.
func (m *Map[K, V]) View() MapView[K, V] {
	return MapView[K, V]{m}
}

// Clone returns a copy of m that aliases no memory with m.
func (m Map[K, V]) Clone() *Map[K, V] {
	res := ptr.To(m)
	if v, ok := m.s.Value.GetOk(); ok {
		res.s.Value.Set(maps.Clone(v))
	}
	return res
}

// Equal reports whether m and m2 are equal.
func (m Map[K, V]) Equal(m2 Map[K, V]) bool {
	if m.s.Metadata != m2.s.Metadata {
		return false
	}
	v1, ok1 := m.s.Value.GetOk()
	v2, ok2 := m2.s.Value.GetOk()
	if ok1 != ok2 {
		return false
	}
	return !ok1 || maps.Equal(v1, v2)
}

// MapView is a read-only view of a [Map].
type MapView[K MapKeyType, V ImmutableType] struct {
	// ж is the underlying mutable value, named with a hard-to-type
	// character that looks pointy like a pointer.
	// It is named distinctively to make you think of how dangerous it is to escape
	// to callers. You must not let callers be able to mutate it.
	ж *Map[K, V]
}

// Valid reports whether the underlying [Map] is non-nil.
func (mv MapView[K, V]) Valid() bool {
	return mv.ж != nil
}

// AsStruct implements [views.StructView] by returning a clone of the [Map]
// which aliases no memory with the original.
func (mv MapView[K, V]) AsStruct() *Map[K, V] {
	if mv.ж == nil {
		return nil
	}
	return mv.ж.Clone()
}

// IsSet reports whether the preference has a value set.
func (mv MapView[K, V]) IsSet() bool {
	return mv.ж.IsSet()
}

// Value returns a read-only view of the value if the preference has a value set.
// Otherwise, it returns a read-only view of its default value.
func (mv MapView[K, V]) Value() views.Map[K, V] {
	return views.MapOf(mv.ж.Value())
}

// ValueOk returns a read-only view of the value and true if the preference has a value set.
// Otherwise, it returns an invalid view and false.
func (mv MapView[K, V]) ValueOk() (val views.Map[K, V], ok bool) {
	if v, ok := mv.ж.ValueOk(); ok {
		return views.MapOf(v), true
	}
	return views.Map[K, V]{}, false
}

// DefaultValue returns a read-only view of the default value of the preference.
func (mv MapView[K, V]) DefaultValue() views.Map[K, V] {
	return views.MapOf(mv.ж.DefaultValue())
}

// Managed reports whether the preference is managed via MDM, Group Policy, or similar means.
func (mv MapView[K, V]) Managed() bool {
	return mv.ж.IsManaged()
}

// ReadOnly reports whether the preference is read-only and cannot be changed by user.
func (mv MapView[K, V]) ReadOnly() bool {
	return mv.ж.IsReadOnly()
}

// Equal reports whether mv and mv2 are equal.
func (mv MapView[K, V]) Equal(mv2 MapView[K, V]) bool {
	if !mv.Valid() && !mv2.Valid() {
		return true
	}
	if mv.Valid() != mv2.Valid() {
		return false
	}
	return mv.ж.Equal(*mv2.ж)
}

// MarshalJSONTo implements [jsonv2.MarshalerTo].
func (mv MapView[K, V]) MarshalJSONTo(out *jsontext.Encoder) error {
	return mv.ж.MarshalJSONTo(out)
}

// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
func (mv *MapView[K, V]) UnmarshalJSONFrom(in *jsontext.Decoder) error {
	var x Map[K, V]
	if err := x.UnmarshalJSONFrom(in); err != nil {
		return err
	}
	mv.ж = &x
	return nil
}

// MarshalJSON implements [json.Marshaler].
func (mv MapView[K, V]) MarshalJSON() ([]byte, error) {
	return jsonv2.Marshal(mv) // uses MarshalJSONTo
}

// UnmarshalJSON implements [json.Unmarshaler].
func (mv *MapView[K, V]) UnmarshalJSON(b []byte) error {
	return jsonv2.Unmarshal(b, mv) // uses UnmarshalJSONFrom
}