github.com/avenga/couper@v1.12.2/config/sequence/sequence.go (about) 1 package sequence 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/hcl/v2" 8 ) 9 10 type List []*Item 11 12 func NewBackendItem(name string) *Item { 13 return &Item{ 14 Name: name, 15 backend: true, 16 } 17 } 18 19 type Item struct { 20 BodyRange *hcl.Range 21 Name string 22 backend bool 23 deps List 24 parent *Item 25 } 26 27 func (s *Item) Add(ref *Item) *Item { 28 if strings.TrimSpace(ref.Name) == "" { 29 return s 30 } 31 32 ref.parent = s 33 34 if s.hasAncestor(ref.Name) { // collect names to populate error message 35 refs := []string{ref.Name} 36 p := s.parent 37 for p != s { 38 if p.parent == nil { 39 break 40 } 41 42 p = p.parent 43 name := p.Name 44 deps := p.Deps() 45 if len(deps) > 0 { 46 name = deps[0].Name 47 } 48 refs = append(refs, name) 49 } 50 51 err := &hcl.Diagnostic{ 52 Detail: fmt.Sprintf("circular sequence reference: %s", 53 strings.Join(append(refs, refs[0]), ",")), 54 Severity: hcl.DiagError, 55 Subject: s.BodyRange, 56 Summary: "configuration error", 57 } 58 panic(err) 59 } 60 61 s.deps = append(s.deps, ref) 62 return s 63 } 64 65 // Deps returns sequence dependency in reversed order since they have to be solved first. 66 func (s *Item) Deps() List { 67 if len(s.deps) < 2 { 68 return s.deps 69 } 70 71 var revert List 72 for i := len(s.deps); i > 0; i-- { 73 revert = append(revert, s.deps[i-1]) 74 } 75 return revert 76 } 77 78 func (s *Item) HasParent() bool { 79 return s != nil && s.parent != nil 80 } 81 82 func (s *Item) hasAncestor(name string) bool { 83 if s == nil { 84 return false 85 } 86 87 if !s.HasParent() { 88 return false 89 } 90 91 if s.parent.Name == name { 92 return true 93 } 94 95 return s.parent.hasAncestor(name) 96 } 97 98 func resolveSequence(item *Item, resolved, seen *[]string) { 99 name := item.Name 100 *seen = append(*seen, name) 101 for _, dep := range item.Deps() { 102 if !containsString(resolved, dep.Name) { 103 if !containsString(seen, dep.Name) { 104 resolveSequence(dep, resolved, seen) 105 continue 106 } 107 } 108 } 109 110 *resolved = append(*resolved, name) 111 } 112 113 // Dependencies just collects the deps for filtering purposes. 114 func Dependencies(items List) (allDeps [][]string) { 115 for _, item := range items { 116 deps := make([]string, 0) 117 seen := make([]string, 0) 118 resolveSequence(item, &deps, &seen) 119 allDeps = append(allDeps, deps) 120 } 121 return allDeps 122 } 123 124 func containsString(slice *[]string, needle string) bool { 125 for _, n := range *slice { 126 if n == needle { 127 return true 128 } 129 } 130 return false 131 }