Skip to content

Commit 7648cbd

Browse files
committed
encoding/jsonschema: implement if/then/else keywords
Use the newly added `matchIf` builtin to implement the JSON Schema conditional operator. Signed-off-by: Roger Peppe <[email protected]> Change-Id: I0a64a67683d573f4bc348fb6704f7b18ac3339a7 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1200945 TryBot-Result: CUEcueckoo <[email protected]> Reviewed-by: Daniel Martí <[email protected]> Unity-Result: CUE porcuepine <[email protected]>
1 parent 276d940 commit 7648cbd

21 files changed

+216
-595
lines changed

cue.mod/pkg/github.com/SchemaStore/schemastore/src/schemas/json/github-workflow.cue

+31-1
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,37 @@ import (
394394
// converted to lowercase during runtime. We recommended using
395395
// lowercase input ids.
396396
inputs?: {
397-
{[=~"^[_a-zA-Z][a-zA-Z0-9_-]*$" & !~"^()$"]: {
397+
{[=~"^[_a-zA-Z][a-zA-Z0-9_-]*$" & !~"^()$"]: matchN(5, [matchIf(null | bool | number | string | [...] | {
398+
type!: "string", ...
399+
}, null | bool | number | string | [...] | {
400+
default?: string, ...
401+
}, _) & {
402+
...
403+
}, matchIf(null | bool | number | string | [...] | {
404+
type!: "boolean", ...
405+
}, null | bool | number | string | [...] | {
406+
default?: bool, ...
407+
}, _) & {
408+
...
409+
}, matchIf(null | bool | number | string | [...] | {
410+
type!: "number", ...
411+
}, null | bool | number | string | [...] | {
412+
default?: number, ...
413+
}, _) & {
414+
...
415+
}, matchIf(null | bool | number | string | [...] | {
416+
type!: "environment", ...
417+
}, null | bool | number | string | [...] | {
418+
default?: string, ...
419+
}, _) & {
420+
...
421+
}, matchIf(null | bool | number | string | [...] | {
422+
type!: "choice", ...
423+
}, null | bool | number | string | [...] | {
424+
options!: _, ...
425+
}, _) & {
426+
...
427+
}]) & {
398428
// A string description of the input parameter.
399429
description!: string
400430

encoding/jsonschema/constraints.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ var constraints = []*constraint{
9090
p2("deprecated", constraintDeprecated, vfrom(VersionDraft2019_09)|openAPI),
9191
p2("description", constraintDescription, allVersions|openAPI),
9292
p1("discriminator", constraintTODO, openAPI),
93-
p1("else", constraintTODO, vfrom(VersionDraft7)),
93+
p1("else", constraintElse, vfrom(VersionDraft7)),
9494
p2("enum", constraintEnum, allVersions|openAPI),
9595
p1("example", constraintTODO, openAPI),
9696
p2("examples", constraintExamples, vfrom(VersionDraft6)),
@@ -99,7 +99,7 @@ var constraints = []*constraint{
9999
p1("externalDocs", constraintTODO, openAPI),
100100
p1("format", constraintTODO, allVersions|openAPI),
101101
p1("id", constraintID, vto(VersionDraft4)),
102-
p1("if", constraintTODO, vfrom(VersionDraft7)),
102+
p1("if", constraintIf, vfrom(VersionDraft7)),
103103
p2("items", constraintItems, allVersions|openAPI),
104104
p1("maxContains", constraintMaxContains, vfrom(VersionDraft2019_09)),
105105
p2("maxItems", constraintMaxItems, allVersions|openAPI),
@@ -122,7 +122,7 @@ var constraints = []*constraint{
122122
p2("propertyNames", constraintPropertyNames, vfrom(VersionDraft6)),
123123
p1("readOnly", constraintTODO, vfrom(VersionDraft7)|openAPI),
124124
p3("required", constraintRequired, allVersions|openAPI),
125-
p1("then", constraintTODO, vfrom(VersionDraft7)),
125+
p1("then", constraintThen, vfrom(VersionDraft7)),
126126
p2("title", constraintTitle, allVersions|openAPI),
127127
p2("type", constraintType, allVersions|openAPI),
128128
p1("unevaluatedItems", constraintTODO, vfrom(VersionDraft2019_09)),

encoding/jsonschema/constraints_combinator.go

+12
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,15 @@ func constraintNot(key string, n cue.Value, s *state) {
148148
ast.NewList(subSchema),
149149
))
150150
}
151+
152+
func constraintIf(key string, n cue.Value, s *state) {
153+
s.ifConstraint = s.schema(n)
154+
}
155+
156+
func constraintThen(key string, n cue.Value, s *state) {
157+
s.thenConstraint = s.schema(n)
158+
}
159+
160+
func constraintElse(key string, n cue.Value, s *state) {
161+
s.elseConstraint = s.schema(n)
162+
}

encoding/jsonschema/decode.go

+23
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ type state struct {
385385
minContains *uint64
386386
maxContains *uint64
387387

388+
ifConstraint ast.Expr
389+
thenConstraint ast.Expr
390+
elseConstraint ast.Expr
391+
388392
schemaVersion Version
389393
schemaVersionPresent bool
390394

@@ -459,6 +463,7 @@ func (s *state) finalize() (e ast.Expr) {
459463
s.addErr(errors.Newf(s.pos.Pos(), "constraints are not possible to satisfy"))
460464
return bottom()
461465
}
466+
s.addIfThenElse()
462467

463468
conjuncts := []ast.Expr{}
464469
disjuncts := []ast.Expr{}
@@ -608,6 +613,24 @@ outer:
608613
return e
609614
}
610615

616+
func (s *state) addIfThenElse() {
617+
if s.ifConstraint == nil || (s.thenConstraint == nil && s.elseConstraint == nil) {
618+
return
619+
}
620+
if s.thenConstraint == nil {
621+
s.thenConstraint = top()
622+
}
623+
if s.elseConstraint == nil {
624+
s.elseConstraint = top()
625+
}
626+
s.all.add(s.pos, ast.NewCall(
627+
ast.NewIdent("matchIf"),
628+
s.ifConstraint,
629+
s.thenConstraint,
630+
s.elseConstraint,
631+
))
632+
}
633+
611634
func (s *state) comment() *ast.CommentGroup {
612635
// Create documentation.
613636
doc := strings.TrimSpace(s.title)
+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): 1040 / 1637 = 63.5%
4-
tests (pass / total): 3378 / 7175 = 47.1%
5-
tests on extracted schemas (pass / total): 3378 / 3792 = 89.1%
3+
schema extract (pass / total): 1072 / 1637 = 65.5%
4+
tests (pass / total): 3457 / 7175 = 48.2%
5+
tests on extracted schemas (pass / total): 3457 / 3874 = 89.2%
66

77
v3:
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%
8+
schema extract (pass / total): 1060 / 1637 = 64.8%
9+
tests (pass / total): 3408 / 7175 = 47.5%
10+
tests on extracted schemas (pass / total): 3408 / 3830 = 89.0%

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

+3-11
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,6 @@
201201
"else": true
202202
}
203203
},
204-
"skip": {
205-
"v2": "extract error: keyword \"if\" not yet implemented (and 1 more errors)",
206-
"v3": "extract error: keyword \"if\" not yet implemented (and 1 more errors)"
207-
},
208204
"tests": [
209205
{
210206
"description": "any non-empty array is valid",
@@ -213,18 +209,14 @@
213209
],
214210
"valid": true,
215211
"skip": {
216-
"v2": "could not compile schema",
217-
"v3": "could not compile schema"
212+
"v2": "6 errors in empty disjunction:\nconflicting values [\"foo\"] and {...} (mismatched types list and struct):\n generated.cue:4:1\n generated.cue:4:72\n instance.json:1:1\nconflicting values bool and [\"foo\"] (mismatched types bool and list):\n generated.cue:4:8\n instance.json:1:1\nconflicting values null and [\"foo\"] (mismatched types null and list):\n generated.cue:4:1\n instance.json:1:1\nconflicting values number and [\"foo\"] (mismatched types number and list):\n generated.cue:4:15\n instance.json:1:1\nconflicting values string and [\"foo\"] (mismatched types string and list):\n generated.cue:4:24\n instance.json:1:1\nexplicit error (_|_ literal) in source:\n generated.cue:4:58\n",
213+
"v3": "conflicting values [\"foo\"] and {...} (mismatched types list and struct):\n generated.cue:4:72\n instance.json:1:1\nconflicting values bool and [\"foo\"] (mismatched types bool and list):\n generated.cue:4:8\n instance.json:1:1\nconflicting values null and [\"foo\"] (mismatched types null and list):\n generated.cue:4:1\n instance.json:1:1\nconflicting values number and [\"foo\"] (mismatched types number and list):\n generated.cue:4:15\n instance.json:1:1\nconflicting values string and [\"foo\"] (mismatched types string and list):\n generated.cue:4:24\n instance.json:1:1\nexplicit error (_|_ literal) in source:\n generated.cue:4:58\n"
218214
}
219215
},
220216
{
221217
"description": "empty array is invalid",
222218
"data": [],
223-
"valid": false,
224-
"skip": {
225-
"v2": "could not compile schema",
226-
"v3": "could not compile schema"
227-
}
219+
"valid": false
228220
}
229221
]
230222
},

0 commit comments

Comments
 (0)