github.com/haraldrudell/parl@v0.4.176/iters/converter.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package iters 7 8 import ( 9 "github.com/haraldrudell/parl/internal/cyclebreaker" 10 "github.com/haraldrudell/parl/perrors" 11 ) 12 13 // Converter traverses another iterator and returns converted values 14 type Converter[K any, V any] struct { 15 // keyIterator provides the key values converterFunction uses to 16 // return values 17 keyIterator Iterator[K] 18 // ConverterFunction receives a key and returns the corresponding value 19 // - func(key K, isCancel bool) (value V, err error) 20 // - if isCancel true, it means this is the last invocation of ConverterFunction and 21 // ConverterFunction should release any resources. 22 // Any returned value is not used 23 // - ConverterFunction signals end of values by returning parl.ErrEndCallbacks. 24 // if hasValue is true, the accompanying value is used 25 // - when ConverterFunction returns error, it will not be invoked again. 26 // For errors other than parl.ErrEndCallbacks, value is not used 27 // - ConverterFunction must be thread-safe 28 // - ConverterFunction is invoked by at most one thread at a time 29 converter ConverterFunction[K, V] 30 *BaseIterator[V] 31 } 32 33 // NewConverterIterator returns a converting iterator. 34 // - converterFunction receives cancel and can return error 35 // - ConverterIterator is thread-safe and re-entrant. 36 // - stores self-referencing pointers 37 func NewConverterIterator[K any, V any]( 38 keyIterator Iterator[K], 39 converter ConverterFunction[K, V], 40 asyncCancel ...func(), 41 ) (iterator Iterator[V]) { 42 if converter == nil { 43 panic(cyclebreaker.NilError("converter")) 44 } else if keyIterator == nil { 45 panic(cyclebreaker.NilError("keyIterator")) 46 } 47 48 c := Converter[K, V]{ 49 keyIterator: keyIterator, 50 converter: converter, 51 } 52 c.BaseIterator = NewBaseIterator(c.iteratorAction, asyncCancel...) 53 54 return &c 55 } 56 57 // Init implements the right-hand side of a short variable declaration in 58 // the init statement for a Go “for” clause 59 // 60 // Usage: 61 // 62 // for i, iterator := NewSlicePointerIterator(someSlice).Init(); iterator.Cond(&i); { 63 // // i is pointer to slice element 64 func (i *Converter[K, T]) Init() (iterationVariable T, iterator Iterator[T]) { 65 iterator = i 66 return 67 } 68 69 // iteratorAction invokes converterFunction recovering a possible panic 70 // - if cancelState == notCanceled, a new value is requested. 71 // Otherwise, iteration cancel is requested 72 // - if err is nil, value is valid and isPanic false. 73 // Otherwise, err is non-nil and isPanic may be set. 74 // value is zero-value 75 func (i *Converter[K, T]) iteratorAction(isCancel bool) (value T, err error) { 76 77 // get next key from keyIterator 78 var key K 79 var isEndOfKeys bool 80 if !isCancel { 81 var hasKey bool 82 if key, hasKey = i.keyIterator.Next(); !hasKey { 83 isEndOfKeys = true 84 isCancel = true 85 } 86 } 87 88 // invoke converter function 89 // - func(key K, isCancel bool) (value V, err error) 90 if value, err = i.converter(key, isCancel); err != nil { 91 err = perrors.Stack(err) 92 } else if isEndOfKeys { 93 err = cyclebreaker.ErrEndCallbacks 94 } 95 96 return 97 }