Commit 50dfd994 authored by Eric Myhre's avatar Eric Myhre

traversal.NodeBuilderChooser can now return error. Also, the default no...

traversal.NodeBuilderChooser can now return error.  Also, the default no longer returns ipldfree.NodeBuilder.

The traversal package having a dependency on the ipldfree Node
implementation package was problematic: it's important that we be able
to benchmark traversal *in combination* with different implementations
of Node, and it turns out if we want to build reusable test and
benchmark functions for that, we get cyclic dependencies when then
trying to use them on our most common implementation!  Ouch.

Having a default implementation of Node referenced was originally out
of a desire for convenience.  But it's logically dubious anyway.
Even the convenience gain in practice is questionable since one still
always needs to configure a LinkLoader in the same areas.  At any rate,
hopefully the error message is helpful enough to make this low-impact.

(We could probably benefit from more helper methods around this, too.
But we can review that later.  Preferably after the 'NodeStyle' feature
from the R&D branches lands, also, which should make this whole area
better named, clearer about where its allocations arrive, and just
less fidgety in general.)
parent c0cd2769
......@@ -6,12 +6,15 @@ import (
"io"
ipld "github.com/ipld/go-ipld-prime"
ipldfree "github.com/ipld/go-ipld-prime/impl/free"
"github.com/ipld/go-ipld-prime/impl/typed"
)
// init sets all the values in TraveralConfig to reasonable defaults
// if they're currently the zero value.
//
// Note that you're absolutely going to need to replace the
// LinkLoader and LinkNodeBuilderChooser if you want automatic link traversal;
// the defaults return error and/or panic.
func (tc *Config) init() {
if tc.Ctx == nil {
tc.Ctx = context.Background()
......@@ -22,11 +25,11 @@ func (tc *Config) init() {
}
}
if tc.LinkNodeBuilderChooser == nil {
tc.LinkNodeBuilderChooser = func(lnk ipld.Link, lnkCtx ipld.LinkContext) ipld.NodeBuilder {
tc.LinkNodeBuilderChooser = func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodeBuilder, error) {
if tlnkNd, ok := lnkCtx.LinkNode.(typed.LinkNode); ok {
return tlnkNd.ReferencedNodeBuilder()
return tlnkNd.ReferencedNodeBuilder(), nil
}
return ipldfree.NodeBuilder()
return nil, fmt.Errorf("no LinkNodeBuilderChooser configured")
}
}
if tc.LinkStorer == nil {
......
......@@ -53,4 +53,4 @@ type Config struct {
// In a more complex example, a program using `bind` over native Go types
// could decide what kind of native type is expected, and return a
// `bind.NodeBuilder` for that specific concrete native type.
type NodeBuilderChooser func(ipld.Link, ipld.LinkContext) ipld.NodeBuilder
type NodeBuilderChooser func(ipld.Link, ipld.LinkContext) (ipld.NodeBuilder, error)
......@@ -67,11 +67,16 @@ func (prog Progress) Focus(n ipld.Node, p ipld.Path, fn VisitFn) error {
LinkNode: n,
ParentNode: prev,
}
// Pick what in-memory format we will build.
nb, err := prog.Cfg.LinkNodeBuilderChooser(lnk, lnkCtx)
if err != nil {
return fmt.Errorf("error traversing node at %q: could not load link %q: %s", p.Truncate(i+1), lnk, err)
}
// Load link!
next, err := lnk.Load(
prog.Cfg.Ctx,
lnkCtx,
prog.Cfg.LinkNodeBuilderChooser(lnk, lnkCtx),
nb,
prog.Cfg.LinkLoader,
)
if err != nil {
......
......@@ -130,14 +130,14 @@ func TestFocusWithLinkLoading(t *testing.T) {
t.Errorf("should not be reached; no way to load this path")
return nil
})
Wish(t, err.Error(), ShouldEqual, `error traversing node at "nested/alink": could not load link "`+leafAlphaLnk.String()+`": no link loader configured`)
Wish(t, err.Error(), ShouldEqual, `error traversing node at "nested/alink": could not load link "`+leafAlphaLnk.String()+`": no LinkNodeBuilderChooser configured`)
})
t.Run("mid-path link should fail", func(t *testing.T) {
err := traversal.Focus(rootNode, ipld.ParsePath("linkedMap/nested/nonlink"), func(prog traversal.Progress, n ipld.Node) error {
t.Errorf("should not be reached; no way to load this path")
return nil
})
Wish(t, err.Error(), ShouldEqual, `error traversing node at "linkedMap": could not load link "`+middleMapNodeLnk.String()+`": no link loader configured`)
Wish(t, err.Error(), ShouldEqual, `error traversing node at "linkedMap": could not load link "`+middleMapNodeLnk.String()+`": no LinkNodeBuilderChooser configured`)
})
})
t.Run("link traversal with loader should work", func(t *testing.T) {
......@@ -146,6 +146,9 @@ func TestFocusWithLinkLoading(t *testing.T) {
LinkLoader: func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) {
return bytes.NewBuffer(storage[lnk]), nil
},
LinkNodeBuilderChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeBuilder, error) {
return ipldfree.NodeBuilder(), nil
},
},
}.Focus(rootNode, ipld.ParsePath("linkedMap/nested/nonlink"), func(prog traversal.Progress, n ipld.Node) error {
Wish(t, n, ShouldEqual, fnb.CreateString("zoo"))
......
......@@ -127,11 +127,16 @@ func (prog Progress) loadLink(v ipld.Node, parent ipld.Node) (ipld.Node, error)
LinkNode: v,
ParentNode: parent,
}
// Pick what in-memory format we will build.
nb, err := prog.Cfg.LinkNodeBuilderChooser(lnk, lnkCtx)
if err != nil {
return nil, fmt.Errorf("error traversing node at %q: could not load link %q: %s", prog.Path, lnk, err)
}
// Load link!
v, err = lnk.Load(
prog.Cfg.Ctx,
lnkCtx,
prog.Cfg.LinkNodeBuilderChooser(lnk, lnkCtx),
nb,
prog.Cfg.LinkLoader,
)
if err != nil {
......
......@@ -124,6 +124,9 @@ func TestWalkMatching(t *testing.T) {
LinkLoader: func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) {
return bytes.NewBuffer(storage[lnk]), nil
},
LinkNodeBuilderChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeBuilder, error) {
return ipldfree.NodeBuilder(), nil
},
},
}.WalkMatching(middleMapNode, s, func(prog traversal.Progress, n ipld.Node) error {
switch order {
......@@ -167,6 +170,9 @@ func TestWalkMatching(t *testing.T) {
LinkLoader: func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) {
return bytes.NewBuffer(storage[lnk]), nil
},
LinkNodeBuilderChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeBuilder, error) {
return ipldfree.NodeBuilder(), nil
},
},
}.WalkMatching(middleListNode, s, func(prog traversal.Progress, n ipld.Node) error {
switch order {
......@@ -209,6 +215,9 @@ func TestWalkMatching(t *testing.T) {
LinkLoader: func(lnk ipld.Link, _ ipld.LinkContext) (io.Reader, error) {
return bytes.NewBuffer(storage[lnk]), nil
},
LinkNodeBuilderChooser: func(_ ipld.Link, _ ipld.LinkContext) (ipld.NodeBuilder, error) {
return ipldfree.NodeBuilder(), nil
},
},
}.WalkMatching(rootNode, s, func(prog traversal.Progress, n ipld.Node) error {
switch order {
......
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