aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hcl2/ext/typeexpr
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/ext/typeexpr')
-rw-r--r--vendor/github.com/hashicorp/hcl2/ext/typeexpr/README.md67
-rw-r--r--vendor/github.com/hashicorp/hcl2/ext/typeexpr/doc.go11
-rw-r--r--vendor/github.com/hashicorp/hcl2/ext/typeexpr/get_type.go196
-rw-r--r--vendor/github.com/hashicorp/hcl2/ext/typeexpr/public.go129
4 files changed, 403 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/ext/typeexpr/README.md b/vendor/github.com/hashicorp/hcl2/ext/typeexpr/README.md
new file mode 100644
index 0000000..ff2b3f2
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/ext/typeexpr/README.md
@@ -0,0 +1,67 @@
1# HCL Type Expressions Extension
2
3This HCL extension defines a convention for describing HCL types using function
4call and variable reference syntax, allowing configuration formats to include
5type information provided by users.
6
7The type syntax is processed statically from a hcl.Expression, so it cannot
8use any of the usual language operators. This is similar to type expressions
9in statically-typed programming languages.
10
11```hcl
12variable "example" {
13 type = list(string)
14}
15```
16
17The extension is built using the `hcl.ExprAsKeyword` and `hcl.ExprCall`
18functions, and so it relies on the underlying syntax to define how "keyword"
19and "call" are interpreted. The above shows how they are interpreted in
20the HCL native syntax, while the following shows the same information
21expressed in JSON:
22
23```json
24{
25 "variable": {
26 "example": {
27 "type": "list(string)"
28 }
29 }
30}
31```
32
33Notice that since we have additional contextual information that we intend
34to allow only calls and keywords the JSON syntax is able to parse the given
35string directly as an expression, rather than as a template as would be
36the case for normal expression evaluation.
37
38For more information, see [the godoc reference](http://godoc.org/github.com/hashicorp/hcl2/ext/typeexpr).
39
40## Type Expression Syntax
41
42When expressed in the native syntax, the following expressions are permitted
43in a type expression:
44
45* `string` - string
46* `bool` - boolean
47* `number` - number
48* `any` - `cty.DynamicPseudoType` (in function `TypeConstraint` only)
49* `list(<type_expr>)` - list of the type given as an argument
50* `set(<type_expr>)` - set of the type given as an argument
51* `map(<type_expr>)` - map of the type given as an argument
52* `tuple([<type_exprs...>])` - tuple with the element types given in the single list argument
53* `object({<attr_name>=<type_expr>, ...}` - object with the attributes and corresponding types given in the single map argument
54
55For example:
56
57* `list(string)`
58* `object({name=string,age=number})`
59* `map(object({name=string,age=number}))`
60
61Note that the object constructor syntax is not fully-general for all possible
62object types because it requires the attribute names to be valid identifiers.
63In practice it is expected that any time an object type is being fixed for
64type checking it will be one that has identifiers as its attributes; object
65types with weird attributes generally show up only from arbitrary object
66constructors in configuration files, which are usually treated either as maps
67or as the dynamic pseudo-type.
diff --git a/vendor/github.com/hashicorp/hcl2/ext/typeexpr/doc.go b/vendor/github.com/hashicorp/hcl2/ext/typeexpr/doc.go
new file mode 100644
index 0000000..c4b3795
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/ext/typeexpr/doc.go
@@ -0,0 +1,11 @@
1// Package typeexpr extends HCL with a convention for describing HCL types
2// within configuration files.
3//
4// The type syntax is processed statically from a hcl.Expression, so it cannot
5// use any of the usual language operators. This is similar to type expressions
6// in statically-typed programming languages.
7//
8// variable "example" {
9// type = list(string)
10// }
11package typeexpr
diff --git a/vendor/github.com/hashicorp/hcl2/ext/typeexpr/get_type.go b/vendor/github.com/hashicorp/hcl2/ext/typeexpr/get_type.go
new file mode 100644
index 0000000..a84338a
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/ext/typeexpr/get_type.go
@@ -0,0 +1,196 @@
1package typeexpr
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/hcl2/hcl"
7 "github.com/zclconf/go-cty/cty"
8)
9
10const invalidTypeSummary = "Invalid type specification"
11
12// getType is the internal implementation of both Type and TypeConstraint,
13// using the passed flag to distinguish. When constraint is false, the "any"
14// keyword will produce an error.
15func getType(expr hcl.Expression, constraint bool) (cty.Type, hcl.Diagnostics) {
16 // First we'll try for one of our keywords
17 kw := hcl.ExprAsKeyword(expr)
18 switch kw {
19 case "bool":
20 return cty.Bool, nil
21 case "string":
22 return cty.String, nil
23 case "number":
24 return cty.Number, nil
25 case "any":
26 if constraint {
27 return cty.DynamicPseudoType, nil
28 }
29 return cty.DynamicPseudoType, hcl.Diagnostics{{
30 Severity: hcl.DiagError,
31 Summary: invalidTypeSummary,
32 Detail: fmt.Sprintf("The keyword %q cannot be used in this type specification: an exact type is required.", kw),
33 Subject: expr.Range().Ptr(),
34 }}
35 case "list", "map", "set":
36 return cty.DynamicPseudoType, hcl.Diagnostics{{
37 Severity: hcl.DiagError,
38 Summary: invalidTypeSummary,
39 Detail: fmt.Sprintf("The %s type constructor requires one argument specifying the element type.", kw),
40 Subject: expr.Range().Ptr(),
41 }}
42 case "object":
43 return cty.DynamicPseudoType, hcl.Diagnostics{{
44 Severity: hcl.DiagError,
45 Summary: invalidTypeSummary,
46 Detail: "The object type constructor requires one argument specifying the attribute types and values as a map.",
47 Subject: expr.Range().Ptr(),
48 }}
49 case "tuple":
50 return cty.DynamicPseudoType, hcl.Diagnostics{{
51 Severity: hcl.DiagError,
52 Summary: invalidTypeSummary,
53 Detail: "The tuple type constructor requires one argument specifying the element types as a list.",
54 Subject: expr.Range().Ptr(),
55 }}
56 case "":
57 // okay! we'll fall through and try processing as a call, then.
58 default:
59 return cty.DynamicPseudoType, hcl.Diagnostics{{
60 Severity: hcl.DiagError,
61 Summary: invalidTypeSummary,
62 Detail: fmt.Sprintf("The keyword %q is not a valid type specification.", kw),
63 Subject: expr.Range().Ptr(),
64 }}
65 }
66
67 // If we get down here then our expression isn't just a keyword, so we'll
68 // try to process it as a call instead.
69 call, diags := hcl.ExprCall(expr)
70 if diags.HasErrors() {
71 return cty.DynamicPseudoType, hcl.Diagnostics{{
72 Severity: hcl.DiagError,
73 Summary: invalidTypeSummary,
74 Detail: "A type specification is either a primitive type keyword (bool, number, string) or a complex type constructor call, like list(string).",
75 Subject: expr.Range().Ptr(),
76 }}
77 }
78
79 switch call.Name {
80 case "bool", "string", "number", "any":
81 return cty.DynamicPseudoType, hcl.Diagnostics{{
82 Severity: hcl.DiagError,
83 Summary: invalidTypeSummary,
84 Detail: fmt.Sprintf("Primitive type keyword %q does not expect arguments.", call.Name),
85 Subject: &call.ArgsRange,
86 }}
87 }
88
89 if len(call.Arguments) != 1 {
90 contextRange := call.ArgsRange
91 subjectRange := call.ArgsRange
92 if len(call.Arguments) > 1 {
93 // If we have too many arguments (as opposed to too _few_) then
94 // we'll highlight the extraneous arguments as the diagnostic
95 // subject.
96 subjectRange = hcl.RangeBetween(call.Arguments[1].Range(), call.Arguments[len(call.Arguments)-1].Range())
97 }
98
99 switch call.Name {
100 case "list", "set", "map":
101 return cty.DynamicPseudoType, hcl.Diagnostics{{
102 Severity: hcl.DiagError,
103 Summary: invalidTypeSummary,
104 Detail: fmt.Sprintf("The %s type constructor requires one argument specifying the element type.", call.Name),
105 Subject: &subjectRange,
106 Context: &contextRange,
107 }}
108 case "object":
109 return cty.DynamicPseudoType, hcl.Diagnostics{{
110 Severity: hcl.DiagError,
111 Summary: invalidTypeSummary,
112 Detail: "The object type constructor requires one argument specifying the attribute types and values as a map.",
113 Subject: &subjectRange,
114 Context: &contextRange,
115 }}
116 case "tuple":
117 return cty.DynamicPseudoType, hcl.Diagnostics{{
118 Severity: hcl.DiagError,
119 Summary: invalidTypeSummary,
120 Detail: "The tuple type constructor requires one argument specifying the element types as a list.",
121 Subject: &subjectRange,
122 Context: &contextRange,
123 }}
124 }
125 }
126
127 switch call.Name {
128
129 case "list":
130 ety, diags := getType(call.Arguments[0], constraint)
131 return cty.List(ety), diags
132 case "set":
133 ety, diags := getType(call.Arguments[0], constraint)
134 return cty.Set(ety), diags
135 case "map":
136 ety, diags := getType(call.Arguments[0], constraint)
137 return cty.Map(ety), diags
138 case "object":
139 attrDefs, diags := hcl.ExprMap(call.Arguments[0])
140 if diags.HasErrors() {
141 return cty.DynamicPseudoType, hcl.Diagnostics{{
142 Severity: hcl.DiagError,
143 Summary: invalidTypeSummary,
144 Detail: "Object type constructor requires a map whose keys are attribute names and whose values are the corresponding attribute types.",
145 Subject: call.Arguments[0].Range().Ptr(),
146 Context: expr.Range().Ptr(),
147 }}
148 }
149
150 atys := make(map[string]cty.Type)
151 for _, attrDef := range attrDefs {
152 attrName := hcl.ExprAsKeyword(attrDef.Key)
153 if attrName == "" {
154 diags = append(diags, &hcl.Diagnostic{
155 Severity: hcl.DiagError,
156 Summary: invalidTypeSummary,
157 Detail: "Object constructor map keys must be attribute names.",
158 Subject: attrDef.Key.Range().Ptr(),
159 Context: expr.Range().Ptr(),
160 })
161 continue
162 }
163 aty, attrDiags := getType(attrDef.Value, constraint)
164 diags = append(diags, attrDiags...)
165 atys[attrName] = aty
166 }
167 return cty.Object(atys), diags
168 case "tuple":
169 elemDefs, diags := hcl.ExprList(call.Arguments[0])
170 if diags.HasErrors() {
171 return cty.DynamicPseudoType, hcl.Diagnostics{{
172 Severity: hcl.DiagError,
173 Summary: invalidTypeSummary,
174 Detail: "Tuple type constructor requires a list of element types.",
175 Subject: call.Arguments[0].Range().Ptr(),
176 Context: expr.Range().Ptr(),
177 }}
178 }
179 etys := make([]cty.Type, len(elemDefs))
180 for i, defExpr := range elemDefs {
181 ety, elemDiags := getType(defExpr, constraint)
182 diags = append(diags, elemDiags...)
183 etys[i] = ety
184 }
185 return cty.Tuple(etys), diags
186 default:
187 // Can't access call.Arguments in this path because we've not validated
188 // that it contains exactly one expression here.
189 return cty.DynamicPseudoType, hcl.Diagnostics{{
190 Severity: hcl.DiagError,
191 Summary: invalidTypeSummary,
192 Detail: fmt.Sprintf("Keyword %q is not a valid type constructor.", call.Name),
193 Subject: expr.Range().Ptr(),
194 }}
195 }
196}
diff --git a/vendor/github.com/hashicorp/hcl2/ext/typeexpr/public.go b/vendor/github.com/hashicorp/hcl2/ext/typeexpr/public.go
new file mode 100644
index 0000000..e3f5eef
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/ext/typeexpr/public.go
@@ -0,0 +1,129 @@
1package typeexpr
2
3import (
4 "bytes"
5 "fmt"
6 "sort"
7
8 "github.com/hashicorp/hcl2/hcl/hclsyntax"
9
10 "github.com/hashicorp/hcl2/hcl"
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.
17func 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.
28func 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.
44func 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}