init: pristine aerc 0.20.0 source

This commit is contained in:
Mortdecai
2026-04-07 19:54:54 -04:00
commit 083402a548
502 changed files with 68722 additions and 0 deletions
+126
View File
@@ -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
}
+54
View File
@@ -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)
}
+133
View File
@@ -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)
}
}
}
+35
View File
@@ -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{}
}
+96
View File
@@ -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)
}
}