package acltree import ( "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/acl/thread" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" ) type aclTreeBuilder struct { cache map[string]*Change identityKeys map[string]keys.SigningPubKey signingPubKeyDecoder keys.SigningPubKeyDecoder tree *Tree thread thread.Thread *changeLoader } func newACLTreeBuilder(t thread.Thread, decoder keys.SigningPubKeyDecoder) *aclTreeBuilder { return &aclTreeBuilder{ signingPubKeyDecoder: decoder, thread: t, changeLoader: newChangeLoader( t, decoder, NewACLChange), } } func (tb *aclTreeBuilder) Init() { tb.cache = make(map[string]*Change) tb.identityKeys = make(map[string]keys.SigningPubKey) tb.tree = &Tree{} tb.changeLoader.Init(tb.cache, tb.identityKeys) } func (tb *aclTreeBuilder) Build() (*Tree, error) { var headsAndOrphans []string headsAndOrphans = append(headsAndOrphans, tb.thread.Orphans()...) headsAndOrphans = append(headsAndOrphans, tb.thread.Heads()...) aclHeads, err := tb.getACLHeads(headsAndOrphans) if err != nil { return nil, err } if err = tb.buildTreeFromStart(aclHeads); err != nil { return nil, fmt.Errorf("buildTree error: %v", err) } tb.cache = nil return tb.tree, nil } func (tb *aclTreeBuilder) buildTreeFromStart(heads []string) (err error) { changes, root, err := tb.dfsFromStart(heads) if err != nil { return err } tb.tree.AddFast(root) tb.tree.AddFast(changes...) return } func (tb *aclTreeBuilder) dfsFromStart(heads []string) (buf []*Change, root *Change, err error) { var possibleRoots []*Change stack := make([]string, len(heads), len(heads)*2) copy(stack, heads) buf = make([]*Change, 0, len(stack)*2) uniqMap := make(map[string]struct{}) for len(stack) > 0 { id := stack[len(stack)-1] stack = stack[:len(stack)-1] if _, exists := uniqMap[id]; exists { continue } ch, err := tb.loadChange(id) if err != nil { continue } uniqMap[id] = struct{}{} buf = append(buf, ch) for _, prev := range ch.PreviousIds { stack = append(stack, prev) } if len(ch.PreviousIds) == 0 { possibleRoots = append(possibleRoots, ch) } } header := tb.thread.Header() for _, r := range possibleRoots { if r.Id == header.FirstChangeId { return buf, r, nil } } return nil, nil, fmt.Errorf("could not find root change") } func (tb *aclTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, err error) { for _, head := range heads { if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads continue } precedingHeads, err := tb.getPrecedingACLHeads(head) if err != nil { continue } for _, aclHead := range precedingHeads { if slice.FindPos(aclTreeHeads, aclHead) != -1 { continue } aclTreeHeads = append(aclTreeHeads, aclHead) } } if len(aclTreeHeads) == 0 { return nil, fmt.Errorf("no usable ACL heads in thread") } return aclTreeHeads, nil } func (tb *aclTreeBuilder) getPrecedingACLHeads(head string) ([]string, error) { headChange, err := tb.loadChange(head) if err != nil { return nil, err } if headChange.Content.GetAclData() != nil { return []string{head}, nil } else { return headChange.PreviousIds, nil } }