1
0
Fork 0
mirror of https://git.sr.ht/~rjarry/aerc synced 2025-09-16 06:02:49 +02:00
aerc/lib/jwz/threadcontainer.go
Robin Jarry 13e9ee3b40 lib: vendor-in the jwz library
The maintainer of this library has gone AWOL. We are depending on
a patch that has never been merged. Let's vendor the library to avoid
future issues.

This patch has been made with the following steps:

git clone https://github.com/konimarti/jwz lib/jwz
git -C lib/jwz checkout fix-missing-messages
mv lib/jwz/test/testdata/ham lib/jwz/testdata
sed -i 's#test/testdata#testdata#' lib/jwz/jwz_test.go
rm -rf lib/jwz/.* lib/jwz/docs lib/jwz/examples lib/jwz/test
sed -i 's#github.com/gatherstars-com/jwz#git.sr.ht/~rjarry/aerc/lib/jwz#' \
	lib/threadbuilder.go
go mod tidy
git add --intent-to-add lib/jwz
make fmt

Along with some manual adjustments to fix the linter warnings. Also, to
make the patch smaller, I only kept 93 test emails from the test data
fixture.

Changelog-changed: The JWZ library used for threading is now vendored.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Moritz Poldrack <moritz@poldrack.dev>
2025-08-28 09:28:16 +02:00

144 lines
3.5 KiB
Go

// Author: Jim Idle - jimi@idle.ws / jimi@gatherstars.com
// SPDX-License-Identifier: Apache-2.0
package jwz
// NoThreadableError indicates that the container was not in a valid state when a utility function was called
// against it
type NoThreadableError struct{}
func (e NoThreadableError) Error() string {
return "Container is not a root (has parent), but contains no Threadable element"
}
// threadContainer is used to encapsulate a Threadable implementation. It holds some intermediate state used while
// threading. This is private to the module and is used while performing the threading operation, then discarded.
type threadContainer struct {
// threadable contains the base of the threadable items in this instance of the struct
//
threadable Threadable
// parent shows which threadable item is the parent of this container, which can be nil for a root node
//
parent *threadContainer
// child shows which threadable is the child of this container
//
child *threadContainer
// next shows which threadable is the sibling of this container
//
next *threadContainer
// forID holds the message id that this container is holding. This is only useful for
// when we don't ever see the actual email (it is referenced by one email, but we don't
// have the email as we are parsing a partial set)
//
forID string
}
// flush copies the ThreadContainer tree structure down into the underlying
// Threadable elements. I.E. make the IThreadable tree look like
// the ThreadContainer tree.
func (tc *threadContainer) flush() error {
// Only a root node can have no threadable element - if we have a parent, then
// we are not a root node.
//
if tc.parent != nil && tc.threadable == nil {
return NoThreadableError{}
}
tc.parent = nil
if tc.threadable != nil {
if tc.child == nil {
tc.threadable.SetChild(nil)
} else {
tc.threadable.SetChild(tc.child.threadable)
}
}
if tc.child != nil {
_ = tc.child.flush()
tc.child = nil
}
if tc.threadable != nil {
if tc.next == nil {
tc.threadable.SetNext(nil)
} else {
tc.threadable.SetNext(tc.next.threadable)
}
}
if tc.next != nil {
_ = tc.next.flush()
tc.next = nil
}
tc.threadable = nil
return nil
}
// findChild returns true if child is under self's tree. This is used for
// detecting circularities in the references header.
func (tc *threadContainer) findChild(target *threadContainer) bool {
found := false
for t := tc.child; t != nil; t = t.next {
found = found || t == target || t.findChild(target)
}
return found
}
// reverseChildren does what it implies and reverses the order of the child elements
func (tc *threadContainer) reverseChildren() {
if tc.child != nil {
var kid, prev, rest *threadContainer
// Reverse the children of this one
//
prev = nil
kid = tc.child
rest = kid.next
for kid != nil {
kid.next = prev
prev = kid
kid = rest
if rest != nil {
rest = rest.next
}
}
tc.child = prev
// Now reverse the children of this one's children
//
for kid = tc.child; kid != nil; kid = kid.next {
kid.reverseChildren()
}
}
}
func (tc *threadContainer) fillDummy(t Threadable) {
// Only a root node can have no threadable element - if we have a message id, then
// we are not a root node.
//
if tc.threadable == nil && tc.forID != "" {
tc.threadable = t.MakeDummy(tc.forID)
}
// Depth first, but it does not matter
//
if tc.child != nil {
tc.child.fillDummy(t)
}
// Simple walk this one, no need to save stack space by following the next chain here
//
if tc.next != nil {
tc.next.fillDummy(t)
}
}