this repo has no description
at master 173 lines 4.8 kB view raw
1// Copyright 2019 CUE Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package jsonschema 16 17import ( 18 "strconv" 19 20 "cuelang.org/go/cue" 21 "cuelang.org/go/cue/ast" 22 "cuelang.org/go/cue/token" 23) 24 25// Array constraints 26 27func constraintAdditionalItems(key string, n cue.Value, s *state) { 28 var elem ast.Expr 29 switch n.Kind() { 30 case cue.BoolKind: 31 // Boolean values are supported even in earlier 32 // versions that did not support boolean schemas otherwise. 33 elem = boolSchema(s.boolValue(n)) 34 case cue.StructKind: 35 elem = s.schema(n) 36 default: 37 s.errf(n, `value of "additionalItems" must be an object or boolean`) 38 } 39 if s.list == nil || !s.listItemsIsArray { 40 // If there's no "items" keyword or its value is not an array "additionalItems" doesn't apply. 41 return 42 } 43 if len(s.list.Elts) == 0 { 44 // Should never happen because "items" always adds an ellipsis 45 panic("no elements in list") 46 } 47 last := s.list.Elts[len(s.list.Elts)-1].(*ast.Ellipsis) 48 if isErrorCall(elem) { 49 // No additional elements allowed. Remove the ellipsis. 50 s.list.Elts = s.list.Elts[:len(s.list.Elts)-1] 51 return 52 } 53 if isTop(elem) { 54 // Nothing to do: there's already an ellipsis in place that 55 // allows anything. 56 return 57 } 58 last.Type = elem 59} 60 61func constraintMinContains(key string, n cue.Value, s *state) { 62 p, err := uint64Value(n) 63 if err != nil { 64 s.errf(n, `value of "minContains" must be a non-negative integer value`) 65 return 66 } 67 s.minContains = &p 68} 69 70func constraintMaxContains(key string, n cue.Value, s *state) { 71 p, err := uint64Value(n) 72 if err != nil { 73 s.errf(n, `value of "maxContains" must be a non-negative integer value`) 74 return 75 } 76 s.maxContains = &p 77} 78 79func constraintContains(key string, n cue.Value, s *state) { 80 list := s.addImport(n, "list") 81 x := s.schema(n) 82 83 var min uint64 = 1 84 if s.minContains != nil { 85 min = *s.minContains 86 } 87 var c ast.Expr = &ast.UnaryExpr{ 88 Op: token.GEQ, 89 X: ast.NewLit(token.INT, strconv.FormatUint(min, 10)), 90 } 91 92 if s.maxContains != nil { 93 c = ast.NewBinExpr(token.AND, c, &ast.UnaryExpr{ 94 Op: token.LEQ, 95 X: ast.NewLit(token.INT, strconv.FormatUint(*s.maxContains, 10)), 96 }) 97 } 98 99 x = ast.NewCall(ast.NewSel(list, "MatchN"), c, clearPos(x)) 100 s.add(n, arrayType, x) 101} 102 103func constraintItems(key string, n cue.Value, s *state) { 104 switch n.Kind() { 105 case cue.StructKind, cue.BoolKind: 106 elem := s.schema(n) 107 ast.SetRelPos(elem, token.NoRelPos) 108 s.add(n, arrayType, ast.NewList(&ast.Ellipsis{Type: elem})) 109 s.hasItems = true 110 111 case cue.ListKind: 112 if !s.schemaVersion.is(vto(VersionDraft2019_09)) { 113 // The list form is only supported up to 2019-09 114 s.errf(n, `from version %v onwards, the value of "items" must be an object or a boolean`, VersionDraft2020_12) 115 return 116 } 117 s.listItemsIsArray = true 118 constraintPrefixItems(key, n, s) 119 } 120} 121 122func constraintPrefixItems(key string, n cue.Value, s *state) { 123 if n.Kind() != cue.ListKind { 124 s.errf(n, `value of "prefixItems" must be an array`) 125 } 126 var a []ast.Expr 127 for _, n := range s.listItems(key, n, true) { 128 v := s.schema(n) // TODO: label with number literal. 129 ast.SetRelPos(v, token.NoRelPos) 130 a = append(a, v) 131 } 132 s.list = ast.NewList(a...) 133 s.list.Elts = append(s.list.Elts, &ast.Ellipsis{}) 134 s.add(n, arrayType, s.list) 135} 136 137func constraintMaxItems(key string, n cue.Value, s *state) { 138 list := s.addImport(n, "list") 139 x := ast.NewCall(ast.NewSel(list, "MaxItems"), clearPos(s.uint(n))) 140 s.add(n, arrayType, x) 141} 142 143func constraintMinItems(key string, n cue.Value, s *state) { 144 a := []ast.Expr{} 145 p, err := uint64Value(n) 146 if err != nil { 147 s.errf(n, "invalid uint") 148 } 149 for ; p > 0; p-- { 150 a = append(a, top()) 151 } 152 s.add(n, arrayType, ast.NewList(append(a, &ast.Ellipsis{})...)) 153 154 // TODO: use this once constraint resolution is properly implemented. 155 // list := s.addImport(n, "list") 156 // s.addConjunct(n, ast.NewCall(ast.NewSel(list, "MinItems"), clearPos(s.uint(n)))) 157} 158 159func constraintUniqueItems(key string, n cue.Value, s *state) { 160 if s.boolValue(n) { 161 if s.schemaVersion.is(k8s) { 162 s.errf(n, "cannot set uniqueItems to true in a Kubernetes schema") 163 return 164 } 165 list := s.addImport(n, "list") 166 s.add(n, arrayType, ast.NewCall(ast.NewSel(list, "UniqueItems"))) 167 } 168} 169 170func clearPos(e ast.Expr) ast.Expr { 171 ast.SetRelPos(e, token.NoRelPos) 172 return e 173}