mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 11:05:45 +00:00
types/views: add iterators to the three Map view types
Their callers using Range are all kinda clunky feeling. Iterators should make them more readable. Updates #12912 Change-Id: I93461eba8e735276fda4a8558a4ae4bfd6c04922 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
f6d4d03355
commit
94c79659fa
@ -440,6 +440,17 @@ func (m MapSlice[K, V]) AsMap() map[K][]V {
|
||||
return out
|
||||
}
|
||||
|
||||
// All returns an iterator iterating over the keys and values of m.
|
||||
func (m MapSlice[K, V]) All() iter.Seq2[K, Slice[V]] {
|
||||
return func(yield func(K, Slice[V]) bool) {
|
||||
for k, v := range m.ж {
|
||||
if !yield(k, SliceOf(v)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map provides a read-only view of a map. It is the caller's responsibility to
|
||||
// make sure V is immutable.
|
||||
type Map[K comparable, V any] struct {
|
||||
@ -526,6 +537,18 @@ func (m Map[K, V]) Range(f MapRangeFn[K, V]) {
|
||||
}
|
||||
}
|
||||
|
||||
// All returns an iterator iterating over the keys
|
||||
// and values of m.
|
||||
func (m Map[K, V]) All() iter.Seq2[K, V] {
|
||||
return func(yield func(K, V) bool) {
|
||||
for k, v := range m.ж {
|
||||
if !yield(k, v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MapFnOf returns a MapFn for m.
|
||||
func MapFnOf[K comparable, T any, V any](m map[K]T, f func(T) V) MapFn[K, T, V] {
|
||||
return MapFn[K, T, V]{
|
||||
@ -587,6 +610,17 @@ func (m MapFn[K, T, V]) Range(f MapRangeFn[K, V]) {
|
||||
}
|
||||
}
|
||||
|
||||
// All returns an iterator iterating over the keys and value views of m.
|
||||
func (m MapFn[K, T, V]) All() iter.Seq2[K, V] {
|
||||
return func(yield func(K, V) bool) {
|
||||
for k, v := range m.ж {
|
||||
if !yield(k, m.wrapv(v)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ContainsPointers reports whether T contains any pointers,
|
||||
// either explicitly or implicitly.
|
||||
// It has special handling for some types that contain pointers
|
||||
|
@ -446,6 +446,7 @@ func (v testStructView) AsStruct() *testStruct {
|
||||
}
|
||||
return v.p.Clone()
|
||||
}
|
||||
func (v testStructView) ValueForTest() string { return v.p.value }
|
||||
|
||||
func TestSliceViewRange(t *testing.T) {
|
||||
vs := SliceOfViews([]*testStruct{{value: "foo"}, {value: "bar"}})
|
||||
@ -458,3 +459,45 @@ func TestSliceViewRange(t *testing.T) {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapIter(t *testing.T) {
|
||||
m := MapOf(map[string]int{"foo": 1, "bar": 2})
|
||||
var got []string
|
||||
for k, v := range m.All() {
|
||||
got = append(got, fmt.Sprintf("%s-%d", k, v))
|
||||
}
|
||||
slices.Sort(got)
|
||||
want := []string{"bar-2", "foo-1"}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapSliceIter(t *testing.T) {
|
||||
m := MapSliceOf(map[string][]int{"foo": {3, 4}, "bar": {1, 2}})
|
||||
var got []string
|
||||
for k, v := range m.All() {
|
||||
got = append(got, fmt.Sprintf("%s-%d", k, v))
|
||||
}
|
||||
slices.Sort(got)
|
||||
want := []string{"bar-{[1 2]}", "foo-{[3 4]}"}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapFnIter(t *testing.T) {
|
||||
m := MapFnOf[string, *testStruct, testStructView](map[string]*testStruct{
|
||||
"foo": {value: "fooVal"},
|
||||
"bar": {value: "barVal"},
|
||||
}, func(p *testStruct) testStructView { return testStructView{p} })
|
||||
var got []string
|
||||
for k, v := range m.All() {
|
||||
got = append(got, fmt.Sprintf("%v-%v", k, v.ValueForTest()))
|
||||
}
|
||||
slices.Sort(got)
|
||||
want := []string{"bar-barVal", "foo-fooVal"}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user