github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/addrs/instance_key.go (about)

     1  package addrs
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/zclconf/go-cty/cty"
     7  	"github.com/zclconf/go-cty/cty/gocty"
     8  )
     9  
    10  // InstanceKey represents the key of an instance within an object that
    11  // contains multiple instances due to using "count" or "for_each" arguments
    12  // in configuration.
    13  //
    14  // IntKey and StringKey are the two implementations of this type. No other
    15  // implementations are allowed. The single instance of an object that _isn't_
    16  // using "count" or "for_each" is represented by NoKey, which is a nil
    17  // InstanceKey.
    18  type InstanceKey interface {
    19  	instanceKeySigil()
    20  	String() string
    21  }
    22  
    23  // ParseInstanceKey returns the instance key corresponding to the given value,
    24  // which must be known and non-null.
    25  //
    26  // If an unknown or null value is provided then this function will panic. This
    27  // function is intended to deal with the values that would naturally be found
    28  // in a hcl.TraverseIndex, which (when parsed from source, at least) can never
    29  // contain unknown or null values.
    30  func ParseInstanceKey(key cty.Value) (InstanceKey, error) {
    31  	switch key.Type() {
    32  	case cty.String:
    33  		return StringKey(key.AsString()), nil
    34  	case cty.Number:
    35  		var idx int
    36  		err := gocty.FromCtyValue(key, &idx)
    37  		return IntKey(idx), err
    38  	default:
    39  		return NoKey, fmt.Errorf("either a string or an integer is required")
    40  	}
    41  }
    42  
    43  // NoKey represents the absense of an InstanceKey, for the single instance
    44  // of a configuration object that does not use "count" or "for_each" at all.
    45  var NoKey InstanceKey
    46  
    47  // IntKey is the InstanceKey representation representing integer indices, as
    48  // used when the "count" argument is specified or if for_each is used with
    49  // a sequence type.
    50  type IntKey int
    51  
    52  func (k IntKey) instanceKeySigil() {
    53  }
    54  
    55  func (k IntKey) String() string {
    56  	return fmt.Sprintf("[%d]", int(k))
    57  }
    58  
    59  // StringKey is the InstanceKey representation representing string indices, as
    60  // used when the "for_each" argument is specified with a map or object type.
    61  type StringKey string
    62  
    63  func (k StringKey) instanceKeySigil() {
    64  }
    65  
    66  func (k StringKey) String() string {
    67  	// FIXME: This isn't _quite_ right because Go's quoted string syntax is
    68  	// slightly different than HCL's, but we'll accept it for now.
    69  	return fmt.Sprintf("[%q]", string(k))
    70  }
    71  
    72  // InstanceKeyLess returns true if the first given instance key i should sort
    73  // before the second key j, and false otherwise.
    74  func InstanceKeyLess(i, j InstanceKey) bool {
    75  	iTy := instanceKeyType(i)
    76  	jTy := instanceKeyType(j)
    77  
    78  	switch {
    79  	case i == j:
    80  		return false
    81  	case i == NoKey:
    82  		return true
    83  	case j == NoKey:
    84  		return false
    85  	case iTy != jTy:
    86  		// The ordering here is arbitrary except that we want NoKeyType
    87  		// to sort before the others, so we'll just use the enum values
    88  		// of InstanceKeyType here (where NoKey is zero, sorting before
    89  		// any other).
    90  		return uint32(iTy) < uint32(jTy)
    91  	case iTy == IntKeyType:
    92  		return int(i.(IntKey)) < int(j.(IntKey))
    93  	case iTy == StringKeyType:
    94  		return string(i.(StringKey)) < string(j.(StringKey))
    95  	default:
    96  		// Shouldn't be possible to get down here in practice, since the
    97  		// above is exhaustive.
    98  		return false
    99  	}
   100  }
   101  
   102  func instanceKeyType(k InstanceKey) InstanceKeyType {
   103  	if _, ok := k.(StringKey); ok {
   104  		return StringKeyType
   105  	}
   106  	if _, ok := k.(IntKey); ok {
   107  		return IntKeyType
   108  	}
   109  	return NoKeyType
   110  }
   111  
   112  // InstanceKeyType represents the different types of instance key that are
   113  // supported. Usually it is sufficient to simply type-assert an InstanceKey
   114  // value to either IntKey or StringKey, but this type and its values can be
   115  // used to represent the types themselves, rather than specific values
   116  // of those types.
   117  type InstanceKeyType rune
   118  
   119  const (
   120  	NoKeyType     InstanceKeyType = 0
   121  	IntKeyType    InstanceKeyType = 'I'
   122  	StringKeyType InstanceKeyType = 'S'
   123  )