2021-10-06 17:38:00 +00:00
|
|
|
package pick
|
|
|
|
|
2022-02-01 14:39:50 +00:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2021-10-06 17:38:00 +00:00
|
|
|
type StringsSlice struct {
|
2022-01-27 12:29:49 +00:00
|
|
|
s []string
|
|
|
|
p *picker
|
2021-10-06 17:38:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewStringsSlice creates new StringsSlice.
|
|
|
|
func NewStringsSlice(items []string, mode pickMode) *StringsSlice {
|
2022-02-01 14:39:50 +00:00
|
|
|
maxIdx := len(items) - 1
|
|
|
|
|
|
|
|
if maxIdx < 0 {
|
|
|
|
maxIdx = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return &StringsSlice{s: items, p: NewPicker(uint32(maxIdx), mode)}
|
2021-10-06 17:38:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pick an element from the strings slice.
|
|
|
|
func (s *StringsSlice) Pick() string {
|
2022-01-27 12:29:49 +00:00
|
|
|
if len(s.s) == 0 {
|
2021-10-06 17:38:00 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2022-01-27 12:29:49 +00:00
|
|
|
return s.s[s.p.NextIndex()]
|
2021-10-06 17:38:00 +00:00
|
|
|
}
|
2022-02-01 14:39:50 +00:00
|
|
|
|
|
|
|
type StringsSliceWithInterval struct {
|
|
|
|
s []string
|
|
|
|
p *picker
|
|
|
|
d time.Duration
|
|
|
|
|
|
|
|
idxMu sync.RWMutex
|
|
|
|
idx uint32
|
|
|
|
|
|
|
|
close chan struct{}
|
|
|
|
closedMu sync.RWMutex
|
|
|
|
closed bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewStringsSliceWithInterval creates new StringsSliceWithInterval.
|
|
|
|
func NewStringsSliceWithInterval(items []string, mode pickMode, interval time.Duration) *StringsSliceWithInterval {
|
|
|
|
maxIdx := len(items) - 1
|
|
|
|
|
|
|
|
if maxIdx < 0 {
|
|
|
|
maxIdx = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if interval <= time.Duration(0) {
|
|
|
|
panic("NewStringsSliceWithInterval: wrong interval")
|
|
|
|
}
|
|
|
|
|
|
|
|
s := &StringsSliceWithInterval{
|
|
|
|
s: items,
|
|
|
|
p: NewPicker(uint32(maxIdx), mode),
|
|
|
|
d: interval,
|
|
|
|
close: make(chan struct{}, 1),
|
|
|
|
}
|
|
|
|
|
|
|
|
s.next()
|
|
|
|
|
|
|
|
go s.rotate()
|
|
|
|
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StringsSliceWithInterval) rotate() {
|
|
|
|
defer close(s.close)
|
|
|
|
|
|
|
|
timer := time.NewTimer(s.d)
|
|
|
|
defer timer.Stop()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-s.close:
|
|
|
|
return
|
|
|
|
|
|
|
|
case <-timer.C:
|
|
|
|
s.next()
|
|
|
|
timer.Reset(s.d)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StringsSliceWithInterval) next() {
|
|
|
|
idx := s.p.NextIndex()
|
|
|
|
|
|
|
|
s.idxMu.Lock()
|
|
|
|
s.idx = idx
|
|
|
|
s.idxMu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pick an element from the strings slice.
|
|
|
|
func (s *StringsSliceWithInterval) Pick() string {
|
|
|
|
if s.isClosed() {
|
|
|
|
panic("StringsSliceWithInterval.Pick(): closed")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(s.s) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
s.idxMu.RLock()
|
|
|
|
defer s.idxMu.RUnlock()
|
|
|
|
|
|
|
|
return s.s[s.idx]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StringsSliceWithInterval) isClosed() (closed bool) {
|
|
|
|
s.closedMu.RLock()
|
|
|
|
closed = s.closed
|
|
|
|
s.closedMu.RUnlock()
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StringsSliceWithInterval) Close() error {
|
|
|
|
if s.isClosed() {
|
|
|
|
return errors.New("closed")
|
|
|
|
}
|
|
|
|
|
|
|
|
s.closedMu.Lock()
|
|
|
|
s.closed = true
|
|
|
|
s.closedMu.Unlock()
|
|
|
|
|
|
|
|
s.close <- struct{}{}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|