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  }