github.com/beauknowssoftware/makehcl@v0.0.0-20200322000747-1b9bb1e1c008/internal/parse/getGlobals.go (about)

     1  package parse
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/hcl/v2"
     7  	"github.com/pkg/errors"
     8  	"github.com/zclconf/go-cty/cty"
     9  )
    10  
    11  type global struct {
    12  	globalType string
    13  	attr       *hcl.Attribute
    14  }
    15  
    16  func (g global) name() globalName {
    17  	return globalName{
    18  		globalType: g.globalType,
    19  		name:       g.attr.Name,
    20  	}
    21  }
    22  
    23  type globalName struct {
    24  	globalType string
    25  	name       string
    26  }
    27  
    28  type globalSorter struct {
    29  	globals  map[globalName]global
    30  	sorted   []global
    31  	visited  map[globalName]bool
    32  	visiting map[globalName]bool
    33  }
    34  
    35  func (s *globalSorter) visit(g global) {
    36  	if s.visited[g.name()] {
    37  		return
    38  	}
    39  
    40  	if s.visiting[g.name()] {
    41  		panic(fmt.Sprintf("env loopback on %v", g.name()))
    42  	}
    43  
    44  	s.visiting[g.name()] = true
    45  
    46  	for _, v := range g.attr.Expr.Variables() {
    47  		globalType := v.RootName()
    48  		spl := v.SimpleSplit()
    49  
    50  		if spl.Rel == nil || len(spl.Rel) == 0 {
    51  			continue
    52  		}
    53  
    54  		name := spl.Rel[0].(hcl.TraverseAttr).Name
    55  		gName := globalName{globalType, name}
    56  
    57  		if g.name() == gName && globalType == "env" {
    58  			continue
    59  		}
    60  
    61  		if gl, hasGlobal := s.globals[gName]; hasGlobal {
    62  			s.visit(gl)
    63  		}
    64  	}
    65  
    66  	s.visiting[g.name()] = false
    67  	s.visited[g.name()] = true
    68  	s.sorted = append(s.sorted, g)
    69  }
    70  
    71  func (s *globalSorter) sort() {
    72  	s.visited = make(map[globalName]bool)
    73  	s.visiting = make(map[globalName]bool)
    74  
    75  	for _, a := range s.globals {
    76  		s.visit(a)
    77  	}
    78  }
    79  
    80  func getGlobals(attrSets map[string]map[string]*hcl.Attribute, ctx *hcl.EvalContext) (map[string]string, error) {
    81  	s := globalSorter{
    82  		globals: make(map[globalName]global),
    83  	}
    84  
    85  	for globalType, attr := range attrSets {
    86  		for _, a := range attr {
    87  			g := global{
    88  				globalType: globalType,
    89  				attr:       a,
    90  			}
    91  			s.globals[g.name()] = g
    92  		}
    93  	}
    94  
    95  	s.sort()
    96  
    97  	vars := make(map[string]cty.Value)
    98  	envResult := make(map[string]string)
    99  	envs := ctx.Variables["env"].AsValueMap()
   100  
   101  	for _, a := range s.sorted {
   102  		if a.globalType == "var" {
   103  			val, diag := a.attr.Expr.Value(ctx)
   104  			vars[a.attr.Name] = val
   105  
   106  			if diag.HasErrors() {
   107  				return nil, errors.Wrap(diag, "failed to get var attributes")
   108  			}
   109  
   110  			ctx.Variables["var"] = cty.ObjectVal(vars)
   111  		} else if a.globalType == "env" {
   112  			val, diag := a.attr.Expr.Value(ctx)
   113  			envs[a.attr.Name] = val
   114  			envResult[a.attr.Name] = val.AsString()
   115  			if diag.HasErrors() {
   116  				return nil, errors.Wrap(diag, "failed to get env attributes")
   117  			}
   118  			ctx.Variables["env"] = cty.ObjectVal(envs)
   119  		}
   120  	}
   121  
   122  	return envResult, nil
   123  }