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 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
package arrayiter
import (
"context"
"fmt"
"reflect"
"sync"
)
func Iterate(ctx context.Context, a interface{}) (Iterator, error) {
arv := reflect.ValueOf(a)
switch arv.Kind() {
case reflect.Array, reflect.Slice:
default:
return nil, fmt.Errorf(`argument must be an array/slice (%s)`, arv.Type())
}
ch := make(chan *Pair)
go func(ctx context.Context, ch chan *Pair, arv reflect.Value) {
defer close(ch)
for i := 0; i < arv.Len(); i++ {
value := arv.Index(i)
pair := &Pair{
Index: i,
Value: value.Interface(),
}
select {
case <-ctx.Done():
return
case ch <- pair:
}
}
}(ctx, ch, arv)
return New(ch), nil
}
// Source represents a array that knows how to create an iterator
type Source interface {
Iterate(context.Context) Iterator
}
// Pair represents a single pair of key and value from a array
type Pair struct {
Index int
Value interface{}
}
// Iterator iterates through keys and values of a array
type Iterator interface {
Next(context.Context) bool
Pair() *Pair
}
type iter struct {
ch chan *Pair
mu sync.RWMutex
next *Pair
}
// Visitor represents an object that handles each pair in a array
type Visitor interface {
Visit(int, interface{}) error
}
// VisitorFunc is a type of Visitor based on a function
type VisitorFunc func(int, interface{}) error
func (fn VisitorFunc) Visit(s int, v interface{}) error {
return fn(s, v)
}
func New(ch chan *Pair) Iterator {
return &iter{
ch: ch,
}
}
// Next returns true if there are more items to read from the iterator
func (i *iter) Next(ctx context.Context) bool {
i.mu.RLock()
if i.ch == nil {
i.mu.RUnlock()
return false
}
i.mu.RUnlock()
i.mu.Lock()
defer i.mu.Unlock()
select {
case <-ctx.Done():
i.ch = nil
return false
case v, ok := <-i.ch:
if !ok {
i.ch = nil
return false
}
i.next = v
return true
}
//nolint:govet
return false // never reached
}
// Pair returns the currently buffered Pair. Calling Next() will reset its value
func (i *iter) Pair() *Pair {
i.mu.RLock()
defer i.mu.RUnlock()
return i.next
}
// Walk walks through each element in the array
func Walk(ctx context.Context, s Source, v Visitor) error {
for i := s.Iterate(ctx); i.Next(ctx); {
pair := i.Pair()
if err := v.Visit(pair.Index, pair.Value); err != nil {
return fmt.Errorf(`failed to visit index %d: %w`, pair.Index, err)
}
}
return nil
}
func AsArray(ctx context.Context, s interface{}, v interface{}) error {
var iter Iterator
switch reflect.ValueOf(s).Kind() {
case reflect.Array, reflect.Slice:
x, err := Iterate(ctx, s)
if err != nil {
return fmt.Errorf(`failed to iterate over array/slice type: %w`, err)
}
iter = x
default:
ssrc, ok := s.(Source)
if !ok {
return fmt.Errorf(`cannot iterate over %T: not a arrayiter.Source type`, s)
}
iter = ssrc.Iterate(ctx)
}
dst := reflect.ValueOf(v)
// dst MUST be a pointer to a array type
if kind := dst.Kind(); kind != reflect.Ptr {
return fmt.Errorf(`dst must be a pointer to a array (%s)`, dst.Type())
}
dst = dst.Elem()
switch dst.Kind() {
case reflect.Array, reflect.Slice:
default:
return fmt.Errorf(`dst must be a pointer to an array or slice (%s)`, dst.Type())
}
var pairs []*Pair
for iter.Next(ctx) {
pair := iter.Pair()
pairs = append(pairs, pair)
}
switch dst.Kind() {
case reflect.Array:
if len(pairs) < dst.Len() {
return fmt.Errorf(`dst array does not have enough space for elements (%d, want %d)`, dst.Len(), len(pairs))
}
case reflect.Slice:
if dst.IsNil() {
dst.Set(reflect.MakeSlice(dst.Type(), len(pairs), len(pairs)))
}
}
// dst must be assignable
if !dst.CanSet() {
return fmt.Errorf(`dst is not writeable`)
}
elemtyp := dst.Type().Elem()
for _, pair := range pairs {
rvvalue := reflect.ValueOf(pair.Value)
if !rvvalue.Type().AssignableTo(elemtyp) {
return fmt.Errorf(`cannot assign key of type %s to map key of type %s`, rvvalue.Type(), elemtyp)
}
dst.Index(pair.Index).Set(rvvalue)
}
return nil
}
|