From ba56d47993fe388f4adb46c1dff862b70932ef55 Mon Sep 17 00:00:00 2001 From: Jonathan Matthews Date: Wed, 21 Aug 2024 11:27:41 +0100 Subject: [PATCH] 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. Also: tweak installation page so that its install-from-source language and examples include those required by the new guide -- which is the first page on the site to demonstrate an unreleased feature, and hence needs to refer to installation of tip from source. Fixes cue-lang/docs-and-content#176. Preview-Path: /docs/howto/use-the-built-in-function-matchn/ Preview-Path: /docs/introduction/installation/ Signed-off-by: Jonathan Matthews Change-Id: Ibc59f98dfc1f7bda95a5aa1baf13c64bf8b3d753 Dispatch-Trailer: {"type":"trybot","CL":1199805,"patchset":7,"ref":"refs/changes/05/1199805/7","targetBranch":"master"} --- .../use-the-built-in-function-matchn/en.md | 234 ++++++++++++++ .../gen_cache.cue | 156 +++++++++ .../use-the-built-in-function-matchn/page.cue | 3 + content/docs/introduction/installation/en.md | 26 +- .../introduction/installation/gen_cache.cue | 12 +- .../use-the-built-in-function-matchn/index.md | 295 ++++++++++++++++++ .../docs/introduction/installation/index.md | 24 +- 7 files changed, 739 insertions(+), 11 deletions(-) create mode 100644 content/docs/howto/use-the-built-in-function-matchn/en.md create mode 100644 content/docs/howto/use-the-built-in-function-matchn/gen_cache.cue create mode 100644 content/docs/howto/use-the-built-in-function-matchn/page.cue create mode 100644 hugo/content/en/docs/howto/use-the-built-in-function-matchn/index.md diff --git a/content/docs/howto/use-the-built-in-function-matchn/en.md b/content/docs/howto/use-the-built-in-function-matchn/en.md new file mode 100644 index 000000000..446fa5841 --- /dev/null +++ b/content/docs/howto/use-the-built-in-function-matchn/en.md @@ -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}}} + +{{}} +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. +{{}} + +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**: + + +{{{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 {{}}. diff --git a/content/docs/howto/use-the-built-in-function-matchn/gen_cache.cue b/content/docs/howto/use-the-built-in-function-matchn/gen_cache.cue new file mode 100644 index 000000000..c3057ec7e --- /dev/null +++ b/content/docs/howto/use-the-built-in-function-matchn/gen_cache.cue @@ -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 + + """ + }] + } + } + } + } + } + } + } +} diff --git a/content/docs/howto/use-the-built-in-function-matchn/page.cue b/content/docs/howto/use-the-built-in-function-matchn/page.cue new file mode 100644 index 000000000..d1b59cb94 --- /dev/null +++ b/content/docs/howto/use-the-built-in-function-matchn/page.cue @@ -0,0 +1,3 @@ +package site + +content: docs: howto: "use-the-built-in-function-matchn": page: _ diff --git a/content/docs/introduction/installation/en.md b/content/docs/introduction/installation/en.md index 83f2e782b..2528ebb4c 100644 --- a/content/docs/introduction/installation/en.md +++ b/content/docs/introduction/installation/en.md @@ -31,7 +31,7 @@ that's appropriate for your operating system. On Linux, Microsoft Windows, and macOS, the `cue` command can be downloaded from the [official CUE releases](https://github.com/cue-lang/cue/releases/). -These releases include *prereleases*, which are cutting-edge versions of `cue` +These releases include *pre-releases*, which are cutting-edge versions of `cue` made available to help expose bugs and flush out unintended behaviours. #### Install using Homebrew @@ -64,16 +64,32 @@ docker pull cuelang/cue:latest On [platforms supported by Go](https://go.dev/dl/#stable), -`cue` can be installed from source using any of its released versions. +`cue` can be installed from source using any of its +release, pre-release, or as-yet-unreleased versions. +Installing from source requires that you already have +[Go](https://go.dev) +installed and available. -For example, to fetch the latest version: +For example, to fetch the `latest` version: -{{{with script "en" "go install"}}} +{{{with script "en" "go install cmd/cue@latest"}}} #ellipsis 0 go install cuelang.org/go/cmd/cue@latest {{{end}}} -This method requires that you already have [Go](https://go.dev) installed. +[This page](https://pkg.go.dev/cuelang.org/go?tab=versions) +lists the installable releases and pre-releases that you can specify instead of +`latest`. + +You can also install the tip version of `cue` by specifying `master`: + +{{{with script "en" "go install cmd/cue@master"}}} +#ellipsis 0 +go install cuelang.org/go/cmd/cue@master +{{{end}}} + +The capabilities of the tip version change frequently because it contains the +most recent, unreleased code. #### Install on Arch Linux diff --git a/content/docs/introduction/installation/gen_cache.cue b/content/docs/introduction/installation/gen_cache.cue index d981e66bf..0f4660a91 100644 --- a/content/docs/introduction/installation/gen_cache.cue +++ b/content/docs/introduction/installation/gen_cache.cue @@ -7,8 +7,8 @@ package site page: { cache: { multi_step: { - hash: "4U197E1I2R39EKP4IHSO167TRT6FC6JHP4SM61VDA1G7SGJS21L0====" - scriptHash: "D37M5I4CC9UQ54DHIBSQ5TTE5SQT8FQRIFIR5UV0EUS1PEIFG7QG====" + hash: "19N1V8ITQ91S79GFHPOHH0JFFSHP4QHTG9AQ12KP18S4QULGVJJ0====" + scriptHash: "T22RVEVNCSL1EBGJC1VB4CUC6NORBUOCAN65A6USMPUL369LHPLG====" steps: [{ doc: "" cmd: "export GOMODCACHE=/caches/gomodcache" @@ -28,6 +28,14 @@ package site """ }, { + doc: "#ellipsis 0" + cmd: "go install cuelang.org/go/cmd/cue@master" + exitCode: 0 + output: """ + ... + + """ + }, { doc: "" cmd: "go mod init go.example" exitCode: 0 diff --git a/hugo/content/en/docs/howto/use-the-built-in-function-matchn/index.md b/hugo/content/en/docs/howto/use-the-built-in-function-matchn/index.md new file mode 100644 index 000000000..48df154b9 --- /dev/null +++ b/hugo/content/en/docs/howto/use-the-built-in-function-matchn/index.md @@ -0,0 +1,295 @@ +--- +title: Using the built-in function "matchN" as a field validator +tags: [commented cue] +authors: [jpluscplusm] +toc_hide: true +--- + +{{}} +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. +{{}} + +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: + +```text { title="TERMINAL" type="terminal" codeToCopy="Y3VlIHZlcnNpb24=" } +$ cue version +cue version v0.11.0-0.dev.0.20240904084811-bb24c7ce1f04 +... +``` + +## 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**: + + +{{< code-tabs >}} +{{< code-tab name="basic.cue" language="cue" area="top-left" >}} +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]) +{{< /code-tab >}}{{< /code-tabs >}} +```text { title="TERMINAL" type="terminal" codeToCopy="Y3VlIHZldCAuOmJhc2lj" } +$ cue vet .:basic +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 +``` + +## "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**: + +{{< code-tabs >}} +{{< code-tab name="one-of.cue" language="cue" area="top-left" >}} +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]) +{{< /code-tab >}}{{< /code-tabs >}} +```text { title="TERMINAL" type="terminal" codeToCopy="Y3VlIHZldCAuOm9uZU9m" } +$ cue vet .:oneOf +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 +``` + +## "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: + +{{< code-tabs >}} +{{< code-tab name="any-of.cue" language="cue" area="top-left" >}} +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]) +{{< /code-tab >}}{{< /code-tabs >}} +```text { title="TERMINAL" type="terminal" codeToCopy="Y3VlIHZldCAuOmFueU9m" } +$ cue vet .:anyOf +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 +``` + +## "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: + +{{< code-tabs >}} +{{< code-tab name="all-of.cue" language="cue" area="top-left" >}} +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)]) +{{< /code-tab >}}{{< /code-tabs >}} +```text { title="TERMINAL" type="terminal" codeToCopy="Y3VlIHZldCAuOmFsbE9m" } +$ cue vet .:allOf +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 +``` + +## "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**: + +{{< code-tabs >}} +{{< code-tab name="not.cue" language="cue" area="top-left" >}} +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)]) +{{< /code-tab >}}{{< /code-tabs >}} +```text { title="TERMINAL" type="terminal" codeToCopy="Y3VlIHZldCAuOm5vdA==" } +$ cue vet .:not +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 +``` + +## More complex uses + +### References + +Either argument to `matchN()` can be resolved through a reference: + +{{< code-tabs >}} +{{< code-tab name="all-but-one.cue" language="cue" area="top-left" >}} +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] +{{< /code-tab >}}{{< /code-tabs >}} +```text { title="TERMINAL" type="terminal" codeToCopy="Y3VlIHZldCAuOmFsbEJ1dE9uZQ==" } +$ cue vet .:allButOne +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 +``` + +### Composite data structures + +The `matchN()` function can validate composite data structures, not just +primitive values. Use it with both structs and lists: + +{{< code-tabs >}} +{{< code-tab name="composite.cue" language="cue" area="top-left" >}} +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] +{{< /code-tab >}}{{< /code-tabs >}} +```text { title="TERMINAL" type="terminal" codeToCopy="Y3VlIHZldCAuOmNvbXBvc2l0ZQ==" } +$ cue vet .:composite +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 +``` + +The sub-optimal error reporting for field `E` is tracked in {{}}. diff --git a/hugo/content/en/docs/introduction/installation/index.md b/hugo/content/en/docs/introduction/installation/index.md index a14346548..7489ea3d1 100644 --- a/hugo/content/en/docs/introduction/installation/index.md +++ b/hugo/content/en/docs/introduction/installation/index.md @@ -26,7 +26,7 @@ that's appropriate for your operating system. On Linux, Microsoft Windows, and macOS, the `cue` command can be downloaded from the [official CUE releases](https://github.com/cue-lang/cue/releases/). -These releases include *prereleases*, which are cutting-edge versions of `cue` +These releases include *pre-releases*, which are cutting-edge versions of `cue` made available to help expose bugs and flush out unintended behaviours. #### Install using Homebrew @@ -57,16 +57,32 @@ $ docker pull cuelang/cue:latest On [platforms supported by Go](https://go.dev/dl/#stable), -`cue` can be installed from source using any of its released versions. +`cue` can be installed from source using any of its +release, pre-release, or as-yet-unreleased versions. +Installing from source requires that you already have +[Go](https://go.dev) +installed and available. -For example, to fetch the latest version: +For example, to fetch the `latest` version: ```text { title="TERMINAL" type="terminal" codeToCopy="Z28gaW5zdGFsbCBjdWVsYW5nLm9yZy9nby9jbWQvY3VlQGxhdGVzdA==" } $ go install cuelang.org/go/cmd/cue@latest ... ``` -This method requires that you already have [Go](https://go.dev) installed. +[This page](https://pkg.go.dev/cuelang.org/go?tab=versions) +lists the installable releases and pre-releases that you can specify instead of +`latest`. + +You can also install the tip version of `cue` by specifying `master`: + +```text { title="TERMINAL" type="terminal" codeToCopy="Z28gaW5zdGFsbCBjdWVsYW5nLm9yZy9nby9jbWQvY3VlQG1hc3Rlcg==" } +$ go install cuelang.org/go/cmd/cue@master +... +``` + +The capabilities of the tip version change frequently because it contains the +most recent, unreleased code. #### Install on Arch Linux