Skip to content

Commit 48431d8

Browse files
haoqixumpvl
authored andcommitted
pkg/list: add MatchN validator function
This change adds the `list.MatchN` validator function, which is useful toich is useful to support JSON Schema validators such as `contains`, `minContains` and `maxContains`. Fixes #3370 Change-Id: I88bbefc4c318de02a47c7a4b7da3279ce41c3f09 Signed-off-by: haoqixu <[email protected]> Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1199602 Reviewed-by: Roger Peppe <[email protected]> Reviewed-by: Marcel van Lohuizen <[email protected]> Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
1 parent 2f19b1a commit 48431d8

File tree

4 files changed

+213
-0
lines changed

4 files changed

+213
-0
lines changed

pkg/list/list.go

+31
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"cuelang.org/go/cue/token"
2626
"cuelang.org/go/internal/core/adt"
2727
"cuelang.org/go/internal/pkg"
28+
"cuelang.org/go/internal/value"
2829
)
2930

3031
// Drop reports the suffix of list x after the first n elements,
@@ -284,3 +285,33 @@ func Contains(a []cue.Value, v cue.Value) bool {
284285
}
285286
return false
286287
}
288+
289+
// MatchN is a validator that checks that the number of elements in the given
290+
// list that unifies with the schema "matchValue" matches "n".
291+
// "n" may be a number constraint and does not have to be a concrete number.
292+
// Likewise, "matchValue" will usually be a non-concrete value.
293+
func MatchN(list []cue.Value, n pkg.Schema, matchValue pkg.Schema) (bool, error) {
294+
var nmatch int64
295+
for _, w := range list {
296+
if matchValue.Unify(w).Validate() == nil {
297+
nmatch++
298+
}
299+
}
300+
301+
r, _ := value.ToInternal(n)
302+
ctx := (*cue.Context)(r)
303+
304+
if err := n.Unify(ctx.Encode(nmatch)).Validate(); err != nil {
305+
return false, pkg.ValidationError{B: &adt.Bottom{
306+
Code: adt.EvalError,
307+
Err: errors.Newf(
308+
token.NoPos,
309+
"number of matched elements is %d: does not satisfy %v",
310+
nmatch,
311+
n,
312+
),
313+
}}
314+
}
315+
316+
return true, nil
317+
}

pkg/list/pkg.go

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/list/testdata/matchn.txtar

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
-- in.cue --
2+
import "list"
3+
4+
t1: {
5+
[=~"^l"]: [1, 2, 3, "str", [1], { foo: 1 }]
6+
7+
l1: list.MatchN(1, string)
8+
l2: list.MatchN(3, number)
9+
l3: list.MatchN(>=1, number)
10+
l4: list.MatchN(1, [int])
11+
l5: list.MatchN(1, {foo: int})
12+
l6: list.MatchN(1, 1)
13+
l7: list.MatchN(1, "str")
14+
l8: list.MatchN(>0, "str")
15+
l9: list.MatchN(1, {foo: 1})
16+
l10: list.MatchN(0, #TOO)
17+
l11: list.MatchN(0, [string])
18+
l12: list.MatchN(3, int | *1)
19+
l13: list.MatchN(2, 2 | *1)
20+
l14: list.MatchN(1 | 2, 2 | *1)
21+
22+
c1: ["xx", 1, 2, 3] | [1]
23+
c1: list.MatchN(>=2, 1 | *2)
24+
}
25+
#TOO: {too: int}
26+
-- out/list --
27+
t1: {
28+
l1: [1, 2, 3, "str", [1], {
29+
foo: 1
30+
}]
31+
l2: [1, 2, 3, "str", [1], {
32+
foo: 1
33+
}]
34+
l3: [1, 2, 3, "str", [1], {
35+
foo: 1
36+
}]
37+
l4: [1, 2, 3, "str", [1], {
38+
foo: 1
39+
}]
40+
l5: [1, 2, 3, "str", [1], {
41+
foo: 1
42+
}]
43+
l6: [1, 2, 3, "str", [1], {
44+
foo: 1
45+
}]
46+
l7: [1, 2, 3, "str", [1], {
47+
foo: 1
48+
}]
49+
l8: [1, 2, 3, "str", [1], {
50+
foo: 1
51+
}]
52+
l9: [1, 2, 3, "str", [1], {
53+
foo: 1
54+
}]
55+
l10: [1, 2, 3, "str", [1], {
56+
foo: 1
57+
}]
58+
l11: [1, 2, 3, "str", [1], {
59+
foo: 1
60+
}]
61+
l12: [1, 2, 3, "str", [1], {
62+
foo: 1
63+
}]
64+
l13: [1, 2, 3, "str", [1], {
65+
foo: 1
66+
}]
67+
l14: [1, 2, 3, "str", [1], {
68+
foo: 1
69+
}]
70+
c1: ["xx", 1, 2, 3]
71+
}
72+
#TOO: {
73+
too: int
74+
}

