github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/typeexpr/public.go (about)

     1  package typeexpr
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  
     8  	"github.com/hashicorp/hcl/v2/hclsyntax"
     9  
    10  	"github.com/hashicorp/hcl/v2"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  // Type attempts to process the given expression as a type expression and, if
    15  // successful, returns the resulting type. If unsuccessful, error diagnostics
    16  // are returned.
    17  func Type(expr hcl.Expression) (cty.Type, hcl.Diagnostics) {
    18  	return getType(expr, false)
    19  }
    20  
    21  // TypeConstraint attempts to parse the given expression as a type constraint
    22  // and, if successful, returns the resulting type. If unsuccessful, error
    23  // diagnostics are returned.
    24  //
    25  // A type constraint has the same structure as a type, but it additionally
    26  // allows the keyword "any" to represent cty.DynamicPseudoType, which is often
    27  // used as a wildcard in type checking and type conversion operations.
    28  func TypeConstraint(expr hcl.Expression) (cty.Type, hcl.Diagnostics) {
    29  	return getType(expr, true)
    30  }
    31  
    32  // TypeString returns a string rendering of the given type as it would be
    33  // expected to appear in the HCL native syntax.
    34  //
    35  // This is primarily intended for showing types to the user in an application
    36  // that uses typexpr, where the user can be assumed to be familiar with the
    37  // type expression syntax. In applications that do not use typeexpr these
    38  // results may be confusing to the user and so type.FriendlyName may be
    39  // preferable, even though it's less precise.
    40  //
    41  // TypeString produces reasonable results only for types like what would be
    42  // produced by the Type and TypeConstraint functions. In particular, it cannot
    43  // support capsule types.
    44  func TypeString(ty cty.Type) string {
    45  	// Easy cases first
    46  	switch ty {
    47  	case cty.String:
    48  		return "string"
    49  	case cty.Bool:
    50  		return "bool"
    51  	case cty.Number:
    52  		return "number"
    53  	case cty.DynamicPseudoType:
    54  		return "any"
    55  	}
    56  
    57  	if ty.IsCapsuleType() {
    58  		panic("TypeString does not support capsule types")
    59  	}
    60  
    61  	if ty.IsCollectionType() {
    62  		ety := ty.ElementType()
    63  		etyString := TypeString(ety)
    64  		switch {
    65  		case ty.IsListType():
    66  			return fmt.Sprintf("list(%s)", etyString)
    67  		case ty.IsSetType():
    68  			return fmt.Sprintf("set(%s)", etyString)
    69  		case ty.IsMapType():
    70  			return fmt.Sprintf("map(%s)", etyString)
    71  		default:
    72  			// Should never happen because the above is exhaustive
    73  			panic("unsupported collection type")
    74  		}
    75  	}
    76  
    77  	if ty.IsObjectType() {
    78  		var buf bytes.Buffer
    79  		buf.WriteString("object({")
    80  		atys := ty.AttributeTypes()
    81  		names := make([]string, 0, len(atys))
    82  		for name := range atys {
    83  			names = append(names, name)
    84  		}
    85  		sort.Strings(names)
    86  		first := true
    87  		for _, name := range names {
    88  			aty := atys[name]
    89  			if !first {
    90  				buf.WriteByte(',')
    91  			}
    92  			if !hclsyntax.ValidIdentifier(name) {
    93  				// Should never happen for any type produced by this package,
    94  				// but we'll do something reasonable here just so we don't
    95  				// produce garbage if someone gives us a hand-assembled object
    96  				// type that has weird attribute names.
    97  				// Using Go-style quoting here isn't perfect, since it doesn't
    98  				// exactly match HCL syntax, but it's fine for an edge-case.
    99  				buf.WriteString(fmt.Sprintf("%q", name))
   100  			} else {
   101  				buf.WriteString(name)
   102  			}
   103  			buf.WriteByte('=')
   104  			buf.WriteString(TypeString(aty))
   105  			first = false
   106  		}
   107  		buf.WriteString("})")
   108  		return buf.String()
   109  	}
   110  
   111  	if ty.IsTupleType() {
   112  		var buf bytes.Buffer
   113  		buf.WriteString("tuple([")
   114  		etys := ty.TupleElementTypes()
   115  		first := true
   116  		for _, ety := range etys {
   117  			if !first {
   118  				buf.WriteByte(',')
   119  			}
   120  			buf.WriteString(TypeString(ety))
   121  			first = false
   122  		}
   123  		buf.WriteString("])")
   124  		return buf.String()
   125  	}
   126  
   127  	// Should never happen because we covered all cases above.
   128  	panic(fmt.Errorf("unsupported type %#v", ty))
   129  }