struct_map.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package prefs
  4. import (
  5. "maps"
  6. jsonv2 "github.com/go-json-experiment/json"
  7. "github.com/go-json-experiment/json/jsontext"
  8. "tailscale.com/types/opt"
  9. "tailscale.com/types/ptr"
  10. "tailscale.com/types/views"
  11. )
  12. // StructMap is a preference type that holds potentially mutable key-value pairs.
  13. type StructMap[K MapKeyType, V views.Cloner[V]] struct {
  14. preference[map[K]V]
  15. }
  16. // StructMapOf returns a [StructMap] configured with the specified value and [Options].
  17. func StructMapOf[K MapKeyType, V views.Cloner[V]](v map[K]V, opts ...Options) StructMap[K, V] {
  18. return StructMap[K, V]{preferenceOf(opt.ValueOf(deepCloneMap(v)), opts...)}
  19. }
  20. // StructMapWithOpts returns an unconfigured [StructMap] with the specified [Options].
  21. func StructMapWithOpts[K MapKeyType, V views.Cloner[V]](opts ...Options) StructMap[K, V] {
  22. return StructMap[K, V]{preferenceOf(opt.Value[map[K]V]{}, opts...)}
  23. }
  24. // SetValue configures the preference with the specified value.
  25. // It fails and returns [ErrManaged] if p is a managed preference,
  26. // and [ErrReadOnly] if p is a read-only preference.
  27. func (m *StructMap[K, V]) SetValue(val map[K]V) error {
  28. return m.preference.SetValue(deepCloneMap(val))
  29. }
  30. // SetManagedValue configures the preference with the specified value
  31. // and marks the preference as managed.
  32. func (m *StructMap[K, V]) SetManagedValue(val map[K]V) {
  33. m.preference.SetManagedValue(deepCloneMap(val))
  34. }
  35. // Clone returns a copy of m that aliases no memory with m.
  36. func (m StructMap[K, V]) Clone() *StructMap[K, V] {
  37. res := ptr.To(m)
  38. if v, ok := m.s.Value.GetOk(); ok {
  39. res.s.Value.Set(deepCloneMap(v))
  40. }
  41. return res
  42. }
  43. // Equal reports whether m and m2 are equal.
  44. // If the template type V implements an Equal(V) bool method, it will be used
  45. // instead of the == operator for value comparison.
  46. // It panics if T is not comparable.
  47. func (m StructMap[K, V]) Equal(m2 StructMap[K, V]) bool {
  48. if m.s.Metadata != m2.s.Metadata {
  49. return false
  50. }
  51. v1, ok1 := m.s.Value.GetOk()
  52. v2, ok2 := m2.s.Value.GetOk()
  53. if ok1 != ok2 {
  54. return false
  55. }
  56. return !ok1 || maps.EqualFunc(v1, v2, comparerFor[V]())
  57. }
  58. func deepCloneMap[K comparable, V views.Cloner[V]](m map[K]V) map[K]V {
  59. c := make(map[K]V, len(m))
  60. for i := range m {
  61. c[i] = m[i].Clone()
  62. }
  63. return c
  64. }
  65. // StructMapView is a read-only view of a [StructMap].
  66. type StructMapView[K MapKeyType, T views.ViewCloner[T, V], V views.StructView[T]] struct {
  67. // ж is the underlying mutable value, named with a hard-to-type
  68. // character that looks pointy like a pointer.
  69. // It is named distinctively to make you think of how dangerous it is to escape
  70. // to callers. You must not let callers be able to mutate it.
  71. ж *StructMap[K, T]
  72. }
  73. // StructMapViewOf returns a read-only view of m.
  74. // It is used by [tailscale.com/cmd/viewer].
  75. func StructMapViewOf[K MapKeyType, T views.ViewCloner[T, V], V views.StructView[T]](m *StructMap[K, T]) StructMapView[K, T, V] {
  76. return StructMapView[K, T, V]{m}
  77. }
  78. // Valid reports whether the underlying [StructMap] is non-nil.
  79. func (mv StructMapView[K, T, V]) Valid() bool {
  80. return mv.ж != nil
  81. }
  82. // AsStruct implements [views.StructView] by returning a clone of the preference
  83. // which aliases no memory with the original.
  84. func (mv StructMapView[K, T, V]) AsStruct() *StructMap[K, T] {
  85. if mv.ж == nil {
  86. return nil
  87. }
  88. return mv.ж.Clone()
  89. }
  90. // IsSet reports whether the preference has a value set.
  91. func (mv StructMapView[K, T, V]) IsSet() bool {
  92. return mv.ж.IsSet()
  93. }
  94. // Value returns a read-only view of the value if the preference has a value set.
  95. // Otherwise, it returns a read-only view of its default value.
  96. func (mv StructMapView[K, T, V]) Value() views.MapFn[K, T, V] {
  97. return views.MapFnOf(mv.ж.Value(), func(t T) V { return t.View() })
  98. }
  99. // ValueOk returns a read-only view of the value and true if the preference has a value set.
  100. // Otherwise, it returns an invalid view and false.
  101. func (mv StructMapView[K, T, V]) ValueOk() (val views.MapFn[K, T, V], ok bool) {
  102. if v, ok := mv.ж.ValueOk(); ok {
  103. return views.MapFnOf(v, func(t T) V { return t.View() }), true
  104. }
  105. return views.MapFn[K, T, V]{}, false
  106. }
  107. // DefaultValue returns a read-only view of the default value of the preference.
  108. func (mv StructMapView[K, T, V]) DefaultValue() views.MapFn[K, T, V] {
  109. return views.MapFnOf(mv.ж.DefaultValue(), func(t T) V { return t.View() })
  110. }
  111. // Managed reports whether the preference is managed via MDM, Group Policy, or similar means.
  112. func (mv StructMapView[K, T, V]) IsManaged() bool {
  113. return mv.ж.IsManaged()
  114. }
  115. // ReadOnly reports whether the preference is read-only and cannot be changed by user.
  116. func (mv StructMapView[K, T, V]) IsReadOnly() bool {
  117. return mv.ж.IsReadOnly()
  118. }
  119. // Equal reports whether mv and mv2 are equal.
  120. func (mv StructMapView[K, T, V]) Equal(mv2 StructMapView[K, T, V]) bool {
  121. if !mv.Valid() && !mv2.Valid() {
  122. return true
  123. }
  124. if mv.Valid() != mv2.Valid() {
  125. return false
  126. }
  127. return mv.ж.Equal(*mv2.ж)
  128. }
  129. // MarshalJSONTo implements [jsonv2.MarshalerTo].
  130. func (mv StructMapView[K, T, V]) MarshalJSONTo(out *jsontext.Encoder) error {
  131. return mv.ж.MarshalJSONTo(out)
  132. }
  133. // UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
  134. func (mv *StructMapView[K, T, V]) UnmarshalJSONFrom(in *jsontext.Decoder) error {
  135. var x StructMap[K, T]
  136. if err := x.UnmarshalJSONFrom(in); err != nil {
  137. return err
  138. }
  139. mv.ж = &x
  140. return nil
  141. }
  142. // MarshalJSON implements [json.Marshaler].
  143. func (mv StructMapView[K, T, V]) MarshalJSON() ([]byte, error) {
  144. return jsonv2.Marshal(mv) // uses MarshalJSONTo
  145. }
  146. // UnmarshalJSON implements [json.Unmarshaler].
  147. func (mv *StructMapView[K, T, V]) UnmarshalJSON(b []byte) error {
  148. return jsonv2.Unmarshal(b, mv) // uses UnmarshalJSONFrom
  149. }