Skip to content

Commit 3a11f99

Browse files
committed
encoding/jsonschema: allow whole floats for unsigned numbers
JSON does not make a distinction between floats and ints, and there are tests in the jsonschema test suite that check that it's OK to use floating point literals in primitives that expect an integer. So accept whole floats anywhere we need a uint. Relevant issue: #253. Signed-off-by: Roger Peppe <[email protected]> Change-Id: I44632a6eadd2aeaff626b27ca6536d4aa47035aa Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1200939 Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]> Reviewed-by: Daniel Martí <[email protected]>
1 parent 8507a5a commit 3a11f99

23 files changed

+81
-293
lines changed

encoding/jsonschema/constraints_array.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func constraintAdditionalItems(key string, n cue.Value, s *state) {
4141
}
4242

4343
func constraintMinContains(key string, n cue.Value, s *state) {
44-
p, err := n.Uint64()
44+
p, err := uint64Value(n)
4545
if err != nil {
4646
s.errf(n, `value of "minContains" must be a non-negative integer value`)
4747
return
@@ -50,7 +50,7 @@ func constraintMinContains(key string, n cue.Value, s *state) {
5050
}
5151

5252
func constraintMaxContains(key string, n cue.Value, s *state) {
53-
p, err := n.Uint64()
53+
p, err := uint64Value(n)
5454
if err != nil {
5555
s.errf(n, `value of "maxContains" must be a non-negative integer value`)
5656
return
@@ -112,7 +112,7 @@ func constraintMaxItems(key string, n cue.Value, s *state) {
112112

113113
func constraintMinItems(key string, n cue.Value, s *state) {
114114
a := []ast.Expr{}
115-
p, err := n.Uint64()
115+
p, err := uint64Value(n)
116116
if err != nil {
117117
s.errf(n, "invalid uint")
118118
}

encoding/jsonschema/decode.go

+32-4
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ package jsonschema
2020

2121
import (
2222
"fmt"
23+
"math"
2324
"net/url"
2425
"sort"
26+
"strconv"
2527
"strings"
2628

2729
"cuelang.org/go/cue"
@@ -221,12 +223,16 @@ func (d *decoder) number(n cue.Value) ast.Expr {
221223
return n.Syntax(cue.Final()).(ast.Expr)
222224
}
223225

224-
func (d *decoder) uint(n cue.Value) ast.Expr {
225-
_, err := n.Uint64()
226+
func (d *decoder) uint(nv cue.Value) ast.Expr {
227+
n, err := uint64Value(nv)
226228
if err != nil {
227-
d.errf(n, "invalid uint")
229+
d.errf(nv, "invalid uint")
230+
}
231+
return &ast.BasicLit{
232+
ValuePos: nv.Pos(),
233+
Kind: token.FLOAT,
234+
Value: strconv.FormatUint(n, 10),
228235
}
229-
return n.Syntax(cue.Final()).(ast.Expr)
230236
}
231237

232238
func (d *decoder) boolValue(n cue.Value) bool {
@@ -811,3 +817,25 @@ func setPos(e ast.Expr, v cue.Value) ast.Expr {
811817
ast.SetPos(e, v.Pos())
812818
return e
813819
}
820+
821+
// uint64Value is like v.Uint64 except that it
822+
// also allows floating point constants, as long
823+
// as they have no fractional part.
824+
func uint64Value(v cue.Value) (uint64, error) {
825+
n, err := v.Uint64()
826+
if err == nil {
827+
return n, nil
828+
}
829+
f, err := v.Float64()
830+
if err != nil {
831+
return 0, err
832+
}
833+
intPart, fracPart := math.Modf(f)
834+
if fracPart != 0 {
835+
return 0, errors.Newf(v.Pos(), "%v is not a whole number", v)
836+
}
837+
if intPart < 0 || intPart > math.MaxUint64 {
838+
return 0, errors.Newf(v.Pos(), "%v is out of bounds", v)
839+
}
840+
return uint64(intPart), nil
841+
}
+6-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Generated by teststats. DO NOT EDIT
22
v2:
3-
schema extract (pass / total): 1020 / 1637 = 62.3%
4-
tests (pass / total): 3338 / 7175 = 46.5%
5-
tests on extracted schemas (pass / total): 3338 / 3752 = 89.0%
3+
schema extract (pass / total): 1040 / 1637 = 63.5%
4+
tests (pass / total): 3378 / 7175 = 47.1%
5+
tests on extracted schemas (pass / total): 3378 / 3792 = 89.1%
66

77
v3:
8-
schema extract (pass / total): 1008 / 1637 = 61.6%
9-
tests (pass / total): 3289 / 7175 = 45.8%
10-
tests on extracted schemas (pass / total): 3289 / 3708 = 88.7%
8+
schema extract (pass / total): 1028 / 1637 = 62.8%
9+
tests (pass / total): 3329 / 7175 = 46.4%
10+
tests on extracted schemas (pass / total): 3329 / 3748 = 88.8%

encoding/jsonschema/testdata/external/tests/draft2019-09/maxContains.json

+2-14
Original file line numberDiff line numberDiff line change
@@ -81,33 +81,21 @@
8181
},
8282
"maxContains": 1.0
8383
},
84-
"skip": {
85-
"v2": "extract error: value of \"maxContains\" must be a non-negative integer value",
86-
"v3": "extract error: value of \"maxContains\" must be a non-negative integer value"
87-
},
8884
"tests": [
8985
{
9086
"description": "one element matches, valid maxContains",
9187
"data": [
9288
1
9389
],
94-
"valid": true,
95-
"skip": {
96-
"v2": "could not compile schema",
97-
"v3": "could not compile schema"
98-
}
90+
"valid": true
9991
},
10092
{
10193
"description": "too many elements match, invalid maxContains",
10294
"data": [
10395
1,
10496
1
10597
],
106-
"valid": false,
107-
"skip": {
108-
"v2": "could not compile schema",
109-
"v3": "could not compile schema"
110-
}
98+
"valid": false
11199
}
112100
]
113101
},

encoding/jsonschema/testdata/external/tests/draft2019-09/maxItems.json

+2-14
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,13 @@
4343
"$schema": "https://json-schema.org/draft/2019-09/schema",
4444
"maxItems": 2.0
4545
},
46-
"skip": {
47-
"v2": "extract error: invalid uint",
48-
"v3": "extract error: invalid uint"
49-
},
5046
"tests": [
5147
{
5248
"description": "shorter is valid",
5349
"data": [
5450
1
5551
],
56-
"valid": true,
57-
"skip": {
58-
"v2": "could not compile schema",
59-
"v3": "could not compile schema"
60-
}
52+
"valid": true
6153
},
6254
{
6355
"description": "too long is invalid",
@@ -66,11 +58,7 @@
6658
2,
6759
3
6860
],
69-
"valid": false,
70-
"skip": {
71-
"v2": "could not compile schema",
72-
"v3": "could not compile schema"
73-
}
61+
"valid": false
7462
}
7563
]
7664
}

encoding/jsonschema/testdata/external/tests/draft2019-09/maxProperties.json

+2-14
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,13 @@
5757
"$schema": "https://json-schema.org/draft/2019-09/schema",
5858
"maxProperties": 2.0
5959
},
60-
"skip": {
61-
"v2": "extract error: invalid uint",
62-
"v3": "extract error: invalid uint"
63-
},
6460
"tests": [
6561
{
6662
"description": "shorter is valid",
6763
"data": {
6864
"foo": 1
6965
},
70-
"valid": true,
71-
"skip": {
72-
"v2": "could not compile schema",
73-
"v3": "could not compile schema"
74-
}
66+
"valid": true
7567
},
7668
{
7769
"description": "too long is invalid",
@@ -80,11 +72,7 @@
8072
"bar": 2,
8173
"baz": 3
8274
},
83-
"valid": false,
84-
"skip": {
85-
"v2": "could not compile schema",
86-
"v3": "could not compile schema"
87-
}
75+
"valid": false
8876
}
8977
]
9078
},

