~samwhited/xmpp

xmpp/paging/rsm.go -rw-r--r-- 3.6 KiB
1a8e804eSam Whited pubsub: make internal package public 7 hours ago
                                                                                
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
// Copyright 2021 The Mellium Contributors.
// Use of this source code is governed by the BSD 2-clause
// license that can be found in the LICENSE file.

//go:generate go run ../internal/genfeature

// Package paging implements result set management.
package paging // import "mellium.im/xmpp/paging"

import (
	"encoding/xml"

	"mellium.im/xmlstream"
)

// Namespaces used by this package.
const (
	NS = "http://jabber.org/protocol/rsm"
)

// Iter provides a mechanism for iterating over the children of an XML element.
// Successive calls to Next will step through each child, returning its start
// element and a reader that is limited to the remainder of the child.
//
// If the results indicate that there is another page of data, the paging child
// is skipped and the various paging methods will return queries that can be
// used to fetch the next and/or previous pages.
type Iter struct {
	iter        *xmlstream.Iter
	nextPageSet *RequestNext
	prevPageSet *RequestPrev
	curSet      *Set
	err         error
	max         uint64
}

// NewIter returns a new iterator that iterates over the children of the most
// recent start element already consumed from r.
func NewIter(r xml.TokenReader, max uint64) *Iter {
	return WrapIter(xmlstream.NewIter(r), max)
}

// WrapIter returns a new iterator that supports paging from an existing
// xmlstream.Iter.
func WrapIter(iter *xmlstream.Iter, max uint64) *Iter {
	return &Iter{
		iter: iter,
		max:  max,
	}
}

// Close indicates that we are finished with the given iterator. Calling it
// multiple times has no effect.
//
// If the underlying TokenReader is also an io.Closer, Close calls the readers
// Close method.
func (i *Iter) Close() error {
	return i.iter.Close()
}

// Current returns a reader over the most recent child.
func (i *Iter) Current() (*xml.StartElement, xml.TokenReader) {
	return i.iter.Current()
}

// Err returns the last error encountered by the iterator (if any).
func (i *Iter) Err() error {
	if i.err != nil {
		return i.err
	}
	return i.iter.Err()
}

// Next returns true if there are more items to decode.
func (i *Iter) Next() bool {
	if i.err != nil {
		return false
	}
	hasNext := i.iter.Next()
	if hasNext {
		start, r := i.iter.Current()
		if start != nil && start.Name.Local == "set" && start.Name.Space == NS {
			i.nextPageSet = nil
			i.prevPageSet = nil
			i.curSet = &Set{}
			i.err = xml.NewTokenDecoder(xmlstream.MultiReader(xmlstream.Token(*start), r)).Decode(i.curSet)
			if i.err != nil {
				return false
			}
			if i.curSet.First.ID != "" {
				i.prevPageSet = &RequestPrev{
					Before: i.curSet.First.ID,
					Max:    i.max,
				}
			}
			if i.curSet.Last != "" {
				i.nextPageSet = &RequestNext{
					After: i.curSet.Last,
					Max:   i.max,
				}
			}
			return i.Next()
		}
	}
	return hasNext
}

// NextPage returns a value that can be used to construct a new iterator that
// queries for the next page.
//
// It is only guaranteed to be set once iteration is finished, or when the
// iterator is closed without error and may be nil.
func (i *Iter) NextPage() *RequestNext {
	return i.nextPageSet
}

// PreviousPage returns a value that can be used to construct a new iterator that
// queries for the previous page.
//
// It is only guaranteed to be set once iteration is finished, or when the
// iterator is closed without error and may be nil.
func (i *Iter) PreviousPage() *RequestPrev {
	return i.prevPageSet
}

// CurrentPage returns information about the current page.
//
// It is only guaranteed to be set once iteration is finished, or when the
// iterator is closed without error and may be nil.
func (i *Iter) CurrentPage() *Set {
	return i.curSet
}