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: 7})) }
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 ¶
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) B: conflicting values 6 and 3
func Constrain ¶
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{}, `{ 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.
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 ¶
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.