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  }