Commit fcc1cc53 authored by hannahhoward's avatar hannahhoward

feat(traversal): traverse links

Add link loading to traverse informatively so that you can traverse across links
parent b5a8551a
package traversal package traversal
import ( import (
"fmt"
ipld "github.com/ipld/go-ipld-prime" ipld "github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector"
) )
...@@ -60,9 +62,29 @@ func (tp TraversalProgress) traverseInformatively(n ipld.Node, s selector.Select ...@@ -60,9 +62,29 @@ func (tp TraversalProgress) traverseInformatively(n ipld.Node, s selector.Select
kstr, _ := k.AsString() kstr, _ := k.AsString()
sNext := s.Explore(n, selector.PathSegmentString{kstr}) sNext := s.Explore(n, selector.PathSegmentString{kstr})
if sNext != nil { if sNext != nil {
// TODO when link load is implemented, it should go roughly here.
tpNext := tp tpNext := tp
tpNext.Path = tp.Path.AppendSegment(kstr) tpNext.Path = tp.Path.AppendSegment(kstr)
if v.ReprKind() == ipld.ReprKind_Link {
lnk, _ := v.AsLink()
// Assemble the LinkContext in case the Loader or NBChooser want it.
lnkCtx := ipld.LinkContext{
LinkPath: tpNext.Path,
LinkNode: v,
ParentNode: n,
}
// Load link!
v, err = lnk.Load(
tpNext.Cfg.Ctx,
lnkCtx,
tpNext.Cfg.LinkNodeBuilderChooser(lnk, lnkCtx),
tpNext.Cfg.LinkLoader,
)
if err != nil {
return fmt.Errorf("error traversing node at %q: could not load link %q: %s", tpNext.Path, lnk, err)
}
}
// TODO when link load is implemented, it should go roughly here.
if err := tpNext.traverseInformatively(v, sNext, fn); err != nil { if err := tpNext.traverseInformatively(v, sNext, fn); err != nil {
return err return err
} }
......
package traversal_test package traversal_test
import ( import (
"bytes"
"io"
"testing" "testing"
. "github.com/warpfork/go-wish" . "github.com/warpfork/go-wish"
ipld "github.com/ipld/go-ipld-prime" ipld "github.com/ipld/go-ipld-prime"
_ "github.com/ipld/go-ipld-prime/encoding/dagjson" _ "github.com/ipld/go-ipld-prime/encoding/dagjson"
"github.com/ipld/go-ipld-prime/fluent"
ipldfree "github.com/ipld/go-ipld-prime/impl/free" ipldfree "github.com/ipld/go-ipld-prime/impl/free"
"github.com/ipld/go-ipld-prime/traversal" "github.com/ipld/go-ipld-prime/traversal"
"github.com/ipld/go-ipld-prime/traversal/selector" "github.com/ipld/go-ipld-prime/traversal/selector"
...@@ -43,6 +46,7 @@ var ( ...@@ -43,6 +46,7 @@ var (
// covers traverse using a variety of selectors. // covers traverse using a variety of selectors.
// all cases here use one already-loaded Node; no link-loading exercised. // all cases here use one already-loaded Node; no link-loading exercised.
func TestTraverse(t *testing.T) { func TestTraverse(t *testing.T) {
ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder()) ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder())
t.Run("traverse selecting true should visit the root", func(t *testing.T) { t.Run("traverse selecting true should visit the root", func(t *testing.T) {
...@@ -109,4 +113,47 @@ func TestTraverse(t *testing.T) { ...@@ -109,4 +113,47 @@ func TestTraverse(t *testing.T) {
Wish(t, err, ShouldEqual, nil) Wish(t, err, ShouldEqual, nil)
Wish(t, order, ShouldEqual, 2) Wish(t, order, ShouldEqual, 2)
}) })
t.Run("traversing across nodes should work", func(t *testing.T) {
ss := ssb.ExploreRecursive(3, ssb.ExploreUnion(
ssb.Matcher(),
ssb.ExploreAll(ssb.ExploreRecursiveEdge()),
))
s, err := ss.Selector()
var order int
err = traversal.TraversalProgress{
Cfg: &traversal.TraversalConfig{
LinkLoader: func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) {
return bytes.NewBuffer(storage[lnk]), nil
},
},
}.Traverse(middleMapNode, s, func(tp traversal.TraversalProgress, n ipld.Node) error {
switch order {
case 0:
Wish(t, n, ShouldEqual, middleMapNode)
Wish(t, tp.Path.String(), ShouldEqual, "")
case 1:
Wish(t, n, ShouldEqual, fnb.CreateBool(true))
Wish(t, tp.Path.String(), ShouldEqual, "foo")
case 2:
Wish(t, n, ShouldEqual, fnb.CreateBool(false))
Wish(t, tp.Path.String(), ShouldEqual, "bar")
case 3:
Wish(t, n, ShouldEqual, fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {
mb.Insert(knb.CreateString("alink"), vnb.CreateLink(leafAlphaLnk))
mb.Insert(knb.CreateString("nonlink"), vnb.CreateString("zoo"))
}))
Wish(t, tp.Path.String(), ShouldEqual, "nested")
case 4:
Wish(t, n, ShouldEqual, fnb.CreateString("alpha"))
Wish(t, tp.Path.String(), ShouldEqual, "nested/alink")
case 5:
Wish(t, n, ShouldEqual, fnb.CreateString("zoo"))
Wish(t, tp.Path.String(), ShouldEqual, "nested/nonlink")
}
order++
return nil
})
Wish(t, err, ShouldEqual, nil)
Wish(t, order, ShouldEqual, 6)
})
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment