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) {}