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 }