encoding/jsonschema/testdata/external/tests/draft2019-09/minContains.json

+2-14
Original file line numberDiff line numberDiff line change
@@ -134,33 +134,21 @@
134134
},
135135
"minContains": 2.0
136136
},
137-
"skip": {
138-
"v2": "extract error: value of \"minContains\" must be a non-negative integer value",
139-
"v3": "extract error: value of \"minContains\" must be a non-negative integer value"
140-
},
141137
"tests": [
142138
{
143139
"description": "one element matches, invalid minContains",
144140
"data": [
145141
1
146142
],
147-
"valid": false,
148-
"skip": {
149-
"v2": "could not compile schema",
150-
"v3": "could not compile schema"
151-
}
143+
"valid": false
152144
},
153145
{
154146
"description": "both elements match, valid minContains",
155147
"data": [
156148
1,
157149
1
158150
],
159-
"valid": true,
160-
"skip": {
161-
"v2": "could not compile schema",
162-
"v3": "could not compile schema"
163-
}
151+
"valid": true
164152
}
165153
]
166154
},

encoding/jsonschema/testdata/external/tests/draft2019-09/minItems.json

+2-14
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,19 @@
3939
"$schema": "https://json-schema.org/draft/2019-09/schema",
4040
"minItems": 1.0
4141
},
42-
"skip": {
43-
"v2": "extract error: invalid uint",
44-
"v3": "extract error: invalid uint"
45-
},
4642
"tests": [
4743
{
4844
"description": "longer is valid",
4945
"data": [
5046
1,
5147
2
5248
],
53-
"valid": true,
54-
"skip": {
55-
"v2": "could not compile schema",
56-
"v3": "could not compile schema"
57-
}
49+
"valid": true
5850
},
5951
{
6052
"description": "too short is invalid",
6153
"data": [],
62-
"valid": false,
63-
"skip": {
64-
"v2": "could not compile schema",
65-
"v3": "could not compile schema"
66-
}
54+
"valid": false
6755
}
6856
]
6957
}

