I needed this for a kind of index that allows me to subscribe to a tag in a git repo, but also get notified on a rebase or a head pointer move.
I was getting bogged down in the details (Git is a single-linked list starting at the head, so walking over it from the first commits to the newest commits is tricky)
This iterator seems to fulfil my needs around tracking the position within the list, and being able to reset.
The meta
API (pointer, implements error interface, has other methods for checking if rewound or EOF) seems to be as neat as one could hope to expect.
package main
import "testing"
import "fmt"
type meta struct {
err error
isRewound bool
isEOF bool
}
func (m *meta) Error() string {
return fmt.Sprintf("result: %e", m.err)
}
func (m *meta) IsRewound() bool {
return m.isRewound
}
func (m *meta) IsEOF() bool {
return m.isEOF
}
type index struct {
data <-chan int
reset <-chan struct{}
cancel <-chan struct{}
}
func (i *index) Next() (int, *meta) {
for {
select {
case val := <-i.data:
// fmt.Printf("Read value from %p\n", i.data)
return val, nil
case <-i.reset:
// fmt.Printf("Read reset from %p", i.reset)
return 128, &meta{nil, true, false}
case <-i.cancel:
i.data = nil
i.reset = nil
i.cancel = nil
// fmt.Printf("Read cancel from canceller %p\n", i.cancel)
return 0, &meta{nil, false, true}
}
}
}
func TestHelloWorld(t *testing.T) {
var (
next = make(chan int)
rewind = make(chan struct{})
cancel = make(chan struct{})
)
go func() {
next <- 1
next <- 2
rewind <- struct{}{}
next <- 1
next <- 2
cancel <- struct{}{}
next <- 0 // after cancel index is not receiving, this kills
// this goroutine
}()
var index = index{next, rewind, cancel}
if val, meta := index.Next(); val != 1 {
t.Fatalf("expected first value to be 1, got %q (meta: %#v)", val, meta)
}
if val, _ := index.Next(); val != 2 {
t.Fatalf("expected second value to be 2, got %q", val)
}
if _, meta := index.Next(); !meta.IsRewound() {
t.Fatalf("expected metadata to cary rewind signal")
}
if val, _ := index.Next(); val != 1 {
t.Fatalf("expected third value to be 1, got %q", val)
}
if val, _ := index.Next(); val != 2 {
t.Fatalf("expected fourth value to be 2, got %q", val)
}
if _, meta := index.Next(); !meta.IsEOF() {
fmt.Printf("expected iterator to be cancelled after reading four values")
}
}