go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/graph/graph.go (about)

     1  // Copyright 2018 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package graph
    16  
    17  import (
    18  	"container/list"
    19  	"fmt"
    20  	"sort"
    21  
    22  	"go.starlark.net/starlark"
    23  	"go.starlark.net/starlarkstruct"
    24  
    25  	"go.chromium.org/luci/common/errors"
    26  	"go.chromium.org/luci/starlark/builtins"
    27  )
    28  
    29  var (
    30  	// ErrFinalized is returned by Graph methods that modify the graph when they
    31  	// are used on a finalized graph.
    32  	ErrFinalized = errors.New("cannot modify a finalized graph")
    33  
    34  	// ErrNotFinalized is returned by Graph traversal/query methods when they are
    35  	// used with a non-finalized graph.
    36  	ErrNotFinalized = errors.New("cannot query a graph under construction")
    37  )
    38  
    39  // backtrace returns an error message annotated with a backtrace.
    40  func backtrace(err error, t *builtins.CapturedStacktrace) string {
    41  	return t.String() + "Error: " + err.Error()
    42  }
    43  
    44  // NodeRedeclarationError is returned when a adding an existing node.
    45  type NodeRedeclarationError struct {
    46  	Trace    *builtins.CapturedStacktrace // where it is added second time
    47  	Previous *Node                        // the previously added node
    48  }
    49  
    50  // Error is part of 'error' interface.
    51  func (e *NodeRedeclarationError) Error() string {
    52  	// TODO(vadimsh): Improve error messages.
    53  	return fmt.Sprintf("%s is redeclared, previous declaration:\n%s", e.Previous, e.Previous.Trace)
    54  }
    55  
    56  // Backtrace returns an error message with a backtrace where it happened.
    57  func (e *NodeRedeclarationError) Backtrace() string {
    58  	return backtrace(e, e.Trace)
    59  }
    60  
    61  // CycleError is returned when adding an edge that introduces a cycle.
    62  //
    63  // Nodes referenced by edges may not be fully declared yet (i.e. they may not
    64  // yet have properties or trace associated with them). They do have valid keys
    65  // though.
    66  type CycleError struct {
    67  	Trace *builtins.CapturedStacktrace // where the edge is being added
    68  	Edge  *Edge                        // an edge being added
    69  	Path  []*Edge                      // the rest of the cycle
    70  }
    71  
    72  // Error is part of 'error' interface.
    73  func (e *CycleError) Error() string {
    74  	// TODO(vadimsh): Improve error messages.
    75  	return fmt.Sprintf("relation %q between %s and %s introduces a cycle",
    76  		e.Edge.Title, e.Edge.Parent, e.Edge.Child)
    77  }
    78  
    79  // Backtrace returns an error message with a backtrace where it happened.
    80  func (e *CycleError) Backtrace() string {
    81  	return backtrace(e, e.Trace)
    82  }
    83  
    84  // DanglingEdgeError is returned by Finalize if a graph has an edge whose parent
    85  // or child (or both) haven't been declared by AddNode.
    86  //
    87  // Use Edge.(Parent|Child).Declared() to figure out which end is not defined.
    88  type DanglingEdgeError struct {
    89  	Edge *Edge
    90  }
    91  
    92  // Error is part of 'error' interface.
    93  func (e *DanglingEdgeError) Error() string {
    94  	rel := ""
    95  	if e.Edge.Title != "" {
    96  		rel = fmt.Sprintf(" in %q", e.Edge.Title)
    97  	}
    98  
    99  	// TODO(vadimsh): Improve error messages.
   100  	hasP := e.Edge.Parent.Declared()
   101  	hasC := e.Edge.Child.Declared()
   102  	switch {
   103  	case hasP && hasC:
   104  		// This should not happen.
   105  		return "incorrect DanglingEdgeError, the edge is fully connected"
   106  	case !hasP && hasC:
   107  		return fmt.Sprintf("%s%s refers to undefined %s",
   108  			e.Edge.Child, rel, e.Edge.Parent)
   109  	case hasP && !hasC:
   110  		return fmt.Sprintf("%s%s refers to undefined %s",
   111  			e.Edge.Parent, rel, e.Edge.Child)
   112  	default:
   113  		return fmt.Sprintf("relation %q: refers to %s and %s, neither is defined",
   114  			e.Edge.Title, e.Edge.Parent, e.Edge.Child)
   115  	}
   116  }
   117  
   118  // Backtrace returns an error message with a backtrace where it happened.
   119  func (e *DanglingEdgeError) Backtrace() string {
   120  	return backtrace(e, e.Edge.Trace)
   121  }
   122  
   123  // Graph is a DAG of keyed nodes.
   124  //
   125  // It is initially in "under construction" state, in which callers can use
   126  // AddNode and AddEdge (in any order) to build the graph, but can't yet query
   127  // it.
   128  //
   129  // Once the construction is complete, the graph should be finalized via
   130  // Finalize() call, which checks there are no dangling edges and freezes the
   131  // graph (so AddNode/AddEdge return errors), making it queryable.
   132  //
   133  // Graph implements starlark.HasAttrs interface and have the following methods:
   134  //   - key(kind1: string, id1: string, kind2: string, id2: string, ...): graph.key
   135  //   - add_node(key: graph.key, props={}, idempotent=False, trace=stacktrace()): graph.node
   136  //   - add_edge(parent: graph.Key, child: graph.Key, title=”, trace=stacktrace())
   137  //   - finalize(): []string
   138  //   - node(key: graph.key): graph.node
   139  //   - children(parent: graph.key, order_by='key'): []graph.node
   140  //   - descendants(root: graph.key, callback=None, order_by='key', topology='breadth'): []graph.node
   141  //   - parents(child: graph.key, order_by='key'): []graph.node
   142  //   - sorted_nodes(nodes: iterable<graph.node>, order_by='key'): []graph.node
   143  type Graph struct {
   144  	KeySet
   145  
   146  	nodes     map[*Key]*Node // all declared and "predeclared" nodes
   147  	edges     []*Edge        // all defined edges, in their definition order
   148  	nextIndex int            // index to assign to the next node, for ordering
   149  	finalized bool           // true if the graph is no longer mutable
   150  }
   151  
   152  // Visitor visits a node, looks at next possible candidates for a visit and
   153  // returns ones that it really wants to visit.
   154  type Visitor func(n *Node, next []*Node) ([]*Node, error)
   155  
   156  // validateOrder returns an error if the edge traversal order is unrecognized.
   157  func validateOrder(orderBy string) error {
   158  	switch orderBy {
   159  	case "key", "~key", "def", "~def":
   160  		return nil
   161  	}
   162  	return fmt.Errorf("unknown order %q, expecting one of \"key\", \"~key\", \"def\", \"~def\"", orderBy)
   163  }
   164  
   165  // validateTopology returns an error if the traversal topology is unrecognized.
   166  func validateTopology(topology string) error {
   167  	if topology != "breadth" && topology != "depth" {
   168  		return fmt.Errorf("unknown topology %q, expecting either \"breadth\" or \"depth\"", topology)
   169  	}
   170  	return nil
   171  }
   172  
   173  // validateKey returns an error if the key is not from this graph.
   174  func (g *Graph) validateKey(title string, k *Key) error {
   175  	if k.set != &g.KeySet {
   176  		return fmt.Errorf("bad %s: %s is from another graph", title, k)
   177  	}
   178  	return nil
   179  }
   180  
   181  // initNode either returns an existing *Node, or adds a new one.
   182  //
   183  // The newly added node is in "predeclared" state: it has no Props or Trace.
   184  // A predeclared node is moved to the fully declared state by AddNode, this can
   185  // happen only once for non-idempotent nodes or more than once for idempotent
   186  // nodes, if each time their props dict is exactly same.
   187  //
   188  // Panics if the graph is finalized.
   189  func (g *Graph) initNode(k *Key) *Node {
   190  	if g.finalized {
   191  		panic(ErrFinalized)
   192  	}
   193  	if n := g.nodes[k]; n != nil {
   194  		return n
   195  	}
   196  	if g.nodes == nil {
   197  		g.nodes = make(map[*Key]*Node, 1)
   198  	}
   199  	n := &Node{Key: k}
   200  	g.nodes[k] = n
   201  	return n
   202  }
   203  
   204  //// API to mutate the graph before it is finalized.
   205  
   206  // AddNode adds a node to the graph.
   207  //
   208  // If such node already exists, either returns an error right away (if
   209  // 'idempotent' is false), or verifies the existing node has also been marked
   210  // as idempotent and has exact same props dict as being passed here.
   211  //
   212  // Trying to use AddNode after the graph has been finalized is an error.
   213  //
   214  // Freezes props.values() as a side effect.
   215  func (g *Graph) AddNode(k *Key, props *starlark.Dict, idempotent bool, trace *builtins.CapturedStacktrace) error {
   216  	if g.finalized {
   217  		return ErrFinalized
   218  	}
   219  	if err := g.validateKey("key", k); err != nil {
   220  		return err
   221  	}
   222  
   223  	// Only string keys are allowed in 'props'.
   224  	for _, pk := range props.Keys() {
   225  		if _, ok := pk.(starlark.String); !ok {
   226  			return fmt.Errorf("non-string key %s in 'props'", pk)
   227  		}
   228  	}
   229  	propsStruct := starlarkstruct.FromKeywords(starlark.String("props"), props.Items())
   230  
   231  	n := g.initNode(k)
   232  	if !n.Declared() {
   233  		n.declare(g.nextIndex, propsStruct, idempotent, trace)
   234  		g.nextIndex++
   235  		return nil
   236  	}
   237  
   238  	// Only idempotent nodes can be redeclared, and only if all declarations
   239  	// are marked as idempotent and they specify exact same props.
   240  	if n.Idempotent && idempotent {
   241  		switch eq, err := starlark.Equal(propsStruct, n.Props); {
   242  		case err != nil:
   243  			return err
   244  		case eq:
   245  			return nil
   246  		}
   247  	}
   248  	return &NodeRedeclarationError{Trace: trace, Previous: n}
   249  }
   250  
   251  // AddEdge adds an edge to the graph.
   252  //
   253  // Trying to use AddEdge after the graph has been finalized is an error.
   254  //
   255  // Neither of the nodes have to exist yet: it is OK to declare nodes and edges
   256  // in arbitrary order as long as at the end of the graph construction (when it
   257  // is finalized) the graph is complete.
   258  //
   259  // It is OK to add the same edge (with the same title) more than once. Only
   260  // the trace of the first definition is recorded.
   261  //
   262  // Returns an error if the new edge introduces a cycle.
   263  func (g *Graph) AddEdge(parent, child *Key, title string, trace *builtins.CapturedStacktrace) error {
   264  	if g.finalized {
   265  		return ErrFinalized
   266  	}
   267  	if err := g.validateKey("parent", parent); err != nil {
   268  		return err
   269  	}
   270  	if err := g.validateKey("child", child); err != nil {
   271  		return err
   272  	}
   273  
   274  	edge := &Edge{
   275  		Parent: g.initNode(parent),
   276  		Child:  g.initNode(child),
   277  		Title:  title,
   278  		Trace:  trace,
   279  	}
   280  
   281  	// Have this exact edge already? This is fine.
   282  	for _, e := range edge.Parent.children {
   283  		if e.Child == edge.Child && e.Title == title {
   284  			return nil
   285  		}
   286  	}
   287  
   288  	// The child may have the parent among its descendants already? Then adding
   289  	// the edge would introduce a cycle.
   290  	err := edge.Child.visitDescendants(nil, func(n *Node, path []*Edge) error {
   291  		if n == edge.Parent {
   292  			return &CycleError{
   293  				Trace: trace,
   294  				Edge:  edge,
   295  				Path:  append([]*Edge(nil), path...),
   296  			}
   297  		}
   298  		return nil
   299  	})
   300  	if err != nil {
   301  		return err
   302  	}
   303  
   304  	edge.Parent.children = append(edge.Parent.children, edge)
   305  	edge.Child.parents = append(edge.Child.parents, edge)
   306  	g.edges = append(g.edges, edge)
   307  	return nil
   308  }
   309  
   310  // Finalize finishes the graph construction by verifying there are no "dangling"
   311  // edges: all edges ever added by AddEdge should connect nodes that were at some
   312  // point defined by AddNode.
   313  //
   314  // Finalizing an already finalized graph is not an error.
   315  //
   316  // A finalized graph is immutable (and frozen in Starlark sense): all subsequent
   317  // calls to AddNode/AddEdge return an error. Conversely, freezing the graph via
   318  // Freeze() finalizes it, panicking if the finalization fails. Users of Graph
   319  // are expected to finalize the graph themselves (checking errors) before
   320  // Starlark tries to freeze it.
   321  //
   322  // Once finalized, the graph becomes queryable.
   323  func (g *Graph) Finalize() (errs errors.MultiError) {
   324  	if g.finalized {
   325  		return
   326  	}
   327  
   328  	for _, e := range g.edges {
   329  		if !e.Parent.Declared() || !e.Child.Declared() {
   330  			errs = append(errs, &DanglingEdgeError{Edge: e})
   331  		}
   332  	}
   333  
   334  	g.finalized = len(errs) == 0
   335  	return
   336  }
   337  
   338  //// API to query the graph after it is finalized.
   339  
   340  // Node returns a node by the key or (nil, nil) if there's no such node.
   341  //
   342  // Trying to use Node before the graph has been finalized is an error.
   343  func (g *Graph) Node(k *Key) (*Node, error) {
   344  	if !g.finalized {
   345  		return nil, ErrNotFinalized
   346  	}
   347  	if err := g.validateKey("key", k); err != nil {
   348  		return nil, err
   349  	}
   350  	switch n := g.nodes[k]; {
   351  	case n == nil:
   352  		return nil, nil
   353  	case !n.Declared():
   354  		panic(fmt.Errorf("impossible not-yet-declared node in a finalized graph: %s", n.Key))
   355  	default:
   356  		return n, nil
   357  	}
   358  }
   359  
   360  // Children returns direct children of a node (given by its key).
   361  //
   362  // The order of the result depends on a value of 'orderBy':
   363  //
   364  //	'key': nodes are ordered lexicographically by their keys (smaller first).
   365  //	'def': nodes are ordered by the order edges to them were defined during
   366  //	     the execution (earlier first).
   367  //
   368  // Any other value of 'orderBy' causes an error.
   369  //
   370  // A missing node is considered to have no children.
   371  //
   372  // Trying to use Children before the graph has been finalized is an error.
   373  func (g *Graph) Children(parent *Key, orderBy string) ([]*Node, error) {
   374  	return g.orderedRelatives(parent, "parent", orderBy, (*Node).listChildren)
   375  }
   376  
   377  // Descendants recursively visits 'root' and all its children, in breadth or
   378  // depth first order.
   379  //
   380  // Returns the list of all visited nodes, in order they were visited, including
   381  // 'root' itself. If 'root' is missing, returns an empty list.
   382  //
   383  // The order of enumeration of direct children of a node depends on a value of
   384  // 'orderBy':
   385  //
   386  //	'key': nodes are ordered lexicographically by their keys (smaller first).
   387  //	'def': nodes are ordered by the order edges to them were defined during
   388  //	     the execution (earlier first).
   389  //
   390  // '~' in front (i.e. ~key' and '~def') means "reverse order".
   391  //
   392  // Any other value of 'orderBy' causes an error.
   393  //
   394  // Each node is visited only once, even if it is reachable through multiple
   395  // paths. Note that the graph has no cycles (by construction).
   396  //
   397  // The visitor callback (if not nil) is called for each visited node. It decides
   398  // what children to visit next. The callback always sees all children of the
   399  // node, even if some of them (or all) have already been visited. Visited nodes
   400  // will be skipped even if the visitor returns them.
   401  //
   402  // Trying to use Descendants before the graph has been finalized is an error.
   403  func (g *Graph) Descendants(root *Key, orderBy, topology string, visitor Visitor) ([]*Node, error) {
   404  	if !g.finalized {
   405  		return nil, ErrNotFinalized
   406  	}
   407  	if err := g.validateKey("root", root); err != nil {
   408  		return nil, err
   409  	}
   410  	if err := validateOrder(orderBy); err != nil {
   411  		return nil, err
   412  	}
   413  	if err := validateTopology(topology); err != nil {
   414  		return nil, err
   415  	}
   416  
   417  	rootNode := g.nodes[root]
   418  	if rootNode == nil {
   419  		return nil, nil
   420  	}
   421  
   422  	switch topology {
   423  	case "breadth":
   424  		return descBreadth(rootNode, orderBy, visitor)
   425  	case "depth":
   426  		return descDepth(rootNode, orderBy, visitor)
   427  	default:
   428  		panic("impossible")
   429  	}
   430  }
   431  
   432  // descBreadth implements non-recursive breadth-first traversal.
   433  func descBreadth(root *Node, orderBy string, visitor Visitor) ([]*Node, error) {
   434  	queue := list.New()
   435  	queuedSet := make(map[*Node]struct{}, 1)
   436  	visited := make([]*Node, 0, 1)
   437  
   438  	enqueue := func(n *Node) {
   439  		if _, yes := queuedSet[n]; !yes {
   440  			queue.PushBack(n)
   441  			queuedSet[n] = struct{}{}
   442  		}
   443  	}
   444  	enqueue(root)
   445  
   446  	for queue.Len() > 0 {
   447  		cur := queue.Remove(queue.Front()).(*Node)
   448  		visited = append(visited, cur)
   449  
   450  		// Ask the visitor callback to decide where it wants to go next.
   451  		next, err := filteredChildren(cur, orderBy, visitor)
   452  		if err != nil {
   453  			return nil, err
   454  		}
   455  
   456  		// Go there (eventually).
   457  		for _, n := range next {
   458  			enqueue(n)
   459  		}
   460  	}
   461  	return visited, nil
   462  }
   463  
   464  // descDepth implements recursive depth-first traversal.
   465  func descDepth(root *Node, orderBy string, visitor Visitor) ([]*Node, error) {
   466  	visited := make([]*Node, 0, 1)
   467  	visitedSet := make(map[*Node]struct{}, 1)
   468  
   469  	var visit func(root *Node) error
   470  	visit = func(root *Node) error {
   471  		// Been here before?
   472  		if _, yes := visitedSet[root]; yes {
   473  			return nil
   474  		}
   475  		visitedSet[root] = struct{}{}
   476  
   477  		// Ask the visitor callback to decide where it wants to go next.
   478  		next, err := filteredChildren(root, orderBy, visitor)
   479  		if err != nil {
   480  			return err
   481  		}
   482  
   483  		// Go there, right now.
   484  		for _, n := range next {
   485  			if err := visit(n); err != nil {
   486  				return err
   487  			}
   488  		}
   489  
   490  		// Now that all children are visited, emit the root itself too.
   491  		visited = append(visited, root)
   492  		return nil
   493  	}
   494  
   495  	if err := visit(root); err != nil {
   496  		return nil, err
   497  	}
   498  	return visited, nil
   499  }
   500  
   501  // filteredChildren calls 'visitor' callback to filter the list of children.
   502  //
   503  // Used by Descendants implementation.
   504  func filteredChildren(cur *Node, orderBy string, visitor Visitor) ([]*Node, error) {
   505  	children := sortByEdgeOrder(cur.listChildren(), orderBy)
   506  	if visitor == nil {
   507  		return children, nil
   508  	}
   509  	// Ask the visitor callback to decide where it wants to go next. Verify the
   510  	// callback didn't sneakily return some *Node it saved somewhere before.
   511  	// This may cause infinite recursion and other weird stuff.
   512  	next, err := visitor(cur, children)
   513  	if err != nil {
   514  		return nil, err
   515  	}
   516  	allowed := make(map[*Node]struct{}, len(children))
   517  	for _, n := range children {
   518  		allowed[n] = struct{}{}
   519  	}
   520  	for _, n := range next {
   521  		if _, yes := allowed[n]; !yes {
   522  			return nil, fmt.Errorf("the callback unexpectedly returned %s which is not a child of %s", n, cur)
   523  		}
   524  	}
   525  	return next, nil
   526  }
   527  
   528  // Parents returns direct parents of a node (given by its key).
   529  //
   530  // The order of the result depends on a value of 'orderBy':
   531  //
   532  //	'key': nodes are ordered lexicographically by their keys (smaller first).
   533  //	'def': nodes are ordered by the order edges to them were defined during
   534  //	     the execution (earlier first).
   535  //
   536  // Any other value of 'orderBy' causes an error.
   537  //
   538  // A missing node is considered to have no parents.
   539  //
   540  // Trying to use Parents before the graph has been finalized is an error.
   541  func (g *Graph) Parents(child *Key, orderBy string) ([]*Node, error) {
   542  	return g.orderedRelatives(child, "child", orderBy, (*Node).listParents)
   543  }
   544  
   545  // SortNodes sorts a slice of nodes of this graph in-place.
   546  //
   547  // The order of the result depends on the value of 'orderBy':
   548  //
   549  //	'key': nodes are ordered lexicographically by their keys (smaller first).
   550  //	'def': nodes are ordered by the order they were defined in the graph.
   551  //
   552  // Any other value of 'orderBy' causes an error.
   553  func (g *Graph) SortNodes(nodes []*Node, orderBy string) error {
   554  	if err := validateOrder(orderBy); err != nil {
   555  		return err
   556  	}
   557  
   558  	// Only nodes from the same graph are comparable to each other. Comparing
   559  	// .Index from different graphs makes no sense.
   560  	for _, n := range nodes {
   561  		if !n.BelongsTo(g) {
   562  			return fmt.Errorf("bad node %s - from another graph", n)
   563  		}
   564  	}
   565  
   566  	switch orderBy {
   567  	case "def":
   568  		sort.Slice(nodes, func(i, j int) bool { return nodes[i].Index < nodes[j].Index })
   569  	case "key":
   570  		sort.Slice(nodes, func(i, j int) bool { return nodes[i].Key.Less(nodes[j].Key) })
   571  	default:
   572  		// Must not happen, orderBy must already be validated here.
   573  		panic(fmt.Sprintf("unknown order %q", orderBy))
   574  	}
   575  	return nil
   576  }
   577  
   578  // orderedRelatives is a common implementation of Children and Parents.
   579  func (g *Graph) orderedRelatives(key *Key, attr, orderBy string, cb func(n *Node) []*Node) ([]*Node, error) {
   580  	if !g.finalized {
   581  		return nil, ErrNotFinalized
   582  	}
   583  	if err := g.validateKey(attr, key); err != nil {
   584  		return nil, err
   585  	}
   586  	if err := validateOrder(orderBy); err != nil {
   587  		return nil, err
   588  	}
   589  	if n := g.nodes[key]; n != nil {
   590  		return sortByEdgeOrder(cb(n), orderBy), nil
   591  	}
   592  	return nil, nil // no node at all -> no related nodes
   593  }
   594  
   595  // sortByEdgeOrder orders either children or parents of some node according to
   596  // the order of edges to/from them.
   597  //
   598  // Assumes 'nodes' came from either .listChildren() or .listParents(), and thus
   599  // are mutable copies, which are already ordered by 'def' order.
   600  //
   601  // Note that 'def' order here means "in order edges were defined". This is
   602  // different from "in order nodes themselves were defined". This is different
   603  // from the meaning of 'def' order in SortNodes.
   604  func sortByEdgeOrder(nodes []*Node, orderBy string) []*Node {
   605  	switch orderBy {
   606  	case "def":
   607  		// default
   608  	case "~def":
   609  		for i, j := 0, len(nodes)-1; i < j; i, j = i+1, j-1 {
   610  			nodes[i], nodes[j] = nodes[j], nodes[i]
   611  		}
   612  	case "key":
   613  		sort.Slice(nodes, func(i, j int) bool { return nodes[i].Key.Less(nodes[j].Key) })
   614  	case "~key":
   615  		sort.Slice(nodes, func(i, j int) bool { return nodes[j].Key.Less(nodes[i].Key) })
   616  	default:
   617  		// Must not happen, orderBy must already be validated here.
   618  		panic(fmt.Sprintf("unknown order %q", orderBy))
   619  	}
   620  	return nodes
   621  }
   622  
   623  //// starlark.Value interface implementation.
   624  
   625  // String is a part of starlark.Value interface
   626  func (g *Graph) String() string { return "graph" }
   627  
   628  // Type is a part of starlark.Value interface.
   629  func (g *Graph) Type() string { return "graph" }
   630  
   631  // Freeze is a part of starlark.Value interface.
   632  //
   633  // It finalizes the graph, panicking on errors. Users of Graph are expected to
   634  // finalize the graph themselves (checking errors) before Starlark tries to
   635  // freeze it.
   636  func (g *Graph) Freeze() {
   637  	if err := g.Finalize(); err != nil {
   638  		panic(err)
   639  	}
   640  }
   641  
   642  // Truth is a part of starlark.Value interface.
   643  func (g *Graph) Truth() starlark.Bool { return starlark.True }
   644  
   645  // Hash is a part of starlark.Value interface.
   646  func (g *Graph) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable") }
   647  
   648  // AttrNames is a part of starlark.HasAttrs interface.
   649  func (g *Graph) AttrNames() []string {
   650  	names := make([]string, 0, len(graphAttrs))
   651  	for k := range graphAttrs {
   652  		names = append(names, k)
   653  	}
   654  	sort.Strings(names)
   655  	return names
   656  }
   657  
   658  // Attr is a part of starlark.HasAttrs interface.
   659  func (g *Graph) Attr(name string) (starlark.Value, error) {
   660  	impl, ok := graphAttrs[name]
   661  	if !ok {
   662  		return nil, nil // per Attr(...) contract
   663  	}
   664  	return impl.BindReceiver(g), nil
   665  }
   666  
   667  //// Starlark bindings for individual graph methods.
   668  
   669  func nodesList(nodes []*Node) *starlark.List {
   670  	vals := make([]starlark.Value, len(nodes))
   671  	for i, n := range nodes {
   672  		vals[i] = n
   673  	}
   674  	return starlark.NewList(vals)
   675  }
   676  
   677  var graphAttrs = map[string]*starlark.Builtin{
   678  	// key(kind1: string, id1: string, kind2: string, id2: string, ...): graph.key
   679  	"key": starlark.NewBuiltin("key", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   680  		if len(kwargs) != 0 {
   681  			return nil, fmt.Errorf("graph.key: not expecting keyword arguments")
   682  		}
   683  		pairs := make([]string, len(args))
   684  		for idx, arg := range args {
   685  			str, ok := arg.(starlark.String)
   686  			if !ok {
   687  				return nil, fmt.Errorf("graph.key: all arguments must be strings, arg #%d was %s", idx, arg.Type())
   688  			}
   689  			pairs[idx] = str.GoString()
   690  		}
   691  		return b.Receiver().(*Graph).Key(pairs...)
   692  	}),
   693  
   694  	// add_node(key: graph.key, props={}, idempotent=False, trace=stacktrace()): graph.node
   695  	"add_node": starlark.NewBuiltin("add_node", func(th *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   696  		var key *Key
   697  		var props *starlark.Dict
   698  		var idempotent starlark.Bool
   699  		var trace *builtins.CapturedStacktrace
   700  		err := starlark.UnpackArgs("add_node", args, kwargs,
   701  			"key", &key,
   702  			"props?", &props,
   703  			"idempotent?", &idempotent,
   704  			"trace?", &trace,
   705  		)
   706  		if err != nil {
   707  			return nil, err
   708  		}
   709  		if props == nil {
   710  			props = &starlark.Dict{}
   711  		}
   712  		if trace == nil {
   713  			var err error
   714  			if trace, err = builtins.CaptureStacktrace(th, 0); err != nil {
   715  				return nil, err
   716  			}
   717  		}
   718  		err = b.Receiver().(*Graph).AddNode(key, props, bool(idempotent), trace)
   719  		return starlark.None, err
   720  	}),
   721  
   722  	// add_edge(parent: graph.Key, child: graph.Key, title='', trace=stacktrace())
   723  	"add_edge": starlark.NewBuiltin("add_edge", func(th *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   724  		var parent *Key
   725  		var child *Key
   726  		var title starlark.String
   727  		var trace *builtins.CapturedStacktrace
   728  		err := starlark.UnpackArgs("add_edge", args, kwargs,
   729  			"parent", &parent,
   730  			"child", &child,
   731  			"title?", &title,
   732  			"trace?", &trace)
   733  		if err != nil {
   734  			return nil, err
   735  		}
   736  
   737  		if trace == nil {
   738  			var err error
   739  			if trace, err = builtins.CaptureStacktrace(th, 0); err != nil {
   740  				return nil, err
   741  			}
   742  		}
   743  
   744  		err = b.Receiver().(*Graph).AddEdge(parent, child, title.GoString(), trace)
   745  		return starlark.None, err
   746  	}),
   747  
   748  	// finalize(): []string
   749  	"finalize": starlark.NewBuiltin("finalize", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   750  		if err := starlark.UnpackArgs("finalize", args, kwargs); err != nil {
   751  			return nil, err
   752  		}
   753  		errs := b.Receiver().(*Graph).Finalize()
   754  		out := make([]starlark.Value, len(errs))
   755  		for i, err := range errs {
   756  			out[i] = starlark.String(err.Error())
   757  		}
   758  		return starlark.NewList(out), nil
   759  	}),
   760  
   761  	// node(key: graph.key): graph.node
   762  	"node": starlark.NewBuiltin("node", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   763  		var key *Key
   764  		if err := starlark.UnpackArgs("node", args, kwargs, "key", &key); err != nil {
   765  			return nil, err
   766  		}
   767  		if node, err := b.Receiver().(*Graph).Node(key); node != nil || err != nil {
   768  			return node, err
   769  		}
   770  		return starlark.None, nil
   771  	}),
   772  
   773  	// children(parent: graph.key, order_by='key'): []graph.node
   774  	"children": starlark.NewBuiltin("children", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   775  		var parent *Key
   776  		var orderBy starlark.String = "key"
   777  		err := starlark.UnpackArgs("children", args, kwargs,
   778  			"parent", &parent,
   779  			"order_by?", &orderBy)
   780  		if err != nil {
   781  			return nil, err
   782  		}
   783  		nodes, err := b.Receiver().(*Graph).Children(parent, orderBy.GoString())
   784  		if err != nil {
   785  			return nil, err
   786  		}
   787  		return nodesList(nodes), nil
   788  	}),
   789  
   790  	// descendants(root: graph.key, callback=None, order_by='key'): []graph.Node.
   791  	//
   792  	// where 'callback' is func(node graph.node, children []graph.node): []graph.node.
   793  	"descendants": starlark.NewBuiltin("descendants", func(th *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   794  		var root *Key
   795  		var callback starlark.Value
   796  		var orderBy starlark.String = "key"
   797  		var topology starlark.String = "breadth"
   798  		err := starlark.UnpackArgs("descendants", args, kwargs,
   799  			"root", &root,
   800  			"callback?", &callback,
   801  			"order_by?", &orderBy,
   802  			"topology?", &topology)
   803  		if err != nil {
   804  			return nil, err
   805  		}
   806  
   807  		// Glue layer between Go callback and Starlark callback.
   808  		var visitor Visitor
   809  		if callback != nil && callback != starlark.None {
   810  			visitor = func(node *Node, children []*Node) ([]*Node, error) {
   811  				// Call callback(node, children).
   812  				ret, err := starlark.Call(th, callback, starlark.Tuple{node, nodesList(children)}, nil)
   813  				if err != nil {
   814  					return nil, err
   815  				}
   816  				// The callback is expected to return a list.
   817  				lst, _ := ret.(*starlark.List)
   818  				if lst == nil {
   819  					return nil, fmt.Errorf(
   820  						"descendants: callback %s unexpectedly returned %s instead of a list",
   821  						callback, ret.Type())
   822  				}
   823  				// And it should be a list of nodes.
   824  				out := make([]*Node, lst.Len())
   825  				for i := range out {
   826  					if out[i], _ = lst.Index(i).(*Node); out[i] == nil {
   827  						return nil, fmt.Errorf(
   828  							"descendants: callback %s unexpectedly returned %s as element #%d instead of a graph.node",
   829  							callback, lst.Index(i).Type(), i)
   830  					}
   831  				}
   832  				return out, nil
   833  			}
   834  		}
   835  
   836  		nodes, err := b.Receiver().(*Graph).Descendants(root, orderBy.GoString(), topology.GoString(), visitor)
   837  		if err != nil {
   838  			return nil, err
   839  		}
   840  		return nodesList(nodes), nil
   841  	}),
   842  
   843  	// parents(child: graph.key, order_by='key'): []graph.node
   844  	"parents": starlark.NewBuiltin("parents", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   845  		var child *Key
   846  		var orderBy starlark.String = "key"
   847  		err := starlark.UnpackArgs("parents", args, kwargs,
   848  			"child", &child,
   849  			"order_by?", &orderBy)
   850  		if err != nil {
   851  			return nil, err
   852  		}
   853  		nodes, err := b.Receiver().(*Graph).Parents(child, orderBy.GoString())
   854  		if err != nil {
   855  			return nil, err
   856  		}
   857  		return nodesList(nodes), nil
   858  	}),
   859  
   860  	// sorted_nodes(nodes: iterable<graph.node>, order_by='key'): []graph.node
   861  	"sorted_nodes": starlark.NewBuiltin("sorted_nodes", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
   862  		var nodes starlark.Iterable
   863  		var orderBy starlark.String = "key"
   864  		err := starlark.UnpackArgs("sorted_nodes", args, kwargs,
   865  			"nodes", &nodes,
   866  			"order_by?", &orderBy)
   867  		if err != nil {
   868  			return nil, err
   869  		}
   870  
   871  		iter := nodes.Iterate()
   872  		defer iter.Done()
   873  
   874  		var toSort []*Node
   875  		var val starlark.Value
   876  		for iter.Next(&val) {
   877  			node, ok := val.(*Node)
   878  			if !ok {
   879  				return nil, fmt.Errorf("sorted_nodes: got %s, expecting graph.node", val)
   880  			}
   881  			toSort = append(toSort, node)
   882  		}
   883  
   884  		if err := b.Receiver().(*Graph).SortNodes(toSort, orderBy.GoString()); err != nil {
   885  			return nil, err
   886  		}
   887  		return nodesList(toSort), nil
   888  	}),
   889  }