Skip to content

Commit 44393e4

Browse files
authored
Introduce float64validator package (#18)
Reference: #1
1 parent 325f93b commit 44393e4

File tree

11 files changed

+542
-2
lines changed

11 files changed

+542
-2
lines changed

.changelog/18.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:feature
2+
Introduced `float64validator` package with `AtLeast()`, `AtMost()`, and `Between()` validation functions
3+
```

float64validator/at_least.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package float64validator
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework-validators/validatordiag"
8+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
9+
)
10+
11+
var _ tfsdk.AttributeValidator = atLeastValidator{}
12+
13+
// atLeastValidator validates that an float Attribute's value is at least a certain value.
14+
type atLeastValidator struct {
15+
min float64
16+
}
17+
18+
// Description describes the validation in plain text formatting.
19+
func (validator atLeastValidator) Description(_ context.Context) string {
20+
return fmt.Sprintf("value must be at least %f", validator.min)
21+
}
22+
23+
// MarkdownDescription describes the validation in Markdown formatting.
24+
func (validator atLeastValidator) MarkdownDescription(ctx context.Context) string {
25+
return validator.Description(ctx)
26+
}
27+
28+
// Validate performs the validation.
29+
func (validator atLeastValidator) Validate(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) {
30+
f, ok := validateFloat(ctx, request, response)
31+
32+
if !ok {
33+
return
34+
}
35+
36+
if f < validator.min {
37+
response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic(
38+
request.AttributePath,
39+
validator.Description(ctx),
40+
fmt.Sprintf("%f", f),
41+
))
42+
43+
return
44+
}
45+
}
46+
47+
// AtLeast returns an AttributeValidator which ensures that any configured
48+
// attribute value:
49+
//
50+
// - Is a number, which can be represented by a 64-bit floating point.
51+
// - Is exclusively greater than the given minimum.
52+
//
53+
// Null (unconfigured) and unknown (known after apply) values are skipped.
54+
func AtLeast(min float64) tfsdk.AttributeValidator {
55+
return atLeastValidator{
56+
min: min,
57+
}
58+
}

float64validator/at_least_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package float64validator
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
"github.com/hashicorp/terraform-plugin-go/tftypes"
11+
)
12+
13+
func TestAtLeastValidator(t *testing.T) {
14+
t.Parallel()
15+
16+
type testCase struct {
17+
val attr.Value
18+
min float64
19+
expectError bool
20+
}
21+
tests := map[string]testCase{
22+
"not a Float64": {
23+
val: types.Bool{Value: true},
24+
expectError: true,
25+
},
26+
"unknown Float64": {
27+
val: types.Float64{Unknown: true},
28+
min: 0.90,
29+
},
30+
"null Float64": {
31+
val: types.Float64{Null: true},
32+
min: 0.90,
33+
},
34+
"valid integer as Float64": {
35+
val: types.Float64{Value: 2},
36+
min: 0.90,
37+
},
38+
"valid float as Float64": {
39+
val: types.Float64{Value: 2.2},
40+
min: 0.90,
41+
},
42+
"valid float as Float64 min": {
43+
val: types.Float64{Value: 0.9},
44+
min: 0.90,
45+
},
46+
"too small float as Float64": {
47+
val: types.Float64{Value: -1.1111},
48+
min: 0.90,
49+
expectError: true,
50+
},
51+
}
52+
53+
for name, test := range tests {
54+
name, test := name, test
55+
t.Run(name, func(t *testing.T) {
56+
request := tfsdk.ValidateAttributeRequest{
57+
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
58+
AttributeConfig: test.val,
59+
}
60+
response := tfsdk.ValidateAttributeResponse{}
61+
AtLeast(test.min).Validate(context.TODO(), request, &response)
62+
63+
if !response.Diagnostics.HasError() && test.expectError {
64+
t.Fatal("expected error, got no error")
65+
}
66+
67+
if response.Diagnostics.HasError() && !test.expectError {
68+
t.Fatalf("got unexpected error: %s", response.Diagnostics)
69+
}
70+
})
71+
}
72+
}

float64validator/at_most.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package float64validator
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework-validators/validatordiag"
8+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
)
11+
12+
var _ tfsdk.AttributeValidator = atMostValidator{}
13+
14+
// atMostValidator validates that an float Attribute's value is at most a certain value.
15+
type atMostValidator struct {
16+
max float64
17+
}
18+
19+
// Description describes the validation in plain text formatting.
20+
func (validator atMostValidator) Description(_ context.Context) string {
21+
return fmt.Sprintf("value must be at most %f", validator.max)
22+
}
23+
24+
// MarkdownDescription describes the validation in Markdown formatting.
25+
func (validator atMostValidator) MarkdownDescription(ctx context.Context) string {
26+
return validator.Description(ctx)
27+
}
28+
29+
// Validate performs the validation.
30+
func (validator atMostValidator) Validate(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) {
31+
f, ok := validateFloat(ctx, request, response)
32+
33+
if !ok {
34+
return
35+
}
36+
37+
if f > validator.max {
38+
response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic(
39+
request.AttributePath,
40+
validator.Description(ctx),
41+
fmt.Sprintf("%f", f),
42+
))
43+
44+
return
45+
}
46+
}
47+
48+
// AtMost returns an AttributeValidator which ensures that any configured
49+
// attribute value:
50+
//
51+
// - Is a number, which can be represented by a 64-bit floating point.
52+
// - Is exclusively less than the given maximum.
53+
//
54+
// Null (unconfigured) and unknown (known after apply) values are skipped.
55+
func AtMost(max float64) tfsdk.AttributeValidator {
56+
return atMostValidator{
57+
max: max,
58+
}
59+
}
60+
61+
// validateFloat ensures that the request contains a Float64 value.
62+
func validateFloat(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) (float64, bool) {
63+
var n types.Float64
64+
65+
diags := tfsdk.ValueAs(ctx, request.AttributeConfig, &n)
66+
67+
if diags.HasError() {
68+
response.Diagnostics = append(response.Diagnostics, diags...)
69+
70+
return 0, false
71+
}
72+
73+
if n.Unknown || n.Null {
74+
return 0, false
75+
}
76+
77+
return n.Value, true
78+
}

float64validator/at_most_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package float64validator
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
"github.com/hashicorp/terraform-plugin-go/tftypes"
11+
)
12+
13+
func TestAtMostValidator(t *testing.T) {
14+
t.Parallel()
15+
16+
type testCase struct {
17+
val attr.Value
18+
max float64
19+
expectError bool
20+
}
21+
tests := map[string]testCase{
22+
"not a Float64": {
23+
val: types.Bool{Value: true},
24+
expectError: true,
25+
},
26+
"unknown Float64": {
27+
val: types.Float64{Unknown: true},
28+
max: 2.00,
29+
},
30+
"null Float64": {
31+
val: types.Float64{Null: true},
32+
max: 2.00,
33+
},
34+
"valid integer as Float64": {
35+
val: types.Float64{Value: 1},
36+
max: 2.00,
37+
},
38+
"valid float as Float64": {
39+
val: types.Float64{Value: 1.1},
40+
max: 2.00,
41+
},
42+
"valid float as Float64 max": {
43+
val: types.Float64{Value: 2.0},
44+
max: 2.00,
45+
},
46+
"too large float as Float64": {
47+
val: types.Float64{Value: 3.0},
48+
max: 2.00,
49+
expectError: true,
50+
},
51+
}
52+
53+
for name, test := range tests {
54+
name, test := name, test
55+
t.Run(name, func(t *testing.T) {
56+
request := tfsdk.ValidateAttributeRequest{
57+
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
58+
AttributeConfig: test.val,
59+
}
60+
response := tfsdk.ValidateAttributeResponse{}
61+
AtMost(test.max).Validate(context.TODO(), request, &response)
62+
63+
if !response.Diagnostics.HasError() && test.expectError {
64+
t.Fatal("expected error, got no error")
65+
}
66+
67+
if response.Diagnostics.HasError() && !test.expectError {
68+
t.Fatalf("got unexpected error: %s", response.Diagnostics)
69+
}
70+
})
71+
}
72+
}

float64validator/between.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package float64validator
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework-validators/validatordiag"
8+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
9+
)
10+
11+
var _ tfsdk.AttributeValidator = betweenValidator{}
12+
13+
// betweenValidator validates that an float Attribute's value is in a range.
14+
type betweenValidator struct {
15+
min, max float64
16+
}
17+
18+
// Description describes the validation in plain text formatting.
19+
func (validator betweenValidator) Description(_ context.Context) string {
20+
return fmt.Sprintf("value must be between %f and %f", validator.min, validator.max)
21+
}
22+
23+
// MarkdownDescription describes the validation in Markdown formatting.
24+
func (validator betweenValidator) MarkdownDescription(ctx context.Context) string {
25+
return validator.Description(ctx)
26+
}
27+
28+
// Validate performs the validation.
29+
func (validator betweenValidator) Validate(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) {
30+
f, ok := validateFloat(ctx, request, response)
31+
32+
if !ok {
33+
return
34+
}
35+
36+
if f < validator.min || f > validator.max {
37+
response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic(
38+
request.AttributePath,
39+
validator.Description(ctx),
40+
fmt.Sprintf("%f", f),
41+
))
42+
43+
return
44+
}
45+
}
46+
47+
// Between returns an AttributeValidator which ensures that any configured
48+
// attribute value:
49+
//
50+
// - Is a number, which can be represented by a 64-bit floating point.
51+
// - Is exclusively greater than the given minimum and less than the given maximum.
52+
//
53+
// Null (unconfigured) and unknown (known after apply) values are skipped.
54+
func Between(min, max float64) tfsdk.AttributeValidator {
55+
if min > max {
56+
return nil
57+
}
58+
59+
return betweenValidator{
60+
min: min,
61+
max: max,
62+
}
63+
}

0 commit comments

Comments
 (0)