cuelang.org/go@v0.13.0/encoding/jsonschema/constraints_format.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 "sync" 19 20 "cuelang.org/go/cue" 21 "cuelang.org/go/cue/ast" 22 ) 23 24 type formatFuncInfo struct { 25 versions versionSet 26 f func(n cue.Value, s *state) 27 } 28 29 // For reference, the Kubernetes-related format strings 30 // are defined here: 31 // https://github.com/kubernetes/apiextensions-apiserver/blob/aca9073a80bee92a0b77741b9c7ad444c49fe6be/pkg/apis/apiextensions/v1beta1/types_jsonschema.go#L73 32 33 var formatFuncs = sync.OnceValue(func() map[string]formatFuncInfo { 34 return map[string]formatFuncInfo{ 35 "binary": {openAPI, formatTODO}, 36 "bsonobjectid": {k8s, formatTODO}, 37 "byte": {openAPI | k8s, formatTODO}, 38 "cidr": {k8s, formatTODO}, 39 "creditcard": {k8s, formatTODO}, 40 "data": {openAPI, formatTODO}, 41 "date": {vfrom(VersionDraft7) | openAPI | k8s, formatDate}, 42 "date-time": {allVersions | openAPI | k8s, formatDateTime}, 43 "datetime": {k8s, formatDateTime}, 44 "double": {openAPI | k8s, formatTODO}, 45 "duration": {vfrom(VersionDraft2019_09) | k8s, formatTODO}, 46 "email": {allVersions | openAPI | k8s, formatTODO}, 47 "float": {openAPI | k8s, formatTODO}, 48 "hexcolor": {k8s, formatTODO}, 49 "hostname": {allVersions | openAPI | k8s, formatTODO}, 50 "idn-email": {vfrom(VersionDraft7), formatTODO}, 51 "idn-hostname": {vfrom(VersionDraft7), formatTODO}, 52 "int32": {openAPI | k8s, formatInt32}, 53 "int64": {openAPI | k8s, formatInt64}, 54 "ipv4": {allVersions | openAPI | k8s, formatTODO}, 55 "ipv6": {allVersions | openAPI | k8s, formatTODO}, 56 "iri": {vfrom(VersionDraft7), formatURI}, 57 "iri-reference": {vfrom(VersionDraft7), formatURIReference}, 58 "isbn": {k8s, formatTODO}, 59 "isbn10": {k8s, formatTODO}, 60 "isbn13": {k8s, formatTODO}, 61 "json-pointer": {vfrom(VersionDraft6), formatTODO}, 62 "mac": {k8s, formatTODO}, 63 "password": {openAPI | k8s, formatTODO}, 64 "regex": {vfrom(VersionDraft7), formatRegex}, 65 "relative-json-pointer": {vfrom(VersionDraft7), formatTODO}, 66 "rgbcolor": {k8s, formatTODO}, 67 "ssn": {k8s, formatTODO}, 68 "time": {vfrom(VersionDraft7), formatTODO}, 69 "uint32": {k8s, formatUint32}, 70 "uint64": {k8s, formatUint64}, 71 // TODO we should probably disallow non-ASCII URIs (IRIs) but 72 // this is good enough for now. 73 "uri": {allVersions | openAPI | k8s, formatURI}, 74 "uri-reference": {vfrom(VersionDraft6), formatURIReference}, 75 "uri-template": {vfrom(VersionDraft6), formatTODO}, 76 "uuid": {vfrom(VersionDraft2019_09) | k8s, formatTODO}, 77 "uuid3": {k8s, formatTODO}, 78 "uuid4": {k8s, formatTODO}, 79 "uuid5": {k8s, formatTODO}, 80 } 81 }) 82 83 func constraintFormat(key string, n cue.Value, s *state) { 84 formatStr, ok := s.strValue(n) 85 if !ok { 86 return 87 } 88 // Note: OpenAPI 3.0 says "the format property is an open 89 // string-valued property, and can have any value" so even when 90 // StrictKeywords is true, we do not generate an error if we're 91 // using OpenAPI. TODO it would still be nice to have a mode 92 // that allows the use to find likely spelling mistakes in 93 // format values in OpenAPI. 94 finfo, ok := formatFuncs()[formatStr] 95 if !ok { 96 // TODO StrictKeywords isn't exactly right here, but in general 97 // we want unknown formats to be ignored even when StrictFeatures 98 // is enabled, and StrictKeywords is closest to what we want. 99 // Perhaps we should have a "lint" mode? 100 if s.cfg.StrictKeywords && !s.schemaVersion.is(openAPILike) { 101 s.errf(n, "unknown format %q", formatStr) 102 } 103 return 104 } 105 if !s.schemaVersion.is(finfo.versions) { 106 if s.cfg.StrictKeywords && !s.schemaVersion.is(openAPILike) { 107 s.errf(n, "format %q is not recognized in schema version %v", formatStr, s.schemaVersion) 108 } 109 return 110 } 111 finfo.f(n, s) 112 } 113 114 func formatURI(n cue.Value, s *state) { 115 s.add(n, stringType, ast.NewSel(s.addImport(n, "net"), "AbsURL")) 116 } 117 118 func formatURIReference(n cue.Value, s *state) { 119 s.add(n, stringType, ast.NewSel(s.addImport(n, "net"), "URL")) 120 } 121 122 func formatDateTime(n cue.Value, s *state) { 123 // TODO this is a bit stricter than the spec, because the spec 124 // allows lower-case "T" and "Z", and leap seconds, but 125 // it's not bad for now. 126 s.add(n, stringType, ast.NewSel(s.addImport(n, "time"), "Time")) 127 } 128 129 func formatDate(n cue.Value, s *state) { 130 // TODO it might be nice to have a dedicated `time.Date` validator rather 131 // than using `time.Format`. 132 s.add(n, stringType, ast.NewCall(ast.NewSel(s.addImport(n, "time"), "Format"), ast.NewString("2006-01-02"))) 133 } 134 135 func formatRegex(n cue.Value, s *state) { 136 // TODO this is a bit stricter than the spec, because the spec 137 // allows Perl idioms such as back-references. 138 s.add(n, stringType, ast.NewSel(s.addImport(n, "regexp"), "Valid")) 139 } 140 141 func formatInt32(n cue.Value, s *state) { 142 s.add(n, numType, ast.NewIdent("int32")) 143 } 144 145 func formatInt64(n cue.Value, s *state) { 146 s.add(n, numType, ast.NewIdent("int64")) 147 } 148 149 func formatUint32(n cue.Value, s *state) { 150 s.add(n, numType, ast.NewIdent("uint32")) 151 } 152 153 func formatUint64(n cue.Value, s *state) { 154 s.add(n, numType, ast.NewIdent("uint64")) 155 } 156 157 func formatTODO(n cue.Value, s *state) {}