mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-08 05:47:07 +09:00
150 lines
3.3 KiB
Go
150 lines
3.3 KiB
Go
package blockbuilder
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/samber/lo"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
|
"github.com/anyproto/anytype-heart/util/pbtypes"
|
|
)
|
|
|
|
// BuildAST builds tree structure from flat list of blocks
|
|
func BuildAST(raw []*model.Block) *Block {
|
|
blocks := lo.SliceToMap(raw, func(b *model.Block) (string, *Block) {
|
|
return b.Id, &Block{block: b}
|
|
})
|
|
|
|
isChildOf := map[string]string{}
|
|
for _, b := range raw {
|
|
children := make([]*Block, 0, len(b.ChildrenIds))
|
|
for _, id := range b.ChildrenIds {
|
|
isChildOf[id] = b.Id
|
|
|
|
if v, ok := blocks[id]; ok {
|
|
children = append(children, v)
|
|
}
|
|
}
|
|
|
|
blocks[b.Id].children = children
|
|
}
|
|
|
|
return findRootBlock(blocks, isChildOf)
|
|
}
|
|
|
|
func findRootBlock(blocks map[string]*Block, isChildOf map[string]string) *Block {
|
|
for id, b := range blocks {
|
|
// Root block has no parent
|
|
if _, ok := isChildOf[id]; !ok {
|
|
return b
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func dropBlockIDs(b *Block) {
|
|
b.block.Id = ""
|
|
for i := range b.block.ChildrenIds {
|
|
b.block.ChildrenIds[i] = ""
|
|
}
|
|
|
|
if b.block.Restrictions == nil {
|
|
b.block.Restrictions = &model.BlockRestrictions{}
|
|
}
|
|
|
|
for _, c := range b.children {
|
|
dropBlockIDs(c)
|
|
}
|
|
}
|
|
|
|
func isMarkContainsLink(m *model.BlockContentTextMark) bool {
|
|
return m.Type == model.BlockContentTextMark_Mention ||
|
|
m.Type == model.BlockContentTextMark_Object
|
|
}
|
|
|
|
func remapLinksInDataview(b *model.BlockContentDataview, newIDtoOldID map[string]string) {
|
|
b.TargetObjectId = newIDtoOldID[b.TargetObjectId]
|
|
|
|
for _, view := range b.Views {
|
|
for _, filter := range view.Filters {
|
|
newID := filter.Value.GetStringValue()
|
|
if newID != "" {
|
|
oldID := newIDtoOldID[newID]
|
|
if oldID != "" {
|
|
filter.Value = pbtypes.String(oldID)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func remapLinks(root *Block, idsMap map[string]string) {
|
|
if b := root.block.GetLink(); b != nil {
|
|
b.TargetBlockId = idsMap[b.TargetBlockId]
|
|
}
|
|
if b := root.block.GetText(); b != nil {
|
|
for _, m := range b.Marks.Marks {
|
|
if isMarkContainsLink(m) {
|
|
m.Param = idsMap[m.Param]
|
|
}
|
|
}
|
|
}
|
|
if b := root.block.GetBookmark(); b != nil {
|
|
if b.Type == model.LinkPreview_Page {
|
|
b.TargetObjectId = idsMap[b.TargetObjectId]
|
|
}
|
|
}
|
|
if b := root.block.GetDataview(); b != nil {
|
|
remapLinksInDataview(b, idsMap)
|
|
}
|
|
|
|
for _, c := range root.children {
|
|
remapLinks(c, idsMap)
|
|
}
|
|
}
|
|
|
|
func AssertTreesEqual(t *testing.T, want, got []*model.Block) bool {
|
|
wantTree := BuildAST(want)
|
|
gotTree := BuildAST(got)
|
|
|
|
dropBlockIDs(wantTree)
|
|
dropBlockIDs(gotTree)
|
|
|
|
ok := assert.Equal(t, wantTree, gotTree)
|
|
if !ok {
|
|
fmt.Println("Want tree:")
|
|
printTree(wantTree)
|
|
fmt.Println("Got tree:")
|
|
printTree(gotTree)
|
|
}
|
|
return ok
|
|
}
|
|
|
|
func AssertPagesEqualWithLinks(t *testing.T, want, got []*model.Block, idsMap map[string]string) bool {
|
|
wantTree := BuildAST(want)
|
|
gotTree := BuildAST(got)
|
|
|
|
dropBlockIDs(wantTree)
|
|
dropBlockIDs(gotTree)
|
|
|
|
remapLinks(gotTree, idsMap)
|
|
return assert.Equal(t, wantTree, gotTree)
|
|
}
|
|
|
|
func printTree(root *Block) {
|
|
b := &strings.Builder{}
|
|
renderNode(b, root, 0)
|
|
fmt.Println(b.String())
|
|
}
|
|
|
|
func renderNode(b *strings.Builder, node *Block, lvl int) {
|
|
b.WriteString(strings.Repeat(" ", lvl))
|
|
b.WriteString(node.String())
|
|
b.WriteString("\n")
|
|
for _, c := range node.children {
|
|
renderNode(b, c, lvl+1)
|
|
}
|
|
}
|