diff options
author | Alex Pilon <apilon@hashicorp.com> | 2019-02-22 18:24:37 -0500 |
---|---|---|
committer | Alex Pilon <apilon@hashicorp.com> | 2019-02-22 18:24:37 -0500 |
commit | 15c0b25d011f37e7c20aeca9eaf461f78285b8d9 (patch) | |
tree | 255c250a5c9d4801c74092d33b7337d8c14438ff /vendor/github.com/hashicorp/hcl2/gohcl/schema.go | |
parent | 07971ca38143c5faf951d152fba370ddcbe26ad5 (diff) | |
download | terraform-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.go | 174 |
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 @@ | |||
1 | package gohcl | ||
2 | |||
3 | import ( | ||
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. | ||
22 | func 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 | |||
111 | type 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 | |||
119 | type labelField struct { | ||
120 | FieldIndex int | ||
121 | Name string | ||
122 | } | ||
123 | |||
124 | func 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 | } | ||