github.com/ipld/go-ipld-prime@v0.21.0/traversal/walk.go (about)

     1  package traversal
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/ipld/go-ipld-prime/datamodel"
     8  	"github.com/ipld/go-ipld-prime/linking"
     9  	"github.com/ipld/go-ipld-prime/linking/preload"
    10  	"github.com/ipld/go-ipld-prime/traversal/selector"
    11  )
    12  
    13  // phase is an internal enum used to track the current phase of a walk. It's
    14  // used to control for a preload pass over a block if one is required.
    15  type phase int
    16  
    17  const (
    18  	phasePreload  phase = iota
    19  	phaseTraverse phase = iota
    20  )
    21  
    22  // WalkLocal walks a tree of Nodes, visiting each of them,
    23  // and calling the given VisitFn on all of them;
    24  // it does not traverse any links.
    25  //
    26  // WalkLocal can skip subtrees if the VisitFn returns SkipMe,
    27  // but lacks any other options for controlling or directing the visit;
    28  // consider using some of the various Walk functions with Selector parameters if you want more control.
    29  func WalkLocal(n datamodel.Node, fn VisitFn) error {
    30  	return Progress{}.WalkLocal(n, fn)
    31  }
    32  
    33  // WalkMatching walks a graph of Nodes, deciding which to visit by applying a Selector,
    34  // and calling the given VisitFn on those that the Selector deems a match.
    35  //
    36  // This function is a helper function which starts a new walk with default configuration.
    37  // It cannot cross links automatically (since this requires configuration).
    38  // Use the equivalent WalkMatching function on the Progress structure
    39  // for more advanced and configurable walks.
    40  func WalkMatching(n datamodel.Node, s selector.Selector, fn VisitFn) error {
    41  	return Progress{}.WalkMatching(n, s, fn)
    42  }
    43  
    44  // WalkAdv is identical to WalkMatching, except it is called for *all* nodes
    45  // visited (not just matching nodes), together with the reason for the visit.
    46  // An AdvVisitFn is used instead of a VisitFn, so that the reason can be provided.
    47  //
    48  // This function is a helper function which starts a new walk with default configuration.
    49  // It cannot cross links automatically (since this requires configuration).
    50  // Use the equivalent WalkAdv function on the Progress structure
    51  // for more advanced and configurable walks.
    52  func WalkAdv(n datamodel.Node, s selector.Selector, fn AdvVisitFn) error {
    53  	return Progress{}.WalkAdv(n, s, fn)
    54  }
    55  
    56  // WalkTransforming walks a graph of Nodes, deciding which to alter by applying a Selector,
    57  // and calls the given TransformFn to decide what new node to replace the visited node with.
    58  // A new Node tree will be returned (the original is unchanged).
    59  //
    60  // This function is a helper function which starts a new walk with default configuration.
    61  // It cannot cross links automatically (since this requires configuration).
    62  // Use the equivalent WalkTransforming function on the Progress structure
    63  // for more advanced and configurable walks.
    64  func WalkTransforming(n datamodel.Node, s selector.Selector, fn TransformFn) (datamodel.Node, error) {
    65  	return Progress{}.WalkTransforming(n, s, fn)
    66  }
    67  
    68  // WalkMatching walks a graph of Nodes, deciding which to visit by applying a Selector,
    69  // and calling the given VisitFn on those that the Selector deems a match.
    70  //
    71  // WalkMatching is a read-only traversal.
    72  // See WalkTransforming if looking for a way to do "updates" to a tree of nodes.
    73  //
    74  // Provide configuration to this process using the Config field in the Progress object.
    75  //
    76  // This walk will automatically cross links, but requires some configuration
    77  // with link loading functions to do so.
    78  //
    79  // Traversals are defined as visiting a (node,path) tuple.
    80  // This is important to note because when walking DAGs with Links,
    81  // it means you may visit the same node multiple times
    82  // due to having reached it via a different path.
    83  // (You can prevent this by using a LinkLoader function which memoizes a set of
    84  // already-visited Links, and returns a SkipMe when encountering them again.)
    85  //
    86  // WalkMatching (and the other traversal functions) can be used again again inside the VisitFn!
    87  // By using the traversal.Progress handed to the VisitFn,
    88  // the Path recorded of the traversal so far will continue to be extended,
    89  // and thus continued nested uses of Walk and Focus will see the fully contextualized Path.
    90  //
    91  // WalkMatching can be configured to run with a Preloader.
    92  // When a Preloader is configured, the walk will first do a "preload" pass over the initial,
    93  // root tree up to link boundaries and report any links encountered to the preloader.
    94  // It will then perform a second pass over the tree, calling the VisitFn where necessary as per normal WalkMatching behavior.
    95  // This two-pass operation will continue for each block loaded, allowing the preloader to
    96  // potentially asynchronously preload any blocks that are going to be encountered at a future point in the walk.
    97  func (prog Progress) WalkMatching(n datamodel.Node, s selector.Selector, fn VisitFn) error {
    98  	prog.init()
    99  	return prog.walkBlock(n, s, func(prog Progress, n datamodel.Node, tr VisitReason) error {
   100  		if tr != VisitReason_SelectionMatch {
   101  			return nil
   102  		}
   103  		return fn(prog, n)
   104  	})
   105  }
   106  
   107  // WalkLocal is the same as the package-scope function of the same name,
   108  // but considers an existing Progress state (and any config it might reference).
   109  func (prog Progress) WalkLocal(n datamodel.Node, fn VisitFn) error {
   110  	if err := prog.checkNodeBudget(); err != nil {
   111  		return err
   112  	}
   113  
   114  	// Visit the current node.
   115  	if err := fn(prog, n); err != nil {
   116  		if _, ok := err.(SkipMe); ok {
   117  			return nil
   118  		}
   119  		return err
   120  	}
   121  	// Recurse on nodes with a recursive kind; otherwise just return.
   122  	switch n.Kind() {
   123  	case datamodel.Kind_Map:
   124  		for itr := n.MapIterator(); !itr.Done(); {
   125  			k, v, err := itr.Next()
   126  			if err != nil {
   127  				return err
   128  			}
   129  			ks, _ := k.AsString()
   130  			progNext := prog
   131  			progNext.Path = prog.Path.AppendSegmentString(ks)
   132  			if err := progNext.WalkLocal(v, fn); err != nil {
   133  				return err
   134  			}
   135  		}
   136  		return nil
   137  	case datamodel.Kind_List:
   138  		for itr := n.ListIterator(); !itr.Done(); {
   139  			idx, v, err := itr.Next()
   140  			if err != nil {
   141  				return err
   142  			}
   143  			progNext := prog
   144  			progNext.Path = prog.Path.AppendSegmentInt(idx)
   145  			if err := progNext.WalkLocal(v, fn); err != nil {
   146  				return err
   147  			}
   148  		}
   149  		return nil
   150  	default:
   151  		return nil
   152  	}
   153  }
   154  
   155  // WalkAdv is identical to WalkMatching, except it is called for *all* nodes
   156  // visited (not just matching nodes), together with the reason for the visit.
   157  // An AdvVisitFn is used instead of a VisitFn, so that the reason can be provided.
   158  func (prog Progress) WalkAdv(n datamodel.Node, s selector.Selector, fn AdvVisitFn) error {
   159  	prog.init()
   160  	return prog.walkBlock(n, s, fn)
   161  }
   162  
   163  // walkBlock anchors a walk at the beginning of the traversal and at the
   164  // beginning of each new link traversed. This allows us to do a preload phase if
   165  // we have a preloader configured.
   166  func (prog Progress) walkBlock(n datamodel.Node, s selector.Selector, visitFn AdvVisitFn) error {
   167  	ph := phaseTraverse
   168  	var budget *Budget
   169  
   170  	if prog.Cfg.Preloader != nil {
   171  		ph = phasePreload
   172  		// preserve the budget so we can reset it for the second pass; it will
   173  		// likely not correctly apply during the preload phase because it
   174  		// doesn't descend into links first. But we'll use it anyway as a
   175  		// best-guess because we have nothing better
   176  		budget = prog.Budget.Clone()
   177  	}
   178  
   179  	// First pass.
   180  	err := prog.walkAdv(ph, n, s, visitFn)
   181  	if err != nil && (ph != phasePreload || !errors.Is(&ErrBudgetExceeded{}, err)) {
   182  		return err
   183  	}
   184  
   185  	if ph == phasePreload {
   186  		// First past was a preload; now do the _real_ pass.
   187  		prog.Budget = budget // reset
   188  		return prog.walkAdv(phaseTraverse, n, s, visitFn)
   189  	}
   190  
   191  	return nil
   192  }
   193  
   194  // walkAdv is the main recursive walk function, called to iterate through
   195  // recursive nodes (root node, maps, lists and new link root nodes).
   196  func (prog Progress) walkAdv(ph phase, n datamodel.Node, s selector.Selector, visitFn AdvVisitFn) error {
   197  	if err := prog.checkNodeBudget(); err != nil {
   198  		return err
   199  	}
   200  
   201  	// If we need to interpret this node in an alternative form, reify and replace.
   202  	if rn, rs, err := prog.reify(n, s); err != nil {
   203  		return err
   204  	} else if rn != nil {
   205  		n = rn
   206  		s = rs
   207  	}
   208  
   209  	// Call the visit function if necessary.
   210  	if err := prog.visit(ph, n, s, visitFn); err != nil {
   211  		return err
   212  	}
   213  
   214  	// If we're handling scalars (e.g. not maps and lists) we can return now.
   215  	switch n.Kind() {
   216  	case datamodel.Kind_Map, datamodel.Kind_List: // continue
   217  	default:
   218  		return nil
   219  	}
   220  
   221  	// For maps and lists: recurse (in one of two ways, depending on if the selector also states specific interests).
   222  
   223  	haveStartAtPath := prog.Cfg.StartAtPath.Len() > 0
   224  	var reachedStartAtPath bool
   225  	recurse := func(v datamodel.Node, ps datamodel.PathSegment) error {
   226  		// First, make sure we're past the start path; if one is specified.
   227  		if haveStartAtPath {
   228  			if reachedStartAtPath {
   229  				prog.PastStartAtPath = reachedStartAtPath
   230  			} else if !prog.PastStartAtPath && prog.Path.Len() < prog.Cfg.StartAtPath.Len() {
   231  				if ps.Equals(prog.Cfg.StartAtPath.Segments()[prog.Path.Len()]) {
   232  					reachedStartAtPath = true
   233  				}
   234  				if !reachedStartAtPath {
   235  					return nil
   236  				}
   237  			}
   238  		}
   239  
   240  		if err := prog.explore(ph, s, n, visitFn, v, ps); err != nil {
   241  			return err
   242  		}
   243  
   244  		return nil
   245  	}
   246  
   247  	attn := s.Interests()
   248  
   249  	if attn == nil { // no specific interests; recurse on all children.
   250  		for itr := selector.NewSegmentIterator(n); !itr.Done(); {
   251  			ps, v, err := itr.Next()
   252  			if err != nil {
   253  				return err
   254  			}
   255  			if err := recurse(v, ps); err != nil {
   256  				return err
   257  			}
   258  		}
   259  
   260  		return nil
   261  	}
   262  
   263  	if len(attn) == 0 { // nothing to see here
   264  		return nil
   265  	}
   266  
   267  	// specific interests, recurse on those.
   268  	for _, ps := range attn {
   269  		if v, err := n.LookupBySegment(ps); err != nil {
   270  			continue
   271  		} else if err := recurse(v, ps); err != nil {
   272  			return err
   273  		}
   274  	}
   275  
   276  	return nil
   277  }
   278  
   279  func (prog Progress) checkNodeBudget() error {
   280  	if prog.Budget != nil {
   281  		if prog.Budget.NodeBudget <= 0 {
   282  			return &ErrBudgetExceeded{BudgetKind: "node", Path: prog.Path}
   283  		}
   284  		prog.Budget.NodeBudget--
   285  	}
   286  	return nil
   287  }
   288  
   289  func (prog Progress) checkLinkBudget(lnk datamodel.Link) error {
   290  	if prog.Budget != nil {
   291  		if prog.Budget.LinkBudget <= 0 {
   292  			return &ErrBudgetExceeded{BudgetKind: "link", Path: prog.Path, Link: lnk}
   293  		}
   294  		prog.Budget.LinkBudget--
   295  	}
   296  	return nil
   297  }
   298  
   299  func (prog Progress) reify(n datamodel.Node, s selector.Selector) (datamodel.Node, selector.Selector, error) {
   300  	// refiy the node if advised.
   301  	if rs, ok := s.(selector.Reifiable); ok {
   302  		adl := rs.NamedReifier()
   303  		if prog.Cfg.LinkSystem.KnownReifiers == nil {
   304  			return nil, nil, fmt.Errorf("adl requested but not supported by link system: %q", adl)
   305  		}
   306  
   307  		reifier, ok := prog.Cfg.LinkSystem.KnownReifiers[adl]
   308  		if !ok {
   309  			return nil, nil, fmt.Errorf("unregistered adl requested: %q", adl)
   310  		}
   311  
   312  		rn, err := reifier(linking.LinkContext{
   313  			Ctx:      prog.Cfg.Ctx,
   314  			LinkPath: prog.Path,
   315  		}, n, &prog.Cfg.LinkSystem)
   316  		if err != nil {
   317  			return nil, nil, fmt.Errorf("failed to reify node as %q: %w", adl, err)
   318  		}
   319  
   320  		// explore into the `InterpretAs` clause to the child selector.
   321  		s, err = s.Explore(n, datamodel.PathSegment{})
   322  		if err != nil {
   323  			return nil, nil, err
   324  		}
   325  		return rn, s, nil
   326  	}
   327  
   328  	return nil, nil, nil
   329  }
   330  
   331  // visit calls the visitor if required
   332  func (prog Progress) visit(ph phase, n datamodel.Node, s selector.Selector, visitFn AdvVisitFn) error {
   333  	if ph != phaseTraverse {
   334  		return nil
   335  	}
   336  
   337  	if !prog.PastStartAtPath && prog.Path.Len() < prog.Cfg.StartAtPath.Len() {
   338  		return nil
   339  	}
   340  
   341  	// Decide if this node is matched -- do callbacks as appropriate.
   342  	match, err := s.Match(n)
   343  	if err != nil {
   344  		return err
   345  	}
   346  	if match != nil {
   347  		return visitFn(prog, match, VisitReason_SelectionMatch)
   348  	}
   349  	return visitFn(prog, n, VisitReason_SelectionCandidate)
   350  }
   351  
   352  // explore is called to explore a single node, and recurse into it if necessary,
   353  // including loading and recursing into links if the node is a link.
   354  func (prog Progress) explore(
   355  	ph phase,
   356  	s selector.Selector,
   357  	n datamodel.Node,
   358  	visitFn AdvVisitFn,
   359  	v datamodel.Node,
   360  	ps datamodel.PathSegment,
   361  ) error {
   362  	sNext, err := s.Explore(n, ps)
   363  	if err != nil {
   364  		return err
   365  	}
   366  	if sNext == nil {
   367  		return nil
   368  	}
   369  
   370  	progNext := prog
   371  	progNext.Path = prog.Path.AppendSegment(ps)
   372  
   373  	if v.Kind() != datamodel.Kind_Link {
   374  		return progNext.walkAdv(ph, v, sNext, visitFn)
   375  	}
   376  
   377  	lnk, _ := v.AsLink()
   378  	if prog.Cfg.LinkVisitOnlyOnce {
   379  		if _, seen := prog.SeenLinks[lnk]; seen {
   380  			return nil
   381  		}
   382  		if ph == phaseTraverse {
   383  			prog.SeenLinks[lnk] = struct{}{}
   384  		}
   385  	}
   386  
   387  	if ph == phasePreload {
   388  		if err := prog.checkLinkBudget(lnk); err != nil {
   389  			return err
   390  		}
   391  		pctx := preload.PreloadContext{
   392  			Ctx:        prog.Cfg.Ctx,
   393  			BasePath:   prog.Path,
   394  			ParentNode: n,
   395  		}
   396  		pl := preload.Link{
   397  			Segment:  ps,
   398  			LinkNode: v,
   399  			Link:     lnk,
   400  		}
   401  		prog.Cfg.Preloader(pctx, pl)
   402  		return nil
   403  	}
   404  
   405  	progNext.LastBlock.Path = progNext.Path
   406  	progNext.LastBlock.Link = lnk
   407  
   408  	v, err = progNext.loadLink(lnk, v, n)
   409  	if err != nil {
   410  		if _, ok := err.(SkipMe); ok {
   411  			return nil
   412  		}
   413  		return err
   414  	}
   415  
   416  	return progNext.walkBlock(v, sNext, visitFn)
   417  }
   418  
   419  // loadLink is called to load a link from the configured LinkSystem with the
   420  // appropriate prototype.
   421  func (prog Progress) loadLink(lnk datamodel.Link, v datamodel.Node, parent datamodel.Node) (datamodel.Node, error) {
   422  	if err := prog.checkLinkBudget(lnk); err != nil {
   423  		return nil, err
   424  	}
   425  	// Put together the context info we'll offer to the loader and prototypeChooser.
   426  	lnkCtx := linking.LinkContext{
   427  		Ctx:        prog.Cfg.Ctx,
   428  		LinkPath:   prog.Path,
   429  		LinkNode:   v,
   430  		ParentNode: parent,
   431  	}
   432  	// Pick what in-memory format we will build.
   433  	np, err := prog.Cfg.LinkTargetNodePrototypeChooser(lnk, lnkCtx)
   434  	if err != nil {
   435  		return nil, fmt.Errorf("error traversing node at %q: could not load link %q: %w", prog.Path, lnk, err)
   436  	}
   437  	// Load link!
   438  	n, err := prog.Cfg.LinkSystem.Load(lnkCtx, lnk, np)
   439  	if err != nil {
   440  		if _, ok := err.(SkipMe); ok {
   441  			return nil, err
   442  		}
   443  		return nil, fmt.Errorf("error traversing node at %q: could not load link %q: %w", prog.Path, lnk, err)
   444  	}
   445  	return n, nil
   446  }
   447  
   448  // WalkTransforming walks a graph of Nodes, deciding which to alter by applying a Selector,
   449  // and calls the given TransformFn to decide what new node to replace the visited node with.
   450  // A new Node tree will be returned (the original is unchanged).
   451  //
   452  // If the TransformFn returns the same Node which it was called with,
   453  // then the transform is a no-op; if every visited node is a no-op,
   454  // then the root node returned from the walk as a whole will also be
   455  // the same as its starting Node (no new memory will be used).
   456  //
   457  // When a Node is replaced, no further recursion of this walk will occur on its contents.
   458  // (You can certainly do a additional traversals, including transforms,
   459  // from inside the TransformFn while building the replacement node.)
   460  //
   461  // The prototype (that is, implementation) of Node returned will be the same as the
   462  // prototype of the Nodes at the same positions in the existing tree
   463  // (literally, builders used to construct any new needed intermediate nodes
   464  // are chosen by asking the existing nodes about their prototype).
   465  func (prog Progress) WalkTransforming(n datamodel.Node, s selector.Selector, fn TransformFn) (datamodel.Node, error) {
   466  	prog.init()
   467  	return prog.walkTransforming(n, s, fn)
   468  }
   469  
   470  func (prog Progress) walkTransforming(n datamodel.Node, s selector.Selector, fn TransformFn) (datamodel.Node, error) {
   471  	if err := prog.checkNodeBudget(); err != nil {
   472  		return nil, err
   473  	}
   474  
   475  	if rn, rs, err := prog.reify(n, s); err != nil {
   476  		return nil, err
   477  	} else if rn != nil {
   478  		n = rn
   479  		s = rs
   480  	}
   481  
   482  	// Decide if this node is matched -- do callbacks as appropriate.
   483  	if s.Decide(n) {
   484  		new_n, err := fn(prog, n)
   485  		if err != nil {
   486  			return nil, err
   487  		}
   488  		if new_n != n {
   489  			// don't continue on transformed subtrees
   490  			return new_n, nil
   491  		}
   492  	}
   493  
   494  	// If we're handling scalars (e.g. not maps and lists) we can return now.
   495  	nk := n.Kind()
   496  	switch nk {
   497  	case datamodel.Kind_List:
   498  		return prog.walk_transform_iterateList(n, s, fn, s.Interests())
   499  	case datamodel.Kind_Map:
   500  		return prog.walk_transform_iterateMap(n, s, fn, s.Interests())
   501  	default:
   502  		return n, nil
   503  	}
   504  }
   505  
   506  func contains(interest []datamodel.PathSegment, candidate datamodel.PathSegment) bool {
   507  	for _, i := range interest {
   508  		if i == candidate {
   509  			return true
   510  		}
   511  	}
   512  	return false
   513  }
   514  
   515  func (prog Progress) walk_transform_iterateList(n datamodel.Node, s selector.Selector, fn TransformFn, attn []datamodel.PathSegment) (datamodel.Node, error) {
   516  	bldr := n.Prototype().NewBuilder()
   517  	lstBldr, err := bldr.BeginList(n.Length())
   518  	if err != nil {
   519  		return nil, err
   520  	}
   521  	for itr := selector.NewSegmentIterator(n); !itr.Done(); {
   522  		ps, v, err := itr.Next()
   523  		if err != nil {
   524  			return nil, err
   525  		}
   526  		if attn == nil || contains(attn, ps) {
   527  			sNext, err := s.Explore(n, ps)
   528  			if err != nil {
   529  				return nil, err
   530  			}
   531  			if sNext != nil {
   532  				progNext := prog
   533  				progNext.Path = prog.Path.AppendSegment(ps)
   534  				if v.Kind() == datamodel.Kind_Link {
   535  					lnk, _ := v.AsLink()
   536  					if prog.Cfg.LinkVisitOnlyOnce {
   537  						if _, seen := prog.SeenLinks[lnk]; seen {
   538  							continue
   539  						}
   540  						prog.SeenLinks[lnk] = struct{}{}
   541  					}
   542  					progNext.LastBlock.Path = progNext.Path
   543  					progNext.LastBlock.Link = lnk
   544  					v, err = progNext.loadLink(lnk, v, n)
   545  					if err != nil {
   546  						if _, ok := err.(SkipMe); ok {
   547  							continue
   548  						}
   549  						return nil, err
   550  					}
   551  				}
   552  
   553  				next, err := progNext.WalkTransforming(v, sNext, fn)
   554  				if err != nil {
   555  					return nil, err
   556  				}
   557  				if err := lstBldr.AssembleValue().AssignNode(next); err != nil {
   558  					return nil, err
   559  				}
   560  			} else {
   561  				if err := lstBldr.AssembleValue().AssignNode(v); err != nil {
   562  					return nil, err
   563  				}
   564  			}
   565  		} else {
   566  			if err := lstBldr.AssembleValue().AssignNode(v); err != nil {
   567  				return nil, err
   568  			}
   569  		}
   570  	}
   571  	if err := lstBldr.Finish(); err != nil {
   572  		return nil, err
   573  	}
   574  	return bldr.Build(), nil
   575  }
   576  
   577  func (prog Progress) walk_transform_iterateMap(n datamodel.Node, s selector.Selector, fn TransformFn, attn []datamodel.PathSegment) (datamodel.Node, error) {
   578  	bldr := n.Prototype().NewBuilder()
   579  	mapBldr, err := bldr.BeginMap(n.Length())
   580  	if err != nil {
   581  		return nil, err
   582  	}
   583  
   584  	for itr := selector.NewSegmentIterator(n); !itr.Done(); {
   585  		ps, v, err := itr.Next()
   586  		if err != nil {
   587  			return nil, err
   588  		}
   589  		if err := mapBldr.AssembleKey().AssignString(ps.String()); err != nil {
   590  			return nil, err
   591  		}
   592  
   593  		if attn == nil || contains(attn, ps) {
   594  			sNext, err := s.Explore(n, ps)
   595  			if err != nil {
   596  				return nil, err
   597  			}
   598  			if sNext != nil {
   599  				progNext := prog
   600  				progNext.Path = prog.Path.AppendSegment(ps)
   601  				if v.Kind() == datamodel.Kind_Link {
   602  					lnk, _ := v.AsLink()
   603  					if prog.Cfg.LinkVisitOnlyOnce {
   604  						if _, seen := prog.SeenLinks[lnk]; seen {
   605  							continue
   606  						}
   607  						prog.SeenLinks[lnk] = struct{}{}
   608  					}
   609  					progNext.LastBlock.Path = progNext.Path
   610  					progNext.LastBlock.Link = lnk
   611  					v, err = progNext.loadLink(lnk, v, n)
   612  					if err != nil {
   613  						if _, ok := err.(SkipMe); ok {
   614  							continue
   615  						}
   616  						return nil, err
   617  					}
   618  				}
   619  
   620  				next, err := progNext.WalkTransforming(v, sNext, fn)
   621  				if err != nil {
   622  					return nil, err
   623  				}
   624  				if err := mapBldr.AssembleValue().AssignNode(next); err != nil {
   625  					return nil, err
   626  				}
   627  			} else {
   628  				if err := mapBldr.AssembleValue().AssignNode(v); err != nil {
   629  					return nil, err
   630  				}
   631  			}
   632  		} else {
   633  			if err := mapBldr.AssembleValue().AssignNode(v); err != nil {
   634  				return nil, err
   635  			}
   636  		}
   637  	}
   638  	if err := mapBldr.Finish(); err != nil {
   639  		return nil, err
   640  	}
   641  	return bldr.Build(), nil
   642  }