github.com/medzin/terraform@v0.11.11/config/hcl2shim/values.go (about)

     1  package hcl2shim
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  
     7  	"github.com/hashicorp/hil/ast"
     8  	"github.com/zclconf/go-cty/cty"
     9  )
    10  
    11  // UnknownVariableValue is a sentinel value that can be used
    12  // to denote that the value of a variable is unknown at this time.
    13  // RawConfig uses this information to build up data about
    14  // unknown keys.
    15  const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
    16  
    17  // ConfigValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic
    18  // types library that HCL2 uses) to a value type that matches what would've
    19  // been produced from the HCL-based interpolator for an equivalent structure.
    20  //
    21  // This function will transform a cty null value into a Go nil value, which
    22  // isn't a possible outcome of the HCL/HIL-based decoder and so callers may
    23  // need to detect and reject any null values.
    24  func ConfigValueFromHCL2(v cty.Value) interface{} {
    25  	if !v.IsKnown() {
    26  		return UnknownVariableValue
    27  	}
    28  	if v.IsNull() {
    29  		return nil
    30  	}
    31  
    32  	switch v.Type() {
    33  	case cty.Bool:
    34  		return v.True() // like HCL.BOOL
    35  	case cty.String:
    36  		return v.AsString() // like HCL token.STRING or token.HEREDOC
    37  	case cty.Number:
    38  		// We can't match HCL _exactly_ here because it distinguishes between
    39  		// int and float values, but we'll get as close as we can by using
    40  		// an int if the number is exactly representable, and a float if not.
    41  		// The conversion to float will force precision to that of a float64,
    42  		// which is potentially losing information from the specific number
    43  		// given, but no worse than what HCL would've done in its own conversion
    44  		// to float.
    45  
    46  		f := v.AsBigFloat()
    47  		if i, acc := f.Int64(); acc == big.Exact {
    48  			// if we're on a 32-bit system and the number is too big for 32-bit
    49  			// int then we'll fall through here and use a float64.
    50  			const MaxInt = int(^uint(0) >> 1)
    51  			const MinInt = -MaxInt - 1
    52  			if i <= int64(MaxInt) && i >= int64(MinInt) {
    53  				return int(i) // Like HCL token.NUMBER
    54  			}
    55  		}
    56  
    57  		f64, _ := f.Float64()
    58  		return f64 // like HCL token.FLOAT
    59  	}
    60  
    61  	if v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType() {
    62  		l := make([]interface{}, 0, v.LengthInt())
    63  		it := v.ElementIterator()
    64  		for it.Next() {
    65  			_, ev := it.Element()
    66  			l = append(l, ConfigValueFromHCL2(ev))
    67  		}
    68  		return l
    69  	}
    70  
    71  	if v.Type().IsMapType() || v.Type().IsObjectType() {
    72  		l := make(map[string]interface{})
    73  		it := v.ElementIterator()
    74  		for it.Next() {
    75  			ek, ev := it.Element()
    76  			l[ek.AsString()] = ConfigValueFromHCL2(ev)
    77  		}
    78  		return l
    79  	}
    80  
    81  	// If we fall out here then we have some weird type that we haven't
    82  	// accounted for. This should never happen unless the caller is using
    83  	// capsule types, and we don't currently have any such types defined.
    84  	panic(fmt.Errorf("can't convert %#v to config value", v))
    85  }
    86  
    87  // HCL2ValueFromConfigValue is the opposite of configValueFromHCL2: it takes
    88  // a value as would be returned from the old interpolator and turns it into
    89  // a cty.Value so it can be used within, for example, an HCL2 EvalContext.
    90  func HCL2ValueFromConfigValue(v interface{}) cty.Value {
    91  	if v == nil {
    92  		return cty.NullVal(cty.DynamicPseudoType)
    93  	}
    94  	if v == UnknownVariableValue {
    95  		return cty.DynamicVal
    96  	}
    97  
    98  	switch tv := v.(type) {
    99  	case bool:
   100  		return cty.BoolVal(tv)
   101  	case string:
   102  		return cty.StringVal(tv)
   103  	case int:
   104  		return cty.NumberIntVal(int64(tv))
   105  	case float64:
   106  		return cty.NumberFloatVal(tv)
   107  	case []interface{}:
   108  		vals := make([]cty.Value, len(tv))
   109  		for i, ev := range tv {
   110  			vals[i] = HCL2ValueFromConfigValue(ev)
   111  		}
   112  		return cty.TupleVal(vals)
   113  	case map[string]interface{}:
   114  		vals := map[string]cty.Value{}
   115  		for k, ev := range tv {
   116  			vals[k] = HCL2ValueFromConfigValue(ev)
   117  		}
   118  		return cty.ObjectVal(vals)
   119  	default:
   120  		// HCL/HIL should never generate anything that isn't caught by
   121  		// the above, so if we get here something has gone very wrong.
   122  		panic(fmt.Errorf("can't convert %#v to cty.Value", v))
   123  	}
   124  }
   125  
   126  func HILVariableFromHCL2Value(v cty.Value) ast.Variable {
   127  	if v.IsNull() {
   128  		// Caller should guarantee/check this before calling
   129  		panic("Null values cannot be represented in HIL")
   130  	}
   131  	if !v.IsKnown() {
   132  		return ast.Variable{
   133  			Type:  ast.TypeUnknown,
   134  			Value: UnknownVariableValue,
   135  		}
   136  	}
   137  
   138  	switch v.Type() {
   139  	case cty.Bool:
   140  		return ast.Variable{
   141  			Type:  ast.TypeBool,
   142  			Value: v.True(),
   143  		}
   144  	case cty.Number:
   145  		v := ConfigValueFromHCL2(v)
   146  		switch tv := v.(type) {
   147  		case int:
   148  			return ast.Variable{
   149  				Type:  ast.TypeInt,
   150  				Value: tv,
   151  			}
   152  		case float64:
   153  			return ast.Variable{
   154  				Type:  ast.TypeFloat,
   155  				Value: tv,
   156  			}
   157  		default:
   158  			// should never happen
   159  			panic("invalid return value for configValueFromHCL2")
   160  		}
   161  	case cty.String:
   162  		return ast.Variable{
   163  			Type:  ast.TypeString,
   164  			Value: v.AsString(),
   165  		}
   166  	}
   167  
   168  	if v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType() {
   169  		l := make([]ast.Variable, 0, v.LengthInt())
   170  		it := v.ElementIterator()
   171  		for it.Next() {
   172  			_, ev := it.Element()
   173  			l = append(l, HILVariableFromHCL2Value(ev))
   174  		}
   175  		// If we were given a tuple then this could actually produce an invalid
   176  		// list with non-homogenous types, which we expect to be caught inside
   177  		// HIL just like a user-supplied non-homogenous list would be.
   178  		return ast.Variable{
   179  			Type:  ast.TypeList,
   180  			Value: l,
   181  		}
   182  	}
   183  
   184  	if v.Type().IsMapType() || v.Type().IsObjectType() {
   185  		l := make(map[string]ast.Variable)
   186  		it := v.ElementIterator()
   187  		for it.Next() {
   188  			ek, ev := it.Element()
   189  			l[ek.AsString()] = HILVariableFromHCL2Value(ev)
   190  		}
   191  		// If we were given an object then this could actually produce an invalid
   192  		// map with non-homogenous types, which we expect to be caught inside
   193  		// HIL just like a user-supplied non-homogenous map would be.
   194  		return ast.Variable{
   195  			Type:  ast.TypeMap,
   196  			Value: l,
   197  		}
   198  	}
   199  
   200  	// If we fall out here then we have some weird type that we haven't
   201  	// accounted for. This should never happen unless the caller is using
   202  	// capsule types, and we don't currently have any such types defined.
   203  	panic(fmt.Errorf("can't convert %#v to HIL variable", v))
   204  }
   205  
   206  func HCL2ValueFromHILVariable(v ast.Variable) cty.Value {
   207  	switch v.Type {
   208  	case ast.TypeList:
   209  		vals := make([]cty.Value, len(v.Value.([]ast.Variable)))
   210  		for i, ev := range v.Value.([]ast.Variable) {
   211  			vals[i] = HCL2ValueFromHILVariable(ev)
   212  		}
   213  		return cty.TupleVal(vals)
   214  	case ast.TypeMap:
   215  		vals := make(map[string]cty.Value, len(v.Value.(map[string]ast.Variable)))
   216  		for k, ev := range v.Value.(map[string]ast.Variable) {
   217  			vals[k] = HCL2ValueFromHILVariable(ev)
   218  		}
   219  		return cty.ObjectVal(vals)
   220  	default:
   221  		return HCL2ValueFromConfigValue(v.Value)
   222  	}
   223  }
   224  
   225  func HCL2TypeForHILType(hilType ast.Type) cty.Type {
   226  	switch hilType {
   227  	case ast.TypeAny:
   228  		return cty.DynamicPseudoType
   229  	case ast.TypeUnknown:
   230  		return cty.DynamicPseudoType
   231  	case ast.TypeBool:
   232  		return cty.Bool
   233  	case ast.TypeInt:
   234  		return cty.Number
   235  	case ast.TypeFloat:
   236  		return cty.Number
   237  	case ast.TypeString:
   238  		return cty.String
   239  	case ast.TypeList:
   240  		return cty.List(cty.DynamicPseudoType)
   241  	case ast.TypeMap:
   242  		return cty.Map(cty.DynamicPseudoType)
   243  	default:
   244  		return cty.NilType // equilvalent to ast.TypeInvalid
   245  	}
   246  }