github.com/beauknowssoftware/makehcl@v0.0.0-20200322000747-1b9bb1e1c008/internal/parse2/attribute.go (about) 1 package parse2 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/hcl/v2" 7 "github.com/zclconf/go-cty/cty" 8 ) 9 10 type setter func(map[string]cty.Value, cty.Value) 11 12 func setDirect(name string) setter { 13 return func(vars map[string]cty.Value, val cty.Value) { 14 vars[name] = val 15 } 16 } 17 18 func setOnObject(object, property string) setter { 19 return func(vars map[string]cty.Value, val cty.Value) { 20 objVal, hasObject := vars[object] 21 if !hasObject { 22 objVal = cty.ObjectVal(make(map[string]cty.Value)) 23 vars[object] = objVal 24 } 25 26 obj := objVal.AsValueMap() 27 if obj == nil { 28 obj = make(map[string]cty.Value) 29 } 30 31 obj[property] = val 32 vars[object] = cty.ObjectVal(obj) 33 } 34 } 35 36 type scope interface { 37 childContext(*hcl.EvalContext) *hcl.EvalContext 38 set(setter, cty.Value) 39 } 40 41 type variableScope struct { 42 variables map[string]cty.Value 43 } 44 45 func (s variableScope) childContext(ctx *hcl.EvalContext) *hcl.EvalContext { 46 ctx = ctx.NewChild() 47 ctx.Variables = s.variables 48 49 return ctx 50 } 51 52 func (s *variableScope) set(setter setter, val cty.Value) { 53 if s.variables == nil { 54 s.variables = make(map[string]cty.Value) 55 } 56 57 setter(s.variables, val) 58 } 59 60 type nestedScope struct { 61 outer scope 62 variableScope 63 } 64 65 func (s nestedScope) childContext(ctx *hcl.EvalContext) *hcl.EvalContext { 66 ctx = s.outer.childContext(ctx) 67 return s.variableScope.childContext(ctx) 68 } 69 70 type attribute struct { 71 set setter 72 scope scope 73 fillable fillable 74 name string 75 dependencies []string 76 } 77 78 func (a attribute) fill(ctx *hcl.EvalContext) hcl.Diagnostics { 79 if a.scope != nil { 80 ctx = a.scope.childContext(ctx) 81 } 82 83 val, diag := a.fillable.fill(ctx) 84 85 if a.set != nil { 86 a.scope.set(a.set, val) 87 } 88 89 return diag 90 } 91 92 func getDependencies(local string, expr hcl.Expression) (result []string) { 93 for _, v := range expr.Variables() { 94 root := v.RootName() 95 switch root { 96 case "var": 97 spl := v.SimpleSplit() 98 name := spl.Rel[0].(hcl.TraverseAttr).Name 99 result = append(result, fmt.Sprintf("var.%v", name)) 100 default: 101 result = append(result, fmt.Sprintf("%v.%v", local, root)) 102 } 103 } 104 105 return 106 } 107 108 type attributeSorter struct { 109 attributes []attribute 110 attributeLookup map[string]attribute 111 sorted []attribute 112 visited map[string]bool 113 visiting map[string]bool 114 } 115 116 func (s *attributeSorter) init() (roots []string) { 117 s.visited = make(map[string]bool) 118 s.visiting = make(map[string]bool) 119 s.attributeLookup = make(map[string]attribute) 120 121 dependents := make(map[string][]string) 122 123 for _, a := range s.attributes { 124 fmt.Println(a.name) 125 s.attributeLookup[a.name] = a 126 127 for _, dep := range a.dependencies { 128 fmt.Printf("%v dep %v\n", a.name, dep) 129 dependents[dep] = append(dependents[dep], a.name) 130 } 131 } 132 133 for _, a := range s.attributes { 134 deps := dependents[a.name] 135 if len(deps) == 0 { 136 roots = append(roots, a.name) 137 } 138 } 139 140 return 141 } 142 143 func (s *attributeSorter) visit(name string) { 144 if s.visited[name] { 145 return 146 } 147 148 if s.visiting[name] { 149 panic(fmt.Sprintf("attribute loopback on %v", name)) 150 } 151 152 s.visiting[name] = true 153 154 a := s.attributeLookup[name] 155 for _, dep := range a.dependencies { 156 s.visit(dep) 157 } 158 159 s.visiting[name] = false 160 s.visited[name] = true 161 s.sorted = append(s.sorted, a) 162 } 163 164 func (s *attributeSorter) sort() { 165 roots := s.init() 166 167 for _, a := range roots { 168 s.visit(a) 169 } 170 }