this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <hq.xu0o0@gmail.com>
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1199602
Reviewed-by: Roger Peppe <rogpeppe@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@gmail.com>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

authored by

haoqixu and committed by
Marcel van Lohuizen
48431d86 2f19b1a7

+213
+31
pkg/list/list.go
··· 25 25 "cuelang.org/go/cue/token" 26 26 "cuelang.org/go/internal/core/adt" 27 27 "cuelang.org/go/internal/pkg" 28 + "cuelang.org/go/internal/value" 28 29 ) 29 30 30 31 // Drop reports the suffix of list x after the first n elements, ··· 284 285 } 285 286 return false 286 287 } 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 + }
+15
pkg/list/pkg.go
··· 156 156 } 157 157 }, 158 158 }, { 159 + Name: "MatchN", 160 + Params: []pkg.Param{ 161 + {Kind: adt.ListKind}, 162 + {Kind: adt.TopKind}, 163 + {Kind: adt.TopKind}, 164 + }, 165 + Result: adt.BoolKind, 166 + NonConcrete: true, 167 + Func: func(c *pkg.CallCtxt) { 168 + list, n, matchValue := c.List(0), c.Schema(1), c.Schema(2) 169 + if c.Do() { 170 + c.Ret, c.Err = MatchN(list, n, matchValue) 171 + } 172 + }, 173 + }, { 159 174 Name: "Avg", 160 175 Params: []pkg.Param{ 161 176 {Kind: adt.ListKind},
+74
pkg/list/testdata/matchn.txtar
··· 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 + }
+93
pkg/list/testdata/matchn_err.txtar
··· 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 + }