pkg/list/testdata/matchn_err.txtar

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
-- in.cue --
2+
import "list"
3+
4+
t1: {
5+
[=~"^l"]: [1, 2, 3, "str", [1], { foo: 1 }]
6+
7+
l1: list.MatchN(>0, [string])
8+
l2: list.MatchN(1, number)
9+
l3: list.MatchN(>1, string)
10+
l4: list.MatchN(0, number)
11+
l5: list.MatchN(string, [int])
12+
l6: list.MatchN(>0, #TOO)
13+
14+
}
15+
#TOO: {too: int}
16+
-- out/list-v3 --
17+
Errors:
18+
t1.l1: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>0, [string])): number of matched elements is 0: does not satisfy >0:
19+
./in.cue:6:6
20+
./in.cue:6:18
21+
t1.l2: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(1, number)): number of matched elements is 3: does not satisfy 1:
22+
./in.cue:7:6
23+
./in.cue:7:18
24+
./in.cue:7:21
25+
t1.l3: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>1, string)): number of matched elements is 1: does not satisfy >1:
26+
./in.cue:8:6
27+
./in.cue:8:18
28+
./in.cue:8:22
29+
t1.l4: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(0, number)): number of matched elements is 3: does not satisfy 0:
30+
./in.cue:9:6
31+
./in.cue:9:18
32+
./in.cue:9:21
33+
t1.l5: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(string, [int])): number of matched elements is 1: does not satisfy string:
34+
./in.cue:10:6
35+
./in.cue:10:18
36+
t1.l6: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>0, {too:int})): number of matched elements is 0: does not satisfy >0:
37+
./in.cue:11:6
38+
./in.cue:11:18
39+
40+
Result:
41+
t1: {
42+
l1: _|_ // t1.l1: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>0, [string])): number of matched elements is 0: does not satisfy >0
43+
l2: _|_ // t1.l2: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(1, number)): number of matched elements is 3: does not satisfy 1
44+
l3: _|_ // t1.l3: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>1, string)): number of matched elements is 1: does not satisfy >1
45+
l4: _|_ // t1.l4: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(0, number)): number of matched elements is 3: does not satisfy 0
46+
l5: _|_ // t1.l5: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(string, [int])): number of matched elements is 1: does not satisfy string
47+
l6: _|_ // t1.l6: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>0, {too:int})): number of matched elements is 0: does not satisfy >0
48+
}
49+
#TOO: {
50+
too: int
51+
}
52+
-- out/list --
53+
Errors:
54+
t1.l1: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>0, [string])): number of matched elements is 0: does not satisfy >0:
55+
./in.cue:6:6
56+
./in.cue:4:12
57+
./in.cue:6:18
58+
t1.l2: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(1, number)): number of matched elements is 3: does not satisfy 1:
59+
./in.cue:7:6
60+
./in.cue:4:12
61+
./in.cue:7:18
62+
./in.cue:7:21
63+
t1.l3: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>1, string)): number of matched elements is 1: does not satisfy >1:
64+
./in.cue:8:6
65+
./in.cue:4:12
66+
./in.cue:8:18
67+
./in.cue:8:22
68+
t1.l4: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(0, number)): number of matched elements is 3: does not satisfy 0:
69+
./in.cue:9:6
70+
./in.cue:4:12
71+
./in.cue:9:18
72+
./in.cue:9:21
73+
t1.l5: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(string, [int])): number of matched elements is 1: does not satisfy string:
74+
./in.cue:10:6
75+
./in.cue:4:12
76+
./in.cue:10:18
77+
t1.l6: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>0, {too:int})): number of matched elements is 0: does not satisfy >0:
78+
./in.cue:11:6
79+
./in.cue:4:12
80+
./in.cue:11:18
81+
82+
Result:
83+
t1: {
84+
l1: _|_ // t1.l1: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>0, [string])): number of matched elements is 0: does not satisfy >0
85+
l2: _|_ // t1.l2: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(1, number)): number of matched elements is 3: does not satisfy 1
86+
l3: _|_ // t1.l3: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>1, string)): number of matched elements is 1: does not satisfy >1
87+
l4: _|_ // t1.l4: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(0, number)): number of matched elements is 3: does not satisfy 0
88+
l5: _|_ // t1.l5: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(string, [int])): number of matched elements is 1: does not satisfy string
89+
l6: _|_ // t1.l6: invalid value [1,2,3,"str",[1],{foo:1}] (does not satisfy list.MatchN(>0, {too:int})): number of matched elements is 0: does not satisfy >0
90+
}
91+
#TOO: {
92+
too: int
93+
}

0 commit comments

Comments
 (0)