init: pristine aerc 0.20.0 source
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
package iterator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/models"
|
||||
"git.sr.ht/~rjarry/aerc/worker/types"
|
||||
)
|
||||
|
||||
// defaultFactory
|
||||
type defaultFactory struct{}
|
||||
|
||||
func (df *defaultFactory) NewIterator(a interface{}) Iterator {
|
||||
switch data := a.(type) {
|
||||
case []models.UID:
|
||||
return &defaultUid{data: data, index: len(data)}
|
||||
case []*types.Thread:
|
||||
return &defaultThread{data: data, index: len(data)}
|
||||
}
|
||||
panic(errors.New("a iterator for this type is not implemented yet"))
|
||||
}
|
||||
|
||||
// defaultUid
|
||||
type defaultUid struct {
|
||||
data []models.UID
|
||||
index int
|
||||
}
|
||||
|
||||
func (du *defaultUid) Next() bool {
|
||||
du.index--
|
||||
return du.index >= 0
|
||||
}
|
||||
|
||||
func (du *defaultUid) Value() interface{} {
|
||||
return du.data[du.index]
|
||||
}
|
||||
|
||||
func (du *defaultUid) StartIndex() int {
|
||||
return len(du.data) - 1
|
||||
}
|
||||
|
||||
func (du *defaultUid) EndIndex() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// defaultThread
|
||||
type defaultThread struct {
|
||||
data []*types.Thread
|
||||
index int
|
||||
}
|
||||
|
||||
func (dt *defaultThread) Next() bool {
|
||||
dt.index--
|
||||
return dt.index >= 0
|
||||
}
|
||||
|
||||
func (dt *defaultThread) Value() interface{} {
|
||||
return dt.data[dt.index]
|
||||
}
|
||||
|
||||
func (dt *defaultThread) StartIndex() int {
|
||||
return len(dt.data) - 1
|
||||
}
|
||||
|
||||
func (dt *defaultThread) EndIndex() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// reverseFactory
|
||||
type reverseFactory struct{}
|
||||
|
||||
func (rf *reverseFactory) NewIterator(a interface{}) Iterator {
|
||||
switch data := a.(type) {
|
||||
case []models.UID:
|
||||
return &reverseUid{data: data, index: -1}
|
||||
case []*types.Thread:
|
||||
return &reverseThread{data: data, index: -1}
|
||||
}
|
||||
panic(errors.New("an iterator for this type is not implemented yet"))
|
||||
}
|
||||
|
||||
// reverseUid
|
||||
type reverseUid struct {
|
||||
data []models.UID
|
||||
index int
|
||||
}
|
||||
|
||||
func (ru *reverseUid) Next() bool {
|
||||
ru.index++
|
||||
return ru.index < len(ru.data)
|
||||
}
|
||||
|
||||
func (ru *reverseUid) Value() interface{} {
|
||||
return ru.data[ru.index]
|
||||
}
|
||||
|
||||
func (ru *reverseUid) StartIndex() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ru *reverseUid) EndIndex() int {
|
||||
return len(ru.data) - 1
|
||||
}
|
||||
|
||||
// reverseThread
|
||||
type reverseThread struct {
|
||||
data []*types.Thread
|
||||
index int
|
||||
}
|
||||
|
||||
func (rt *reverseThread) Next() bool {
|
||||
rt.index++
|
||||
return rt.index < len(rt.data)
|
||||
}
|
||||
|
||||
func (rt *reverseThread) Value() interface{} {
|
||||
return rt.data[rt.index]
|
||||
}
|
||||
|
||||
func (rt *reverseThread) StartIndex() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rt *reverseThread) EndIndex() int {
|
||||
return len(rt.data) - 1
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package iterator
|
||||
|
||||
// IndexProvider implements a subset of the Iterator interface
|
||||
type IndexProvider interface {
|
||||
StartIndex() int
|
||||
EndIndex() int
|
||||
}
|
||||
|
||||
// FixBounds will force the index i to either its lower- or upper-bound value
|
||||
// if out-of-bound
|
||||
func FixBounds(i, lower, upper int) int {
|
||||
switch {
|
||||
case i > upper:
|
||||
i = upper
|
||||
case i < lower:
|
||||
i = lower
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// WrapBounds will wrap the index i around its upper- or lower-bound if
|
||||
// out-of-bound
|
||||
func WrapBounds(i, lower, upper int) int {
|
||||
if upper <= 0 {
|
||||
return lower
|
||||
}
|
||||
switch {
|
||||
case i > upper:
|
||||
i = lower + (i-upper-1)%upper
|
||||
case i < lower:
|
||||
i = upper - (lower-i-1)%upper
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
type BoundsCheckFunc func(int, int, int) int
|
||||
|
||||
// MoveIndex moves the index variable idx forward by delta steps and ensures
|
||||
// that the boundary policy as defined by the CheckBoundsFunc is enforced.
|
||||
//
|
||||
// If CheckBoundsFunc is nil, fix boundary checks are performed.
|
||||
func MoveIndex(idx, delta int, indexer IndexProvider, cb BoundsCheckFunc) int {
|
||||
lower, upper := indexer.StartIndex(), indexer.EndIndex()
|
||||
sign := 1
|
||||
if upper < lower {
|
||||
lower, upper = upper, lower
|
||||
sign = -1
|
||||
}
|
||||
result := idx + sign*delta
|
||||
if cb == nil {
|
||||
return FixBounds(result, lower, upper)
|
||||
}
|
||||
return cb(result, lower, upper)
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package iterator_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/lib/iterator"
|
||||
)
|
||||
|
||||
type indexer struct {
|
||||
start int
|
||||
end int
|
||||
}
|
||||
|
||||
func (ip *indexer) StartIndex() int {
|
||||
return ip.start
|
||||
}
|
||||
|
||||
func (ip *indexer) EndIndex() int {
|
||||
return ip.end
|
||||
}
|
||||
|
||||
func TestMoveIndex(t *testing.T) {
|
||||
tests := []struct {
|
||||
idx int
|
||||
delta int
|
||||
start int
|
||||
end int
|
||||
cb iterator.BoundsCheckFunc
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
idx: 0,
|
||||
delta: 1,
|
||||
start: 0,
|
||||
end: 2,
|
||||
cb: iterator.FixBounds,
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
idx: 0,
|
||||
delta: 5,
|
||||
start: 0,
|
||||
end: 2,
|
||||
cb: iterator.FixBounds,
|
||||
expected: 2,
|
||||
},
|
||||
{
|
||||
idx: 0,
|
||||
delta: -1,
|
||||
start: 0,
|
||||
end: 2,
|
||||
cb: iterator.FixBounds,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
idx: 0,
|
||||
delta: 2,
|
||||
start: 0,
|
||||
end: 2,
|
||||
cb: iterator.WrapBounds,
|
||||
expected: 2,
|
||||
},
|
||||
{
|
||||
idx: 0,
|
||||
delta: 3,
|
||||
start: 0,
|
||||
end: 2,
|
||||
cb: iterator.WrapBounds,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
idx: 0,
|
||||
delta: -1,
|
||||
start: 0,
|
||||
end: 2,
|
||||
cb: iterator.WrapBounds,
|
||||
expected: 2,
|
||||
},
|
||||
{
|
||||
idx: 2,
|
||||
delta: 2,
|
||||
start: 0,
|
||||
end: 2,
|
||||
cb: iterator.WrapBounds,
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
idx: 0,
|
||||
delta: -2,
|
||||
start: 0,
|
||||
end: 2,
|
||||
cb: iterator.WrapBounds,
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
idx: 1,
|
||||
delta: 1,
|
||||
start: 2,
|
||||
end: 0,
|
||||
cb: iterator.FixBounds,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
idx: 0,
|
||||
delta: 1,
|
||||
start: 2,
|
||||
end: 0,
|
||||
cb: iterator.FixBounds,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
idx: 0,
|
||||
delta: 1,
|
||||
start: 2,
|
||||
end: 0,
|
||||
cb: iterator.WrapBounds,
|
||||
expected: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
idx := iterator.MoveIndex(
|
||||
test.idx,
|
||||
test.delta,
|
||||
&indexer{test.start, test.end},
|
||||
test.cb,
|
||||
)
|
||||
if idx != test.expected {
|
||||
t.Errorf("test %d [%#v] failed: got %d but expected %d",
|
||||
i, test, idx, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package iterator
|
||||
|
||||
// Factory is the interface that wraps the NewIterator method. The
|
||||
// NewIterator() creates either UID or thread iterators and ensures that both
|
||||
// types of iterators implement the same iteration direction.
|
||||
type Factory interface {
|
||||
NewIterator(a interface{}) Iterator
|
||||
}
|
||||
|
||||
// Iterator implements an interface for iterating over UID or thread data. If
|
||||
// Next() returns true, the current value of the iterator can be read with
|
||||
// Value(). The return value of Value() is an interface{} type which needs to
|
||||
// be cast to the correct type.
|
||||
//
|
||||
// The iterators are implemented such that the first returned value always
|
||||
// represents the top message in the message list. Hence, StartIndex() would
|
||||
// return the index of the top message whereas EndIndex() returns the index of
|
||||
// message at the bottom of the list.
|
||||
type Iterator interface {
|
||||
Next() bool
|
||||
Value() interface{}
|
||||
StartIndex() int
|
||||
EndIndex() int
|
||||
}
|
||||
|
||||
// NewFactory creates an iterator factory. When reverse is true, the iterators
|
||||
// are reversed in the sense that the lowest UID messages are displayed at the
|
||||
// top of the message list. Otherwise, the default order is with the highest
|
||||
// UID message on top.
|
||||
func NewFactory(reverse bool) Factory {
|
||||
if reverse {
|
||||
return &reverseFactory{}
|
||||
}
|
||||
return &defaultFactory{}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package iterator_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.sr.ht/~rjarry/aerc/lib/iterator"
|
||||
"git.sr.ht/~rjarry/aerc/models"
|
||||
"git.sr.ht/~rjarry/aerc/worker/types"
|
||||
)
|
||||
|
||||
func toThreads(uids []models.UID) []*types.Thread {
|
||||
threads := make([]*types.Thread, len(uids))
|
||||
for i, u := range uids {
|
||||
threads[i] = &types.Thread{Uid: u}
|
||||
}
|
||||
return threads
|
||||
}
|
||||
|
||||
func TestIterator_DefaultFactory(t *testing.T) {
|
||||
input := []models.UID{"1", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
want := []models.UID{"9", "8", "7", "6", "5", "4", "3", "2", "1"}
|
||||
|
||||
factory := iterator.NewFactory(false)
|
||||
if factory == nil {
|
||||
t.Errorf("could not create factory")
|
||||
}
|
||||
start, end := len(input)-1, 0
|
||||
checkUids(t, factory, input, want, start, end)
|
||||
checkThreads(t, factory, toThreads(input),
|
||||
toThreads(want), start, end)
|
||||
}
|
||||
|
||||
func TestIterator_ReverseFactory(t *testing.T) {
|
||||
input := []models.UID{"1", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
want := []models.UID{"1", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
|
||||
factory := iterator.NewFactory(true)
|
||||
if factory == nil {
|
||||
t.Errorf("could not create factory")
|
||||
}
|
||||
|
||||
start, end := 0, len(input)-1
|
||||
checkUids(t, factory, input, want, start, end)
|
||||
checkThreads(t, factory, toThreads(input),
|
||||
toThreads(want), start, end)
|
||||
}
|
||||
|
||||
func checkUids(t *testing.T, factory iterator.Factory,
|
||||
input []models.UID, want []models.UID, start, end int,
|
||||
) {
|
||||
label := "uids"
|
||||
got := make([]models.UID, 0)
|
||||
iter := factory.NewIterator(input)
|
||||
for iter.Next() {
|
||||
got = append(got, iter.Value().(models.UID))
|
||||
}
|
||||
if len(got) != len(want) {
|
||||
t.Errorf("%s: number of elements not correct", label)
|
||||
}
|
||||
for i, u := range want {
|
||||
if got[i] != u {
|
||||
t.Errorf("%s: order not correct", label)
|
||||
}
|
||||
}
|
||||
if iter.StartIndex() != start {
|
||||
t.Errorf("%s: start index not correct", label)
|
||||
}
|
||||
if iter.EndIndex() != end {
|
||||
t.Errorf("%s: end index not correct", label)
|
||||
}
|
||||
}
|
||||
|
||||
func checkThreads(t *testing.T, factory iterator.Factory,
|
||||
input []*types.Thread, want []*types.Thread, start, end int,
|
||||
) {
|
||||
label := "threads"
|
||||
got := make([]*types.Thread, 0)
|
||||
iter := factory.NewIterator(input)
|
||||
for iter.Next() {
|
||||
got = append(got, iter.Value().(*types.Thread))
|
||||
}
|
||||
if len(got) != len(want) {
|
||||
t.Errorf("%s: number of elements not correct", label)
|
||||
}
|
||||
for i, th := range want {
|
||||
if got[i].Uid != th.Uid {
|
||||
t.Errorf("%s: order not correct", label)
|
||||
}
|
||||
}
|
||||
if iter.StartIndex() != start {
|
||||
t.Errorf("%s: start index not correct", label)
|
||||
}
|
||||
if iter.EndIndex() != end {
|
||||
t.Errorf("%s: end index not correct", label)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user