aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/hcl2/gohcl/schema.go
diff options
context:
space:
mode:
authorAlex Pilon <apilon@hashicorp.com>2019-02-22 18:24:37 -0500
committerAlex Pilon <apilon@hashicorp.com>2019-02-22 18:24:37 -0500
commit15c0b25d011f37e7c20aeca9eaf461f78285b8d9 (patch)
tree255c250a5c9d4801c74092d33b7337d8c14438ff /vendor/github.com/hashicorp/hcl2/gohcl/schema.go
parent07971ca38143c5faf951d152fba370ddcbe26ad5 (diff)
downloadterraform-provider-statuscake-15c0b25d011f37e7c20aeca9eaf461f78285b8d9.tar.gz
terraform-provider-statuscake-15c0b25d011f37e7c20aeca9eaf461f78285b8d9.tar.zst
terraform-provider-statuscake-15c0b25d011f37e7c20aeca9eaf461f78285b8d9.zip
deps: github.com/hashicorp/terraform@sdk-v0.11-with-go-modules
Updated via: go get github.com/hashicorp/terraform@sdk-v0.11-with-go-modules and go mod tidy
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/gohcl/schema.go')
-rw-r--r--vendor/github.com/hashicorp/hcl2/gohcl/schema.go174
1 files changed, 174 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/gohcl/schema.go b/vendor/github.com/hashicorp/hcl2/gohcl/schema.go
new file mode 100644
index 0000000..88164cb
--- /dev/null
+++ b/vendor/github.com/hashicorp/hcl2/gohcl/schema.go
@@ -0,0 +1,174 @@
1package gohcl
2
3import (
4 "fmt"
5 "reflect"
6 "sort"
7 "strings"
8
9 "github.com/hashicorp/hcl2/hcl"
10)
11
12// ImpliedBodySchema produces a hcl.BodySchema derived from the type of the
13// given value, which must be a struct value or a pointer to one. If an
14// inappropriate value is passed, this function will panic.
15//
16// The second return argument indicates whether the given struct includes
17// a "remain" field, and thus the returned schema is non-exhaustive.
18//
19// This uses the tags on the fields of the struct to discover how each
20// field's value should be expressed within configuration. If an invalid
21// mapping is attempted, this function will panic.
22func ImpliedBodySchema(val interface{}) (schema *hcl.BodySchema, partial bool) {
23 ty := reflect.TypeOf(val)
24
25 if ty.Kind() == reflect.Ptr {
26 ty = ty.Elem()
27 }
28
29 if ty.Kind() != reflect.Struct {
30 panic(fmt.Sprintf("given value must be struct, not %T", val))
31 }
32
33 var attrSchemas []hcl.AttributeSchema
34 var blockSchemas []hcl.BlockHeaderSchema
35
36 tags := getFieldTags(ty)
37
38 attrNames := make([]string, 0, len(tags.Attributes))
39 for n := range tags.Attributes {
40 attrNames = append(attrNames, n)
41 }
42 sort.Strings(attrNames)
43 for _, n := range attrNames {
44 idx := tags.Attributes[n]
45 optional := tags.Optional[n]
46 field := ty.Field(idx)
47
48 var required bool
49
50 switch {
51 case field.Type.AssignableTo(exprType):
52 // If we're decoding to hcl.Expression then absense can be
53 // indicated via a null value, so we don't specify that
54 // the field is required during decoding.
55 required = false
56 case field.Type.Kind() != reflect.Ptr && !optional:
57 required = true
58 default:
59 required = false
60 }
61
62 attrSchemas = append(attrSchemas, hcl.AttributeSchema{
63 Name: n,
64 Required: required,
65 })
66 }
67
68 blockNames := make([]string, 0, len(tags.Blocks))
69 for n := range tags.Blocks {
70 blockNames = append(blockNames, n)
71 }
72 sort.Strings(blockNames)
73 for _, n := range blockNames {
74 idx := tags.Blocks[n]
75 field := ty.Field(idx)
76 fty := field.Type
77 if fty.Kind() == reflect.Slice {
78 fty = fty.Elem()
79 }
80 if fty.Kind() == reflect.Ptr {
81 fty = fty.Elem()
82 }
83 if fty.Kind() != reflect.Struct {
84 panic(fmt.Sprintf(
85 "hcl 'block' tag kind cannot be applied to %s field %s: struct required", field.Type.String(), field.Name,
86 ))
87 }
88 ftags := getFieldTags(fty)
89 var labelNames []string
90 if len(ftags.Labels) > 0 {
91 labelNames = make([]string, len(ftags.Labels))
92 for i, l := range ftags.Labels {
93 labelNames[i] = l.Name
94 }
95 }
96
97 blockSchemas = append(blockSchemas, hcl.BlockHeaderSchema{
98 Type: n,
99 LabelNames: labelNames,
100 })
101 }
102
103 partial = tags.Remain != nil
104 schema = &hcl.BodySchema{
105 Attributes: attrSchemas,
106 Blocks: blockSchemas,
107 }
108 return schema, partial
109}
110
111type fieldTags struct {
112 Attributes map[string]int
113 Blocks map[string]int
114 Labels []labelField
115 Remain *int
116 Optional map[string]bool
117}
118
119type labelField struct {
120 FieldIndex int
121 Name string
122}
123
124func getFieldTags(ty reflect.Type) *fieldTags {
125 ret := &fieldTags{
126 Attributes: map[string]int{},
127 Blocks: map[string]int{},
128 Optional: map[string]bool{},
129 }
130
131 ct := ty.NumField()
132 for i := 0; i < ct; i++ {
133 field := ty.Field(i)
134 tag := field.Tag.Get("hcl")
135 if tag == "" {
136 continue
137 }
138
139 comma := strings.Index(tag, ",")
140 var name, kind string
141 if comma != -1 {
142 name = tag[:comma]
143 kind = tag[comma+1:]
144 } else {
145 name = tag
146 kind = "attr"
147 }
148
149 switch kind {
150 case "attr":
151 ret.Attributes[name] = i
152 case "block":
153 ret.Blocks[name] = i
154 case "label":
155 ret.Labels = append(ret.Labels, labelField{
156 FieldIndex: i,
157 Name: name,
158 })
159 case "remain":
160 if ret.Remain != nil {
161 panic("only one 'remain' tag is permitted")
162 }
163 idx := i // copy, because this loop will continue assigning to i
164 ret.Remain = &idx
165 case "optional":
166 ret.Attributes[name] = i
167 ret.Optional[name] = true
168 default:
169 panic(fmt.Sprintf("invalid hcl field tag kind %q on %s %q", kind, field.Type.String(), field.Name))
170 }
171 }
172
173 return ret
174}