encoding/jsonschema: add support for OpenAPI nullable

Change-Id: I5f7346e08933fc9ac1896765f99070bf0868d359
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6321
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Johan Euphrosine <proppy@google.com>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/encoding/jsonschema/constraints.go b/encoding/jsonschema/constraints.go
index 89333f6..49552a8 100644
--- a/encoding/jsonschema/constraints.go
+++ b/encoding/jsonschema/constraints.go
@@ -213,6 +213,13 @@
 		s.all.add(n, ast.NewBinExpr(token.OR, a...))
 	}),
 
+	// TODO: only allow for OpenAPI.
+	p1("nullable", func(n cue.Value, s *state) {
+		null := ast.NewNull()
+		setPos(null, n)
+		s.nullable = null
+	}),
+
 	p1d("const", 6, func(n cue.Value, s *state) {
 		s.all.add(n, s.value(n))
 	}),
diff --git a/encoding/jsonschema/decode.go b/encoding/jsonschema/decode.go
index 4be4718..de83b8c 100644
--- a/encoding/jsonschema/decode.go
+++ b/encoding/jsonschema/decode.go
@@ -320,8 +320,9 @@
 	pos cue.Value
 
 	// The constraints in types represent disjunctions per type.
-	types [numCoreTypes]constraintInfo
-	all   constraintInfo // values and oneOf etc.
+	types    [numCoreTypes]constraintInfo
+	all      constraintInfo // values and oneOf etc.
+	nullable *ast.BasicLit  // nullable
 
 	usedTypes    cue.Kind
 	allowedTypes cue.Kind
@@ -463,17 +464,26 @@
 		e = ast.NewBinExpr(token.AND, conjuncts...)
 	}
 
-	if s.default_ != nil {
+	a := []ast.Expr{e}
+	if s.nullable != nil {
+		a = []ast.Expr{s.nullable, e}
+	}
+
+outer:
+	switch {
+	case s.default_ != nil:
 		// check conditions where default can be skipped.
 		switch x := s.default_.(type) {
 		case *ast.ListLit:
 			if s.usedTypes == cue.ListKind && len(x.Elts) == 0 {
-				return e
+				break outer
 			}
 		}
-		e = ast.NewBinExpr(token.OR, e, &ast.UnaryExpr{Op: token.MUL, X: s.default_})
+		a = append(a, &ast.UnaryExpr{Op: token.MUL, X: s.default_})
 	}
 
+	e = ast.NewBinExpr(token.OR, a...)
+
 	if len(s.definitions) > 0 {
 		if st, ok := e.(*ast.StructLit); ok {
 			st.Elts = append(st.Elts, s.definitions...)
diff --git a/encoding/jsonschema/testdata/openapi.txtar b/encoding/jsonschema/testdata/openapi.txtar
index ad52ca1..6361042 100644
--- a/encoding/jsonschema/testdata/openapi.txtar
+++ b/encoding/jsonschema/testdata/openapi.txtar
@@ -13,6 +13,7 @@
           type: string
         address:
           $ref: "#/components/schemas/PhoneNumber"
+          nullable: true
     PhoneNumber:
       description: "The number to dial."
       type: string
@@ -22,7 +23,7 @@
 #User: {
 	name?:    string
 	id?:      int
-	address?: #PhoneNumber
+	address?: null | #PhoneNumber
 	...
 }