1package iterator
2
3import (
4	"context"
5	"fmt"
6
7	"github.com/cayleygraph/cayley/graph"
8)
9
10var _ graph.Iterator = &Skip{}
11
12// Skip iterator will skip certain number of values from primary iterator.
13type Skip struct {
14	uid       uint64
15	skip      int64
16	skipped   int64
17	primaryIt graph.Iterator
18}
19
20func NewSkip(primaryIt graph.Iterator, skip int64) *Skip {
21	return &Skip{
22		uid:       NextUID(),
23		skip:      skip,
24		primaryIt: primaryIt,
25	}
26}
27
28func (it *Skip) UID() uint64 {
29	return it.uid
30}
31
32// Reset resets the internal iterators and the iterator itself.
33func (it *Skip) Reset() {
34	it.skipped = 0
35	it.primaryIt.Reset()
36}
37
38func (it *Skip) Tagger() *graph.Tagger {
39	return it.primaryIt.Tagger()
40}
41
42func (it *Skip) TagResults(dst map[string]graph.Value) {
43	it.primaryIt.TagResults(dst)
44}
45
46func (it *Skip) Clone() graph.Iterator {
47	return NewSkip(it.primaryIt.Clone(), it.skip)
48}
49
50// SubIterators returns a slice of the sub iterators.
51func (it *Skip) SubIterators() []graph.Iterator {
52	return []graph.Iterator{it.primaryIt}
53}
54
55// Next advances the Skip iterator. It will skip all initial values
56// before returning actual result.
57func (it *Skip) Next(ctx context.Context) bool {
58	graph.NextLogIn(it)
59	for ; it.skipped < it.skip; it.skipped++ {
60		if !it.primaryIt.Next(ctx) {
61			return graph.NextLogOut(it, false)
62		}
63	}
64	if it.primaryIt.Next(ctx) {
65		return graph.NextLogOut(it, true)
66	}
67	return graph.NextLogOut(it, false)
68}
69
70func (it *Skip) Err() error {
71	return it.primaryIt.Err()
72}
73
74func (it *Skip) Result() graph.Value {
75	return it.primaryIt.Result()
76}
77
78func (it *Skip) Contains(ctx context.Context, val graph.Value) bool {
79	return it.primaryIt.Contains(ctx, val) // FIXME(dennwc): will not skip anything in this case
80}
81
82// NextPath checks whether there is another path. It will skip first paths
83// according to iterator parameter.
84func (it *Skip) NextPath(ctx context.Context) bool {
85	for ; it.skipped < it.skip; it.skipped++ {
86		if !it.primaryIt.NextPath(ctx) {
87			return false
88		}
89	}
90	return it.primaryIt.NextPath(ctx)
91}
92
93// Close closes the primary and all iterators.  It closes all subiterators
94// it can, but returns the first error it encounters.
95func (it *Skip) Close() error {
96	return it.primaryIt.Close()
97}
98
99func (it *Skip) Type() graph.Type { return graph.Skip }
100
101func (it *Skip) Optimize() (graph.Iterator, bool) {
102	optimizedPrimaryIt, optimized := it.primaryIt.Optimize()
103	if it.skip == 0 { // nothing to skip
104		return optimizedPrimaryIt, true
105	}
106	it.primaryIt = optimizedPrimaryIt
107	return it, optimized
108}
109
110func (it *Skip) Stats() graph.IteratorStats {
111	primaryStats := it.primaryIt.Stats()
112	primaryStats.Size -= it.skip
113	if primaryStats.Size < 0 {
114		primaryStats.Size = 0
115	}
116	return primaryStats
117}
118
119func (it *Skip) Size() (int64, bool) {
120	primarySize, exact := it.primaryIt.Size()
121	if exact {
122		primarySize -= it.skip
123		if primarySize < 0 {
124			primarySize = 0
125		}
126	}
127	return primarySize, exact
128}
129
130func (it *Skip) String() string {
131	return fmt.Sprintf("Skip(%d)", it.skip)
132}
133