package schema import ( "container/list" "fmt" ) type tracker struct { // Nodes encountered in current path during the recursive traversal. Used to // check for cycles seenNodes map[interface{}]struct{} // List of node names encountered in order in current path during the recursive traversal. // Used to hydrate errors with path to the exact node where error occured. // // NOTE: node and node names can be the same listOfNodes *list.List } func newTracker() *tracker { return &tracker{ seenNodes: map[interface{}]struct{}{}, listOfNodes: list.New(), } } func (t *tracker) errWithTrace(prefix string, initTrace string) error { traceString := initTrace curr := t.listOfNodes.Front() for curr != nil { if curr.Value.(string) != "" { traceString += " -> " + curr.Value.(string) } curr = curr.Next() } return fmt.Errorf(prefix + ". traversal trace: " + traceString) } func (t *tracker) hasCycle(node interface{}) bool { _, ok := t.seenNodes[node] return ok } func (t *tracker) push(node interface{}, name string) { t.seenNodes[node] = struct{}{} t.listOfNodes.PushBack(name) } func (t *tracker) pop(nodeType interface{}) { back := t.listOfNodes.Back() t.listOfNodes.Remove(back) delete(t.seenNodes, nodeType) }