2021-11-12 18:12:02 +01:00
|
|
|
package types
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2022-03-08 18:13:39 +01:00
|
|
|
"sort"
|
2021-11-12 18:12:02 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type Thread struct {
|
|
|
|
Uid uint32
|
|
|
|
Parent *Thread
|
|
|
|
PrevSibling *Thread
|
|
|
|
NextSibling *Thread
|
|
|
|
FirstChild *Thread
|
|
|
|
|
|
|
|
Hidden bool // if this flag is set the message isn't rendered in the UI
|
|
|
|
Deleted bool // if this flag is set the message was deleted
|
|
|
|
}
|
|
|
|
|
2022-02-21 00:18:41 +01:00
|
|
|
func (t *Thread) AddChild(child *Thread) {
|
2022-02-24 00:41:13 +01:00
|
|
|
t.insertCmp(child, func(child, iter *Thread) bool { return true })
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Thread) OrderedInsert(child *Thread) {
|
|
|
|
t.insertCmp(child, func(child, iter *Thread) bool { return child.Uid > iter.Uid })
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Thread) insertCmp(child *Thread, cmp func(*Thread, *Thread) bool) {
|
2022-02-21 00:18:41 +01:00
|
|
|
if t.FirstChild == nil {
|
|
|
|
t.FirstChild = child
|
|
|
|
} else {
|
2022-02-24 00:41:13 +01:00
|
|
|
start := &Thread{Uid: t.FirstChild.Uid, NextSibling: t.FirstChild}
|
2022-02-21 00:18:41 +01:00
|
|
|
var iter *Thread
|
2022-02-24 00:41:13 +01:00
|
|
|
for iter = start; iter.NextSibling != nil && cmp(child, iter); iter = iter.NextSibling {
|
2022-02-21 00:18:41 +01:00
|
|
|
}
|
2022-02-24 00:41:13 +01:00
|
|
|
child.NextSibling = iter.NextSibling
|
2022-02-21 00:18:41 +01:00
|
|
|
iter.NextSibling = child
|
2022-02-24 00:41:13 +01:00
|
|
|
t.FirstChild = start.NextSibling
|
2022-02-21 00:18:41 +01:00
|
|
|
}
|
|
|
|
child.Parent = t
|
|
|
|
}
|
|
|
|
|
2021-11-12 18:12:02 +01:00
|
|
|
func (t *Thread) Walk(walkFn NewThreadWalkFn) error {
|
|
|
|
err := newWalk(t, walkFn, 0, nil)
|
|
|
|
if err == ErrSkipThread {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Thread) String() string {
|
|
|
|
if t == nil {
|
|
|
|
return "<nil>"
|
|
|
|
}
|
|
|
|
parent := -1
|
|
|
|
if t.Parent != nil {
|
|
|
|
parent = int(t.Parent.Uid)
|
|
|
|
}
|
|
|
|
next := -1
|
|
|
|
if t.NextSibling != nil {
|
|
|
|
next = int(t.NextSibling.Uid)
|
|
|
|
}
|
|
|
|
child := -1
|
|
|
|
if t.FirstChild != nil {
|
|
|
|
child = int(t.FirstChild.Uid)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf(
|
|
|
|
"[%d] (parent:%v, next:%v, child:%v)",
|
|
|
|
t.Uid, parent, next, child,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newWalk(node *Thread, walkFn NewThreadWalkFn, lvl int, ce error) error {
|
|
|
|
if node == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
err := walkFn(node, lvl, ce)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
|
|
|
err = newWalk(child, walkFn, lvl+1, err)
|
|
|
|
if err == ErrSkipThread {
|
|
|
|
err = nil
|
|
|
|
continue
|
|
|
|
} else if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var ErrSkipThread = errors.New("skip this Thread")
|
|
|
|
|
|
|
|
type NewThreadWalkFn func(t *Thread, level int, currentErr error) error
|
|
|
|
|
2022-07-31 22:16:40 +02:00
|
|
|
// Implement interface to be able to sort threads by newest (max UID)
|
2021-11-12 18:12:02 +01:00
|
|
|
type ByUID []*Thread
|
|
|
|
|
|
|
|
func getMaxUID(thread *Thread) uint32 {
|
|
|
|
// TODO: should we make this part of the Thread type to avoid recomputation?
|
|
|
|
var Uid uint32
|
|
|
|
|
2022-07-29 22:31:54 +02:00
|
|
|
_ = thread.Walk(func(t *Thread, _ int, currentErr error) error {
|
2021-11-12 18:12:02 +01:00
|
|
|
if t.Uid > Uid {
|
|
|
|
Uid = t.Uid
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return Uid
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s ByUID) Len() int {
|
|
|
|
return len(s)
|
|
|
|
}
|
2022-07-31 22:16:40 +02:00
|
|
|
|
2021-11-12 18:12:02 +01:00
|
|
|
func (s ByUID) Swap(i, j int) {
|
|
|
|
s[i], s[j] = s[j], s[i]
|
|
|
|
}
|
2022-07-31 22:16:40 +02:00
|
|
|
|
2021-11-12 18:12:02 +01:00
|
|
|
func (s ByUID) Less(i, j int) bool {
|
|
|
|
maxUID_i := getMaxUID(s[i])
|
|
|
|
maxUID_j := getMaxUID(s[j])
|
|
|
|
return maxUID_i < maxUID_j
|
|
|
|
}
|
2022-03-08 18:13:39 +01:00
|
|
|
|
|
|
|
func SortThreadsBy(toSort []*Thread, sortBy []uint32) {
|
|
|
|
// build a map from sortBy
|
|
|
|
uidMap := make(map[uint32]int)
|
|
|
|
for i, uid := range sortBy {
|
|
|
|
uidMap[uid] = i
|
|
|
|
}
|
|
|
|
// sortslice of toSort with less function of indexing the map sortBy
|
|
|
|
sort.Slice(toSort, func(i, j int) bool {
|
|
|
|
return uidMap[getMaxUID(toSort[i])] < uidMap[getMaxUID(toSort[j])]
|
|
|
|
})
|
|
|
|
}
|