-
Notifications
You must be signed in to change notification settings - Fork 0
/
iterator.go
147 lines (124 loc) · 3.22 KB
/
iterator.go
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
// A forward only immutable iterator over a collection of items.
// The iterator is a powerful mechanism to serve and transform data lazily reducing the memory requirements and
// improving the time to first result performances.
package iterator
import (
"io"
"github.com/pkg/errors"
)
// If there is a next element return: next, false, nil
// If an error occurs computing the next element return: nil, false, error
// If there is no next element return: nil, true, nil
type ComputeNext func() (next interface{}, eod bool, err error)
type Closer func() error
// An iterator over a stream of data
type Iterator interface {
HasNext() bool
Next() (interface{}, error)
Peek() (item interface{}, e error)
io.Closer
}
type State int
const (
// We haven't yet computed or have already returned the element
NotReady State = iota
// We have computed the next element and haven't returned it yet.
Ready
// We have reached the end of the data and are finished.
Done
// We've suffered an error, kaput !!.
Failed
)
var _ Iterator = (*DefaultIterator)(nil)
var _ io.Closer = (*DefaultIterator)(nil)
type DefaultIterator struct {
state State
next interface{}
err error
ComputeNext ComputeNext
closer Closer
}
// Given a way to compute next, returns an iterator
func NewDefaultIterator(computeNext ComputeNext) Iterator {
return &DefaultIterator{
ComputeNext: computeNext,
}
}
// Given a way to compute next and a close handler, return a closeable iterator
func NewCloseableIterator(computeNext ComputeNext, closer Closer) Iterator {
return &DefaultIterator{
ComputeNext: computeNext,
closer: closer,
}
}
// Returns true if the iterator can be continued or false if the end of data has been reached.
// It returns an error if the check fails.
func (it *DefaultIterator) HasNext() bool {
switch it.state {
case Ready:
return true
case Done:
return false
case Failed:
return false
//, errors.New("metadata iterator in an error state")
}
return it.tryToComputeNext()
}
// Returns the next item in the iteration.
// This method should be always called in combination with the HasNext.
// If the iterator reached the end of data, the method will return an error
func (it *DefaultIterator) Next() (interface{}, error) {
hasNext := it.HasNext()
if it.err != nil {
return nil, it.err
}
if !hasNext {
return nil, errors.New("no such element") // is eof an error?
}
it.state = NotReady
nextItem := it.next
it.next = nil
return nextItem, nil
}
//
func (it *DefaultIterator) tryToComputeNext() bool {
it.state = Failed // temporary pessimism
next, eod, err := it.ComputeNext()
if err != nil { // we got an err, stated
it.state = Failed
it.err = err
return false
}
if eod {
it.state = Done
return false
}
it.state = Ready
it.next = next
return true
}
// Returns the next element without continuing the iteration.
func (it *DefaultIterator) Peek() (interface{}, error) {
hasNext := it.HasNext()
// an error getting the next item
if it.err != nil {
return nil, it.err
}
// no more items
if !hasNext {
return nil, io.EOF
}
next := it.next
return next, nil
}
func (it *DefaultIterator) Close() error {
it.state = Done
if it.closer != nil {
err := it.closer()
if err != nil {
return err
}
}
return nil
}