cuego

package
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 3, 2024 License: Apache-2.0 Imports: 7 Imported by: 6

Documentation

Overview

Package cuego allows using CUE constraints in Go programs.

CUE constraints can be used to validate Go types as well as fill out missing struct fields that are implied from the constraints and the values already defined by the struct value.

CUE constraints can be added through field tags or by associating CUE code with a Go type. The field tags method follows the usual Go pattern:

type Sum struct {
    A int `cue:"C-B" json:",omitempty"`
    B int `cue:"C-A" json:",omitempty"`
    C int `cue:"A+B" json:",omitempty"`
}

func main() {
    fmt.Println(cuego.Validate(&Sum{A: 1, B: 5, C: 6}))
}

AddConstraints allows annotating Go types with any CUE constraints.

Validating Go Values

To check whether a struct's values satisfy its constraints, call Validate:

if err := cuego.Validate(p); err != nil {
   return err
}

Validation assumes that all values are filled in correctly and will not infer values. To automatically infer values, use Complete.

Completing Go Values

Package cuego can also be used to infer undefined values from a set of CUE constraints, for instance to fill out fields in a struct. A value is considered undefined if it is a nil pointer type or if it is a zero value and there is a JSON field tag with the omitempty flag. A Complete will implicitly validate a struct.

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultContext = &Context{}

DefaultContext is the shared context used with top-level functions.

Functions

func Complete

func Complete(x interface{}) error

Complete sets previously undefined values in x that can be uniquely determined form the constraints defined on the type of x such that validation passes, or returns an error, without modifying anything, if this is not possible.

Complete does a JSON round trip. This means that data not preserved in such a round trip, such as the location name of a time.Time, is lost after a successful update.

Example (StructTag)
package main

import (
	"fmt"
	"strings"

	"cuelang.org/go/cue/errors"
	"cuelang.org/go/cuego"
)

func main() {
	type Sum struct {
		A int `cue:"C-B" json:",omitempty"`
		B int `cue:"C-A" json:",omitempty"`
		C int `cue:"A+B" json:",omitempty"`
	}

	a := Sum{A: 1, B: 5}
	err := cuego.Complete(&a)
	fmt.Printf("completed: %#v (err: %v)\n", a, err)

	a = Sum{A: 2, C: 8}
	err = cuego.Complete(&a)
	fmt.Printf("completed: %#v (err: %v)\n", a, err)

	a = Sum{A: 2, B: 3, C: 8}
	err = cuego.Complete(&a)
	fmt.Println(errMsg(err))

}

func errMsg(err error) string {
	a := []string{}
	for _, err := range errors.Errors(err) {
		a = append(a, err.Error())
	}
	s := strings.Join(a, "\n")
	if s == "" {
		return "nil"
	}
	return s
}
Output:

completed: cuego_test.Sum{A:1, B:5, C:6} (err: <nil>)
completed: cuego_test.Sum{A:2, B:6, C:8} (err: <nil>)
2 errors in empty disjunction:
conflicting values null and {A:2,B:3,C:8} (mismatched types null and struct)
A: conflicting values 5 and 2

func Constrain

func Constrain(x interface{}, constraints string) error

Constrain associates the given CUE constraints with the type of x or reports an error if the constraints are invalid or not compatible with x.

Constrain works across package boundaries and is typically called in the package defining the type. Use a Context to apply constraints locally.

Example
package main

import (
	"fmt"
	"strings"

	"cuelang.org/go/cue/errors"
	"cuelang.org/go/cuego"
)

func main() {
	type Config struct {
		Filename string
		OptFile  string `json:",omitempty"`
		MaxCount int
		MinCount int

		// TODO: show a field with time.Time
	}

	err := cuego.Constrain(&Config{}, `{
		let jsonFile = =~".json$"

		// Filename must be defined and have a .json extension
		Filename: jsonFile

		// OptFile must be undefined or be a file name with a .json extension
		OptFile?: jsonFile

		MinCount: >0 & <=MaxCount
		MaxCount: <=10_000
	}`)

	fmt.Println("error:", errMsg(err))

	fmt.Println("validate:", errMsg(cuego.Validate(&Config{
		Filename: "foo.json",
		MaxCount: 1200,
		MinCount: 39,
	})))

	fmt.Println("validate:", errMsg(cuego.Validate(&Config{
		Filename: "foo.json",
		MaxCount: 12,
		MinCount: 39,
	})))

	fmt.Println("validate:", errMsg(cuego.Validate(&Config{
		Filename: "foo.jso",
		MaxCount: 120,
		MinCount: 39,
	})))

	// TODO(errors): fix bound message (should be "does not match")

}

func errMsg(err error) string {
	a := []string{}
	for _, err := range errors.Errors(err) {
		a = append(a, err.Error())
	}
	s := strings.Join(a, "\n")
	if s == "" {
		return "nil"
	}
	return s
}
Output:

error: nil
validate: nil
validate: 2 errors in empty disjunction:
conflicting values null and {Filename:"foo.json",MaxCount:12,MinCount:39} (mismatched types null and struct)
MinCount: invalid value 39 (out of bound <=12)
validate: 2 errors in empty disjunction:
conflicting values null and {Filename:"foo.jso",MaxCount:120,MinCount:39} (mismatched types null and struct)
Filename: invalid value "foo.jso" (out of bound =~".json$")

func MustConstrain

func MustConstrain(x interface{}, constraints string)

MustConstrain is like Constrain, but panics if there is an error.

func Validate

func Validate(x interface{}) error

Validate is a wrapper for Validate called on the global context.

Types

type Context

type Context struct {
	// contains filtered or unexported fields
}

A Context holds type constraints that are only applied within a given context. Global constraints that are defined at the time a constraint is created are applied as well.

func (*Context) Complete

func (c *Context) Complete(x interface{}) error

Complete sets previously undefined values in x that can be uniquely determined form the constraints defined on the type of x such that validation passes, or returns an error, without modifying anything, if this is not possible.

A value is considered undefined if it is pointer type and is nil or if it is a field with a zero value and a json tag with the omitempty tag. Complete does a JSON round trip. This means that data not preserved in such a round trip, such as the location name of a time.Time, is lost after a successful update.

func (*Context) Constrain

func (c *Context) Constrain(x interface{}, constraints string) error

Constrain associates the given CUE constraints with the type of x or reports an error if the constraints are invalid or not compatible with x.

func (*Context) Validate

func (c *Context) Validate(x interface{}) error

Validate checks whether x validates against the registered constraints for the type of x.

Constraints for x can be defined as field tags or through the Register function.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL