cuelang.org/go@v0.10.1/encoding/jsonschema/constraints_generic.go (about) 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 15 package jsonschema 16 17 import ( 18 "cuelang.org/go/cue" 19 "cuelang.org/go/cue/ast" 20 "cuelang.org/go/cue/errors" 21 "cuelang.org/go/cue/token" 22 ) 23 24 // Generic constraints 25 26 func constraintAddDefinitions(key string, n cue.Value, s *state) { 27 if n.Kind() != cue.StructKind { 28 s.errf(n, `"definitions" expected an object, found %s`, n.Kind()) 29 } 30 31 old := s.isSchema 32 s.isSchema = true 33 defer func() { s.isSchema = old }() 34 35 s.processMap(n, func(key string, n cue.Value) { 36 name := key 37 38 var f *ast.Field 39 40 ident := "#" + name 41 if ast.IsValidIdent(ident) { 42 f = &ast.Field{Value: s.schema(n, label{ident, true})} 43 f.Label = ast.NewIdent(ident) 44 } else { 45 f = &ast.Field{Value: s.schema(n, label{"#", true}, label{name: name})} 46 f.Label = ast.NewString(name) 47 ident = "#" 48 f = &ast.Field{ 49 Label: ast.NewIdent("#"), 50 Value: ast.NewStruct(f), 51 } 52 } 53 54 ast.SetRelPos(f, token.NewSection) 55 s.definitions = append(s.definitions, f) 56 s.setField(label{name: ident, isDef: true}, f) 57 }) 58 } 59 60 func constraintComment(key string, n cue.Value, s *state) { 61 } 62 63 func constraintConst(key string, n cue.Value, s *state) { 64 s.all.add(n, s.value(n)) 65 s.allowedTypes &= n.Kind() 66 s.knownTypes &= n.Kind() 67 } 68 69 func constraintDefault(key string, n cue.Value, s *state) { 70 sc := *s 71 s.default_ = sc.value(n) 72 // TODO: must validate that the default is subsumed by the normal value, 73 // as CUE will otherwise broaden the accepted values with the default. 74 s.examples = append(s.examples, s.default_) 75 } 76 77 func constraintDeprecated(key string, n cue.Value, s *state) { 78 if s.boolValue(n) { 79 s.deprecated = true 80 } 81 } 82 83 func constraintDescription(key string, n cue.Value, s *state) { 84 s.description, _ = s.strValue(n) 85 } 86 87 func constraintEnum(key string, n cue.Value, s *state) { 88 var a []ast.Expr 89 var types cue.Kind 90 for _, x := range s.listItems("enum", n, true) { 91 if (s.allowedTypes & x.Kind()) == 0 { 92 // Enum value is redundant because it's 93 // not in the allowed type set. 94 continue 95 } 96 a = append(a, s.value(x)) 97 types |= x.Kind() 98 } 99 s.knownTypes &= types 100 s.allowedTypes &= types 101 if len(a) > 0 { 102 s.all.add(n, ast.NewBinExpr(token.OR, a...)) 103 } 104 } 105 106 func constraintExamples(key string, n cue.Value, s *state) { 107 if n.Kind() != cue.ListKind { 108 s.errf(n, `value of "examples" must be an array, found %v`, n.Kind()) 109 } 110 // TODO: implement examples properly. 111 // for _, n := range s.listItems("examples", n, true) { 112 // if ex := s.value(n); !isAny(ex) { 113 // s.examples = append(s.examples, ex) 114 // } 115 // } 116 } 117 118 func constraintNullable(key string, n cue.Value, s *state) { 119 // TODO: only allow for OpenAPI. 120 null := ast.NewNull() 121 setPos(null, n) 122 s.nullable = null 123 } 124 125 func constraintRef(key string, n cue.Value, s *state) { 126 u := s.resolveURI(n) 127 128 fragmentParts, err := splitFragment(u) 129 if err != nil { 130 s.addErr(errors.Newf(n.Pos(), "%v", err)) 131 return 132 } 133 expr := s.makeCUERef(n, u, fragmentParts) 134 if expr == nil { 135 expr = &ast.BadExpr{From: n.Pos()} 136 } 137 138 s.all.add(n, expr) 139 } 140 141 func constraintTitle(key string, n cue.Value, s *state) { 142 s.title, _ = s.strValue(n) 143 } 144 145 func constraintType(key string, n cue.Value, s *state) { 146 var types cue.Kind 147 set := func(n cue.Value) { 148 str, ok := s.strValue(n) 149 if !ok { 150 s.errf(n, "type value should be a string") 151 } 152 switch str { 153 case "null": 154 types |= cue.NullKind 155 s.setTypeUsed(n, nullType) 156 // TODO: handle OpenAPI restrictions. 157 case "boolean": 158 types |= cue.BoolKind 159 s.setTypeUsed(n, boolType) 160 case "string": 161 types |= cue.StringKind 162 s.setTypeUsed(n, stringType) 163 case "number": 164 types |= cue.NumberKind 165 s.setTypeUsed(n, numType) 166 case "integer": 167 types |= cue.IntKind 168 s.setTypeUsed(n, numType) 169 s.add(n, numType, ast.NewIdent("int")) 170 case "array": 171 types |= cue.ListKind 172 s.setTypeUsed(n, arrayType) 173 case "object": 174 types |= cue.StructKind 175 s.setTypeUsed(n, objectType) 176 177 default: 178 s.errf(n, "unknown type %q", n) 179 } 180 } 181 182 switch n.Kind() { 183 case cue.StringKind: 184 set(n) 185 case cue.ListKind: 186 for i, _ := n.List(); i.Next(); { 187 set(i.Value()) 188 } 189 default: 190 s.errf(n, `value of "type" must be a string or list of strings`) 191 } 192 193 s.allowedTypes &= types 194 }