From 81c626ef6dbd9d26e4964d44037e9dc26dd3b3bd Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Tue, 31 Mar 2020 18:26:44 +0200 Subject: [PATCH] traversal.SkipMe can now be used to control walks. --- traversal/fns.go | 13 +++++++++++++ traversal/walk.go | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/traversal/fns.go b/traversal/fns.go index 5c5ea844..b67ba6f2 100644 --- a/traversal/fns.go +++ b/traversal/fns.go @@ -54,3 +54,16 @@ type Config struct { // could decide what kind of native type is expected, and return a // `bind.NodeBuilder` for that specific concrete native type. type LinkTargetNodeStyleChooser func(ipld.Link, ipld.LinkContext) (ipld.NodeStyle, error) + +// SkipMe is a signalling "error" which can be used to tell traverse to skip some data. +// +// SkipMe can be returned by the Config.LinkLoader to skip entire blocks without aborting the walk. +// (This can be useful if you know you don't have data on hand, +// but want to continue the walk in other areas anyway; +// or, if you're doing a way where you know that it's valid to memoize seen +// areas based on Link alone.) +type SkipMe struct{} + +func (SkipMe) Error() string { + return "skip" +} diff --git a/traversal/walk.go b/traversal/walk.go index dde9937d..bcd173d0 100644 --- a/traversal/walk.go +++ b/traversal/walk.go @@ -57,6 +57,8 @@ func WalkTransforming(n ipld.Node, s selector.Selector, fn TransformFn) (ipld.No // This is important to note because when walking DAGs with Links, // it means you may visit the same node multiple times // due to having reached it via a different path. +// (You can prevent this by using a LinkLoader function which memoizes a set of +// already-visited Links, and returns a SkipMe when encountering them again.) // // WalkMatching (and the other traversal functions) can be used again again inside the VisitFn! // By using the traversal.Progress handed to the VisitFn, @@ -122,6 +124,9 @@ func (prog Progress) walkAdv_iterateAll(n ipld.Node, s selector.Selector, fn Adv progNext.LastBlock.Link = lnk v, err = progNext.loadLink(v, n) if err != nil { + if _, ok := err.(SkipMe); ok { + return nil + } return err } } @@ -151,6 +156,9 @@ func (prog Progress) walkAdv_iterateSelective(n ipld.Node, attn []ipld.PathSegme progNext.LastBlock.Link = lnk v, err = progNext.loadLink(v, n) if err != nil { + if _, ok := err.(SkipMe); ok { + return nil + } return err } } @@ -189,6 +197,9 @@ func (prog Progress) loadLink(v ipld.Node, parent ipld.Node) (ipld.Node, error) prog.Cfg.LinkLoader, ) if err != nil { + if _, ok := err.(SkipMe); ok { + return nil, err + } return nil, fmt.Errorf("error traversing node at %q: could not load link %q: %s", prog.Path, lnk, err) } return nb.Build(), nil