-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs/howto: use the built-in function "matchN"
This adds a guide demonstrating different uses for the new "matchN" built-in function. The guide contains several separate sections which have use cases aligned with common phrases such as "all of" and "any of", insofar as they apply to the checking of some list of constraints. A final section captures a couple of more involved uses of the function which didn't make sense to include earlier on the page. A sub-optimal example demonstrating matchN validing a list value is included because I felt it was important to show that a list *can* be validated, but unfortunately I can't construct a list-based example that has /nice/ error reporting. This is tracked in cue-lang/cue#3389. Fixes cue-lang/docs-and-content#176. Preview-Path: /docs/howto/use-the-built-in-function-matchn/ Signed-off-by: Jonathan Matthews <[email protected]> Change-Id: Ibc59f98dfc1f7bda95a5aa1baf13c64bf8b3d753 Dispatch-Trailer: {"type":"trybot","CL":1199805,"patchset":10,"ref":"refs/changes/05/1199805/10","targetBranch":"master"}
- Loading branch information
1 parent
2b884cc
commit 74d687f
Showing
4 changed files
with
688 additions
and
0 deletions.
There are no files selected for viewing
234 changes: 234 additions & 0 deletions
234
content/docs/howto/use-the-built-in-function-matchn/en.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
--- | ||
title: Using the built-in function "matchN" as a field validator | ||
tags: [commented cue] | ||
authors: [jpluscplusm] | ||
toc_hide: true | ||
--- | ||
|
||
{{{with _script_ "en" "HIDDEN: access to cue tip"}}} | ||
export PATH=/cues/$CUELANG_CUE_TIP:$PATH | ||
{{{end}}} | ||
|
||
{{<caution>}} | ||
This guide demonstrates a CUE feature that isn't yet available in a release. | ||
It is only available in the `cue` command and Go API when they are | ||
[installed from source]({{< relref "/docs/introduction/installation" >}}#install-from-source) | ||
using the `@master` version selector. | ||
{{</caution>}} | ||
|
||
This [Commented CUE]({{< relref "docs/howto#commented-cue-guides" >}}) | ||
demonstrates how to use the | ||
[built-in]({{< relref "docs/reference/glossary#built-in-functions" >}}) | ||
function `matchN()` as a field validator. This flexible function allows CUE | ||
constraints to be bound together in a wide range of combinations, including | ||
"one of", "any of", "all of", and "none of"/"not". | ||
|
||
This guide uses the following unreleased version of CUE: | ||
|
||
{{{with script "en" "cue version"}}} | ||
#ellipsis 1 | ||
cue version | ||
{{{end}}} | ||
|
||
## Basic use | ||
|
||
The `matchN()` function takes two arguments: | ||
|
||
1. a **number constraint**; | ||
2. a **list of constraints**. | ||
|
||
The function validates a field's value by unifying the value with each item in | ||
the **list of constraints** in turn, and keeping count of how many list items | ||
the field's value is able to unify with. A field's value is valid if the count | ||
unifies successfully with the **number constraint**: | ||
|
||
<!-- We use upload/script pairs because code blocks can't access non-default | ||
versions of CUE cf. https://cuelang.org/issues/3265 --> | ||
{{{with upload "en" "basic"}}} | ||
-- basic.cue -- | ||
package basic | ||
|
||
A: 42 | ||
// A validates successfully. | ||
A: matchN(1, [int]) | ||
A: matchN(2, [int, >10]) | ||
A: matchN(2, [int, >10, >100]) | ||
|
||
B: 42 | ||
// B fails to validate. | ||
B: matchN(1, [int, >10]) | ||
B: matchN(3, [int, >10, >100]) | ||
{{{end}}} | ||
{{{with script "en" "basic"}}} | ||
! cue vet .:basic | ||
{{{end}}} | ||
|
||
## "One of" | ||
|
||
With its **number constraint** set to `1` the `matchN()` function checks that a | ||
field's value unifies successfully with just one of the **list of | ||
constraints**: | ||
|
||
{{{with upload "en" "one of"}}} | ||
-- one-of.cue -- | ||
package oneOf | ||
|
||
A: 42 | ||
// A validates successfully. | ||
A: matchN(1, [int]) | ||
A: matchN(1, [>10, >100, string]) | ||
|
||
B: 42 | ||
// B fails to validate. | ||
B: matchN(1, [int, >10]) | ||
B: matchN(1, [string, >100]) | ||
{{{end}}} | ||
{{{with script "en" "one of"}}} | ||
! cue vet .:oneOf | ||
{{{end}}} | ||
|
||
## "Any of" | ||
|
||
If the **number constraint** is set to `>0`, the function checks that at least | ||
one of the **list of constraints** unifies with the field's value: | ||
|
||
{{{with upload "en" "any of"}}} | ||
-- any-of.cue -- | ||
package anyOf | ||
|
||
A: 42 | ||
// A validates successfully. | ||
A: matchN(>0, [>10]) | ||
A: matchN(>0, [int, >0, >100, string]) | ||
|
||
B: 42 | ||
// B fails to validate. | ||
B: matchN(1, [int, >0]) | ||
B: matchN(>0, [string, >100]) | ||
{{{end}}} | ||
{{{with script "en" "any of"}}} | ||
! cue vet .:anyOf | ||
{{{end}}} | ||
|
||
## "All of" | ||
|
||
To check that a field's value unifies successfully with all of the **list of | ||
constraints**, set the **number constraint** to a value matching the number of | ||
items in the list: | ||
|
||
{{{with upload "en" "all of"}}} | ||
-- all-of.cue -- | ||
package allOf | ||
|
||
import "math" | ||
|
||
A: 42 | ||
// A validates successfully. | ||
A: matchN(1, [int]) | ||
A: matchN(2, [int, >10]) | ||
A: matchN(4, [int, >10, <100, math.MultipleOf(2)]) | ||
|
||
B: 42 | ||
// B fails to validate. | ||
B: matchN(3, [int, >10, >100]) | ||
B: matchN(4, [int, >10, <100, math.MultipleOf(41)]) | ||
{{{end}}} | ||
{{{with script "en" "all of"}}} | ||
! cue vet .:allOf | ||
{{{end}}} | ||
|
||
## "Not" | ||
|
||
If you set the **number constraint** to `0` then `matchN()` checks that a | ||
field's value doesn't unify successfully with any of the **list of | ||
constraints**: | ||
|
||
{{{with upload "en" "not"}}} | ||
-- not.cue -- | ||
package not | ||
|
||
import ( | ||
"strings" | ||
"struct" | ||
) | ||
|
||
A: 42 | ||
// A validates successfully. | ||
A: matchN(0, [string]) | ||
A: matchN(0, [bytes, struct.MinFields(0)]) | ||
A: matchN(0, [>100, strings.HasPrefix("4")]) | ||
|
||
B: 42 | ||
// B fails to validate. | ||
B: matchN(0, [int]) | ||
B: matchN(0, [string, number]) | ||
B: matchN(0, [42, >100, strings.HasSuffix(2)]) | ||
{{{end}}} | ||
{{{with script "en" "not"}}} | ||
! cue vet .:not | ||
{{{end}}} | ||
|
||
## More complex uses | ||
|
||
### References | ||
|
||
Either argument to `matchN()` can be resolved through a reference: | ||
|
||
{{{with upload "en" "all but one"}}} | ||
-- all-but-one.cue -- | ||
package allButOne | ||
|
||
// A validates successfully. | ||
A: 42 | ||
A: matchN(len(#C)-1, #C) | ||
|
||
// B fails to validate. | ||
B: 42.0 | ||
B: matchN(len(#C)-1, #C) | ||
|
||
#C: [number, int, >100] | ||
{{{end}}} | ||
{{{with script "en" "all but one"}}} | ||
! cue vet .:allButOne | ||
{{{end}}} | ||
|
||
### Composite data structures | ||
|
||
The `matchN()` function can validate composite data structures, not just | ||
primitive values. Use it with both structs and lists: | ||
|
||
{{{with upload "en" "composite"}}} | ||
-- composite.cue -- | ||
package composite | ||
|
||
// A validates successfully. | ||
A: matchN(>0, [#C1, #C2]) & { | ||
x: 42 | ||
y: "42" | ||
} | ||
// B fails to validate. | ||
B: matchN(>0, [#C1, #C2]) & { | ||
x: "4.2" | ||
y: "4.2" | ||
z: "4.2" | ||
} | ||
#C1: {x: int, ...} | ||
#C2: { | ||
z: float | ||
y: string | ||
... | ||
} | ||
|
||
// D validates successfully. | ||
D: [1, 2, 3] & matchN(1, [#F1, #F2, #F3]) | ||
// E fails to validate. | ||
E: [11, 12, 13] & matchN(1, [#F1, #F2, #F3]) | ||
#F1: [...>0] | ||
#F2: [...>10] | ||
#F3: [...>100] | ||
{{{end}}} | ||
{{{with script "en" "composite"}}} | ||
! cue vet .:composite | ||
{{{end}}} | ||
|
||
The sub-optimal error reporting for field `E` is tracked in {{<issue 3389/>}}. |
156 changes: 156 additions & 0 deletions
156
content/docs/howto/use-the-built-in-function-matchn/gen_cache.cue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package site | ||
{ | ||
content: { | ||
docs: { | ||
howto: { | ||
"use-the-built-in-function-matchn": { | ||
page: { | ||
cache: { | ||
upload: { | ||
basic: "BVCBRnKJG+wNRZrJOR04Rip8lj9gDNjO8nuavk2vYYg=" | ||
"one of": "zFAI8Lz70zs7s8ltNgV2f7FidjP/XtkFyb8X3yz+0mI=" | ||
"any of": "PSrMgOzunzkYDQ2cM614Gbo69iN6G5zQXKbsaEBWo7g=" | ||
"all of": "nqmwdA3y+knBFRRrmIV59aIQo/D+z+OueCjyGin1/wQ=" | ||
not: "zbtzhyLu7xy37xMBOo1JDbU0ibFAIL4wdNwhGQPX/DU=" | ||
"all but one": "fWgky6e5aUCD5GXEXZuaL/byX7/u4TRLamKxR4g85tg=" | ||
composite: "ZZjARSuVJMIo2mm7vc0nidivFMCtQtfp03GdBVC+6do=" | ||
} | ||
multi_step: { | ||
hash: "OKNQ7BA00BLUULEN7NVHS2P18L4JENKTBUTNL4T26U33713CRP9G====" | ||
scriptHash: "C2VPTFEKLRKCJQFHON0O2GLSQC0FCHTJGF4JKPJRCINNR5QQSPHG====" | ||
steps: [{ | ||
doc: "" | ||
cmd: "export PATH=/cues/v0.11.0-0.dev.0.20240904084811-bb24c7ce1f04:$PATH" | ||
exitCode: 0 | ||
output: "" | ||
}, { | ||
doc: "#ellipsis 1" | ||
cmd: "cue version" | ||
exitCode: 0 | ||
output: """ | ||
cue version v0.11.0-0.dev.0.20240904084811-bb24c7ce1f04 | ||
... | ||
""" | ||
}, { | ||
doc: "" | ||
cmd: "cue vet .:basic" | ||
exitCode: 1 | ||
output: """ | ||
B: invalid value 42 (does not satisfy matchN(1, [int,>10])): 2 matched, expected 1: | ||
./basic.cue:11:4 | ||
./basic.cue:9:4 | ||
./basic.cue:11:11 | ||
./basic.cue:12:4 | ||
B: invalid value 42 (does not satisfy matchN(3, [int,>10,>100])): 2 matched, expected 3: | ||
./basic.cue:12:4 | ||
./basic.cue:9:4 | ||
./basic.cue:11:4 | ||
./basic.cue:12:11 | ||
""" | ||
}, { | ||
doc: "" | ||
cmd: "cue vet .:oneOf" | ||
exitCode: 1 | ||
output: """ | ||
B: invalid value 42 (does not satisfy matchN(1, [string,>100])): 0 matched, expected 1: | ||
./one-of.cue:11:4 | ||
./one-of.cue:8:4 | ||
./one-of.cue:10:4 | ||
./one-of.cue:11:11 | ||
""" | ||
}, { | ||
doc: "" | ||
cmd: "cue vet .:anyOf" | ||
exitCode: 1 | ||
output: """ | ||
B: invalid value 42 (does not satisfy matchN(1, [int,>0])): 2 matched, expected 1: | ||
./any-of.cue:10:4 | ||
./any-of.cue:8:4 | ||
./any-of.cue:10:11 | ||
./any-of.cue:11:4 | ||
B: invalid value 42 (does not satisfy matchN(>0, [string,>100])): 0 matched, expected >0: | ||
./any-of.cue:11:4 | ||
./any-of.cue:8:4 | ||
./any-of.cue:10:4 | ||
./any-of.cue:11:11 | ||
""" | ||
}, { | ||
doc: "" | ||
cmd: "cue vet .:allOf" | ||
exitCode: 1 | ||
output: """ | ||
B: invalid value 42 (does not satisfy matchN(3, [int,>10,>100])): 2 matched, expected 3: | ||
./all-of.cue:13:4 | ||
./all-of.cue:11:4 | ||
./all-of.cue:13:11 | ||
./all-of.cue:14:4 | ||
B: invalid value 42 (does not satisfy matchN(4, [int,>10,<100,math.MultipleOf(41)])): 3 matched, expected 4: | ||
./all-of.cue:14:4 | ||
./all-of.cue:11:4 | ||
./all-of.cue:13:4 | ||
./all-of.cue:14:11 | ||
""" | ||
}, { | ||
doc: "" | ||
cmd: "cue vet .:not" | ||
exitCode: 1 | ||
output: """ | ||
B: invalid value 42 (does not satisfy matchN(0, [int])): 1 matched, expected 0: | ||
./not.cue:16:4 | ||
./not.cue:14:4 | ||
./not.cue:16:11 | ||
./not.cue:17:4 | ||
./not.cue:18:4 | ||
B: invalid value 42 (does not satisfy matchN(0, [string,number])): 1 matched, expected 0: | ||
./not.cue:17:4 | ||
./not.cue:14:4 | ||
./not.cue:16:4 | ||
./not.cue:17:11 | ||
./not.cue:18:4 | ||
B: invalid value 42 (does not satisfy matchN(0, [42,>100,strings.HasSuffix(2)])): 1 matched, expected 0: | ||
./not.cue:18:4 | ||
./not.cue:14:4 | ||
./not.cue:16:4 | ||
./not.cue:17:4 | ||
./not.cue:18:11 | ||
""" | ||
}, { | ||
doc: "" | ||
cmd: "cue vet .:allButOne" | ||
exitCode: 1 | ||
output: """ | ||
B: invalid value 42.0 (does not satisfy matchN(2, [number,int,>100])): 1 matched, expected 2: | ||
./all-but-one.cue:9:4 | ||
./all-but-one.cue:8:4 | ||
./all-but-one.cue:9:11 | ||
""" | ||
}, { | ||
doc: "" | ||
cmd: "cue vet .:composite" | ||
exitCode: 1 | ||
output: """ | ||
B: invalid value {x:"4.2",y:"4.2",z:"4.2"} (does not satisfy matchN(>0, [{x:int},{z:float,y:string}])): 0 matched, expected >0: | ||
./composite.cue:9:4 | ||
./composite.cue:9:11 | ||
E: invalid value [11,12,13] (does not satisfy matchN(1, [[],[],[]])): 2 matched, expected 1: | ||
./composite.cue:24:19 | ||
./composite.cue:24:4 | ||
./composite.cue:24:26 | ||
""" | ||
}] | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package site | ||
|
||
content: docs: howto: "use-the-built-in-function-matchn": page: _ |
Oops, something went wrong.