Skip to content

Commit 166a1e7

Browse files
committed
chore: HCLString should always marshal to a singular string
1 parent 75ec816 commit 166a1e7

File tree

9 files changed

+109
-166
lines changed

9 files changed

+109
-166
lines changed

extract/json.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ func (p *stateParse) nullableInteger(key string) *int64 {
3636
return &v
3737
}
3838

39+
func (p *stateParse) nullableString(key string) *string {
40+
if p.values[key] == nil {
41+
return nil
42+
}
43+
v := optional[string](p.values, key)
44+
return &v
45+
}
46+
3947
func (p *stateParse) string(key string) string {
4048
v, err := expected[string](p.values, key)
4149
if err != nil {

extract/parameter.go

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@ import (
1313
)
1414

1515
func ParameterFromBlock(block *terraform.Block) (*types.Parameter, hcl.Diagnostics) {
16-
diags := required(block, "name", "type")
16+
diags := required(block, "name")
1717
if diags.HasErrors() {
1818
return nil, diags
1919
}
2020

21-
pType, typDiag := requiredString("type", block)
21+
pType, typDiag := optionalStringEnum[types.ParameterType](block, "type", types.ParameterTypeString, func(s types.ParameterType) error {
22+
return s.Valid()
23+
})
2224
if typDiag != nil {
2325
diags = diags.Append(typDiag)
2426
}
2527

26-
pName, nameDiag := requiredString("name", block)
28+
pName, nameDiag := requiredString(block, "name")
2729
if nameDiag != nil {
2830
diags = diags.Append(nameDiag)
2931
}
@@ -87,7 +89,7 @@ func ParameterValidationFromBlock(block *terraform.Block) (types.ParameterValida
8789
return types.ParameterValidation{}, diags
8890
}
8991

90-
pErr, errDiag := requiredString("error", block)
92+
pErr, errDiag := requiredString(block, "error")
9193
if errDiag != nil {
9294
diags = diags.Append(errDiag)
9395
}
@@ -97,11 +99,11 @@ func ParameterValidationFromBlock(block *terraform.Block) (types.ParameterValida
9799
}
98100

99101
p := types.ParameterValidation{
100-
Regex: optionalString(block, "regex"),
102+
Regex: nullableString(block, "regex"),
101103
Error: pErr,
102104
Min: nullableInteger(block, "min"),
103105
Max: nullableInteger(block, "max"),
104-
Monotonic: optionalString(block, "monotonic"),
106+
Monotonic: nullableString(block, "monotonic"),
105107
}
106108

107109
return p, diags
@@ -113,12 +115,12 @@ func ParameterOptionFromBlock(block *terraform.Block) (types.ParameterOption, hc
113115
return types.ParameterOption{}, diags
114116
}
115117

116-
pName, nameDiag := requiredString("name", block)
118+
pName, nameDiag := requiredString(block, "name")
117119
if nameDiag != nil {
118120
diags = diags.Append(nameDiag)
119121
}
120122

121-
pVal, valDiag := requiredString("value", block)
123+
pVal, valDiag := requiredString(block, "value")
122124
if valDiag != nil {
123125
diags = diags.Append(valDiag)
124126
}
@@ -137,7 +139,29 @@ func ParameterOptionFromBlock(block *terraform.Block) (types.ParameterOption, hc
137139
return p, diags
138140
}
139141

140-
func requiredString(key string, block *terraform.Block) (string, *hcl.Diagnostic) {
142+
func optionalStringEnum[T ~string](block *terraform.Block, key string, def T, valid func(s T) error) (T, *hcl.Diagnostic) {
143+
str := optionalString(block, key)
144+
if str == "" {
145+
return def, nil
146+
}
147+
148+
if err := valid(T(str)); err != nil {
149+
tyAttr := block.GetAttribute(key)
150+
return "", &hcl.Diagnostic{
151+
Severity: hcl.DiagError,
152+
Summary: fmt.Sprintf("Invalid %q attribute", key),
153+
Detail: err.Error(),
154+
Subject: &(tyAttr.HCLAttribute().Range),
155+
//Context: &(block.HCLBlock().DefRange),
156+
Expression: tyAttr.HCLAttribute().Expr,
157+
EvalContext: block.Context().Inner(),
158+
}
159+
}
160+
161+
return T(str), nil
162+
}
163+
164+
func requiredString(block *terraform.Block, key string) (string, *hcl.Diagnostic) {
141165
tyAttr := block.GetAttribute(key)
142166
tyVal := tyAttr.Value()
143167
if tyVal.Type() != cty.String {
@@ -210,6 +234,20 @@ func optionalInteger(block *terraform.Block, key string) int64 {
210234
return i
211235
}
212236

237+
func nullableString(block *terraform.Block, key string) *string {
238+
attr := block.GetAttribute(key)
239+
if attr == nil || attr.IsNil() {
240+
return nil
241+
}
242+
val := attr.Value()
243+
if val.Type() != cty.String {
244+
return nil
245+
}
246+
247+
str := val.AsString()
248+
return &str
249+
}
250+
213251
func optionalString(block *terraform.Block, key string) string {
214252
attr := block.GetAttribute(key)
215253
if attr == nil || attr.IsNil() {

extract/state.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func ParameterFromState(block *tfjson.StateResource) (types.Parameter, error) {
5656
RichParameter: types.RichParameter{
5757
Name: st.string("name"),
5858
Description: st.optionalString("description"),
59-
Type: st.string("type"),
59+
Type: types.ParameterType(st.optionalString("type")),
6060
Mutable: st.optionalBool("mutable"),
6161
DefaultValue: st.optionalString("default"),
6262
Icon: st.optionalString("icon"),
@@ -107,11 +107,11 @@ func parameterValidation(vals map[string]any) (*types.ParameterValidation, error
107107
st := newStateParse(vals)
108108

109109
opt := types.ParameterValidation{
110-
Regex: st.optionalString("regex"),
110+
Regex: st.nullableString("regex"),
111111
Error: st.optionalString("error"),
112112
Min: st.nullableInteger("min"),
113113
Max: st.nullableInteger("max"),
114-
Monotonic: st.optionalString("monotonic"),
114+
Monotonic: st.nullableString("monotonic"),
115115
}
116116

117117
if len(st.errors) > 0 {

internal/verify/cmp.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package verify
22

33
import (
4+
"encoding/json"
45
"testing"
56

67
tfjson "github.com/hashicorp/terraform-json"
@@ -34,8 +35,13 @@ func CompareParameters(t *testing.T, pr *preview.Output, values *tfjson.StateMod
3435
types.SortParameters(stateParams)
3536
types.SortParameters(pr.Parameters)
3637
for i, param := range stateParams {
37-
// TODO: A better compare function would be easier to debug
38-
assert.Equal(t, param, pr.Parameters[i], "parameter %q %d", param.Name, i)
38+
adata, err := json.Marshal(param)
39+
require.NoError(t, err, "marshal parameter %q", param.Name)
40+
41+
bdata, err := json.Marshal(pr.Parameters[i])
42+
require.NoError(t, err, "marshal parameter %q", pr.Parameters[i].Name)
43+
44+
require.JSONEq(t, string(adata), string(bdata), "parameter %q", param.Name)
3945
}
4046

4147
return passed

types/enum.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package types
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
type ParameterType string
9+
10+
const (
11+
ParameterTypeString ParameterType = "string"
12+
ParameterTypeNumber ParameterType = "number"
13+
ParameterTypeBoolean ParameterType = "boolean"
14+
ParameterTypeListString ParameterType = "list(string)"
15+
)
16+
17+
func (t ParameterType) Valid() error {
18+
switch t {
19+
case ParameterTypeString, ParameterTypeNumber, ParameterTypeBoolean, ParameterTypeListString:
20+
return nil
21+
default:
22+
return fmt.Errorf("invalid parameter type %q, expected one of [%s]", t,
23+
strings.Join([]string{
24+
string(ParameterTypeString),
25+
string(ParameterTypeNumber),
26+
string(ParameterTypeBoolean),
27+
string(ParameterTypeListString),
28+
}, ", "))
29+
}
30+
}

types/parameter.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type RichParameter struct {
4343
Name string `json:"name"`
4444
DisplayName string `json:"display_name"`
4545
Description string `json:"description"`
46-
Type string `json:"type"`
46+
Type ParameterType `json:"type"`
4747
Mutable bool `json:"mutable"`
4848
DefaultValue string `json:"default_value"`
4949
Icon string `json:"icon"`
@@ -56,11 +56,11 @@ type RichParameter struct {
5656
}
5757

5858
type ParameterValidation struct {
59-
Regex string `json:"validation_regex"`
60-
Error string `json:"validation_error"`
61-
Min *int64 `json:"validation_min"`
62-
Max *int64 `json:"validation_max"`
63-
Monotonic string `json:"validation_monotonic"`
59+
Regex *string `json:"validation_regex"`
60+
Error string `json:"validation_error"`
61+
Min *int64 `json:"validation_min"`
62+
Max *int64 `json:"validation_max"`
63+
Monotonic *string `json:"validation_monotonic"`
6464
}
6565

6666
type ParameterOption struct {

types/parameter_test.go

Lines changed: 0 additions & 142 deletions
This file was deleted.

types/value.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type HCLString struct {
2121
Source *string
2222
}
2323

24-
func (s *HCLString) MarshalJSON() ([]byte, error) {
24+
func (s HCLString) MarshalJSON() ([]byte, error) {
2525
return json.Marshal(s.AsString())
2626
}
2727

0 commit comments

Comments
 (0)