encoding/jsonschema/testdata/external/tests/draft2019-09/minProperties.json

+2-14
Original file line numberDiff line numberDiff line change
@@ -49,31 +49,19 @@
4949
"$schema": "https://json-schema.org/draft/2019-09/schema",
5050
"minProperties": 1.0
5151
},
52-
"skip": {
53-
"v2": "extract error: invalid uint",
54-
"v3": "extract error: invalid uint"
55-
},
5652
"tests": [
5753
{
5854
"description": "longer is valid",
5955
"data": {
6056
"foo": 1,
6157
"bar": 2
6258
},
63-
"valid": true,
64-
"skip": {
65-
"v2": "could not compile schema",
66-
"v3": "could not compile schema"
67-
}
59+
"valid": true
6860
},
6961
{
7062
"description": "too short is invalid",
7163
"data": {},
72-
"valid": false,
73-
"skip": {
74-
"v2": "could not compile schema",
75-
"v3": "could not compile schema"
76-
}
64+
"valid": false
7765
}
7866
]
7967
}

encoding/jsonschema/testdata/external/tests/draft2020-12/maxContains.json

+2-14
Original file line numberDiff line numberDiff line change
@@ -81,33 +81,21 @@
8181
},
8282
"maxContains": 1.0
8383
},
84-
"skip": {
85-
"v2": "extract error: value of \"maxContains\" must be a non-negative integer value",
86-
"v3": "extract error: value of \"maxContains\" must be a non-negative integer value"
87-
},
8884
"tests": [
8985
{
9086
"description": "one element matches, valid maxContains",
9187
"data": [
9288
1
9389
],
94-
"valid": true,
95-
"skip": {
96-
"v2": "could not compile schema",
97-
"v3": "could not compile schema"
98-
}
90+
"valid": true
9991
},
10092
{
10193
"description": "too many elements match, invalid maxContains",
10294
"data": [
10395
1,
10496
1
10597
],
106-
"valid": false,
107-
"skip": {
108-
"v2": "could not compile schema",
109-
"v3": "could not compile schema"
110-
}
98+
"valid": false
11199
}
112100
]
113101
},

encoding/jsonschema/testdata/external/tests/draft2020-12/maxItems.json

+2-14
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,13 @@
4343
"$schema": "https://json-schema.org/draft/2020-12/schema",
4444
"maxItems": 2.0
4545
},
46-
"skip": {
47-
"v2": "extract error: invalid uint",
48-
"v3": "extract error: invalid uint"
49-
},
5046
"tests": [
5147
{
5248
"description": "shorter is valid",
5349
"data": [
5450
1
5551
],
56-
"valid": true,
57-
"skip": {
58-
"v2": "could not compile schema",
59-
"v3": "could not compile schema"
60-
}
52+
"valid": true
6153
},
6254
{
6355
"description": "too long is invalid",
@@ -66,11 +58,7 @@
6658
2,
6759
3
6860
],
69-
"valid": false,
70-
"skip": {
71-
"v2": "could not compile schema",
72-
"v3": "could not compile schema"
73-
}
61+
"valid": false
7462
}
7563
]
7664
}

0 commit comments

Comments
 (0)