encoding/jsonschema: constraints don't imply types

This was a major bug based on the wrong assumption that
a JSON Schema constraint implies the type for that constraint.
Instead, the contraints only apply if a value is of a certain
type.

To keep diffs to a minimum, and to improve the overal output,
it now explicitly sorts literal
composit types (structs and lists) at the end of a series
of conjunctions.

Change-Id: Ice98a2bc00ae4a68170cd6cd726565a453b1187f
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6302
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/jsonschema/testdata/emptyanyof.txtar b/encoding/jsonschema/testdata/emptyanyof.txtar
new file mode 100644
index 0000000..0313e6d
--- /dev/null
+++ b/encoding/jsonschema/testdata/emptyanyof.txtar
@@ -0,0 +1,26 @@
+-- emptyanyof.json --
+{
+ "$defs": {
+   "shell": {
+      "description": "Specify a shell.",
+      "type": "string",
+      "anyOf": [
+        {
+        },
+        {
+          "enum": [
+            "bash",
+            "sh",
+            "cmd",
+            "powershell"
+          ]
+        }
+      ]
+   }
+ }
+}
+
+-- out.cue --
+_
+
+#shell: (string | ("bash" | "sh" | "cmd" | "powershell")) & string
diff --git a/encoding/jsonschema/testdata/err.txtar b/encoding/jsonschema/testdata/err.txtar
new file mode 100644
index 0000000..2175de8
--- /dev/null
+++ b/encoding/jsonschema/testdata/err.txtar
@@ -0,0 +1,20 @@
+-- type.json --
+{
+  "type": "object",
+
+  "properties": {
+    "multi": {
+        "type": [ "integer" ],
+        "minimum": 2,
+        "maximum": 3,
+        "maxLength": 5
+    }
+  },
+  "additionalProperties": false
+}
+
+-- out.err --
+constraint not allowed because type string is excluded:
+    type.json:9:22
+-- out.cue --
+multi?: int & >=2 & <=3
diff --git a/encoding/jsonschema/testdata/list.txtar b/encoding/jsonschema/testdata/list.txtar
index 3f7bd0a..c48c862 100644
--- a/encoding/jsonschema/testdata/list.txtar
+++ b/encoding/jsonschema/testdata/list.txtar
@@ -41,5 +41,5 @@
 foo?: [...string]
 tuple?: [string, int, 2]
 has?:  list.Contains(3)
-size?: [_, _, _, ...] & list.MaxItems(9) & list.UniqueItems()
+size?: list.UniqueItems() & list.MaxItems(9) & [_, _, _, ...]
 additional?: [int, int, ...string]
diff --git a/encoding/jsonschema/testdata/num.txtar b/encoding/jsonschema/testdata/num.txtar
index 1968c3a..cbbd984 100644
--- a/encoding/jsonschema/testdata/num.txtar
+++ b/encoding/jsonschema/testdata/num.txtar
@@ -13,10 +13,16 @@
         "maximum": 3
     },
     "exclusive": {
-        "type": "number",
+        "type": "integer",
         "exclusiveMinimum": 2,
         "exclusiveMaximum": 3
     },
+    "multi": {
+        "type": [ "integer", "string" ],
+        "minimum": 2,
+        "maximum": 3,
+        "maxLength": 5
+    },
     "cents": {
       "type": "number",
       "multipleOf": 0.05
@@ -26,10 +32,14 @@
 }
 
 -- out.cue --
-import "math"
+import (
+	"strings"
+	"math"
+)
 
 constant?:  2
 several?:   1 | 2 | 3 | 4
 inclusive?: >=2 & <=3
-exclusive?: >2 & <3
+exclusive?: int & >2 & <3
+multi?:     int & >=2 & <=3 | strings.MaxRunes(5)
 cents?:     math.MultipleOf(0.05)
diff --git a/encoding/jsonschema/testdata/object.txtar b/encoding/jsonschema/testdata/object.txtar
index 9f773e1..1e6644e 100644
--- a/encoding/jsonschema/testdata/object.txtar
+++ b/encoding/jsonschema/testdata/object.txtar
@@ -53,6 +53,15 @@
         "^\\P{Lo}": { "type": "integer" }
       },
       "additionalProperties": { "type": "string" }
+    },
+    "multi": {
+      "type": [ "object", "number" ],
+      "properties": {
+        "foo": { "type": "number" },
+        "bar": { "type": "number" }
+      },
+      "maxProperties": 5,
+      "minimum": 7
     }
   },
   "additionalProperties": false
@@ -68,7 +77,6 @@
 additional?: {
 	foo?: number
 	bar?: number
-
 	{[!~"^(foo|bar)$"]: string}
 }
 map?: [string]: string
@@ -94,6 +102,10 @@
 	{[=~"^\\P{Lu}" & !~"^(foo|bar)$"]: string}
 
 	{[=~"^\\P{Lo}" & !~"^(foo|bar)$"]: int}
-
 	{[!~"^\\P{Lu}" & !~"^\\P{Lo}" & !~"^(foo|bar)$"]: string}
 }
+multi?: >=7 | struct.MaxFields(5) & {
+	foo?: number
+	bar?: number
+	...
+}
diff --git a/encoding/jsonschema/testdata/refroot.txtar b/encoding/jsonschema/testdata/refroot.txtar
index c090506..6f33439 100644
--- a/encoding/jsonschema/testdata/refroot.txtar
+++ b/encoding/jsonschema/testdata/refroot.txtar
@@ -16,7 +16,7 @@
 _schema
 _schema: {
 	@jsonschema(schema="http://json-schema.org/draft-07/schema#")
-	number | null | bool | string | [...] | {
+	null | bool | number | string | [...] | {
 		@jsonschema(id="http://cuelang.org/go/encoding/openapi/testdata/order.json")
 		value?: _
 		next?:  _schema_1
diff --git a/encoding/jsonschema/testdata/refroot2.txtar b/encoding/jsonschema/testdata/refroot2.txtar
index 20e6361..e4a162c 100644
--- a/encoding/jsonschema/testdata/refroot2.txtar
+++ b/encoding/jsonschema/testdata/refroot2.txtar
@@ -14,7 +14,7 @@
 _schema
 _schema: {
 	@jsonschema(schema="http://json-schema.org/draft-07/schema#")
-	number | null | bool | string | [...] | {
+	null | bool | number | string | [...] | {
 		value?: _
 		next?:  _schema_1
 		...
diff --git a/encoding/jsonschema/testdata/typedis.txtar b/encoding/jsonschema/testdata/typedis.txtar
index 6bf10ac..d2a3aeb 100644
--- a/encoding/jsonschema/testdata/typedis.txtar
+++ b/encoding/jsonschema/testdata/typedis.txtar
@@ -34,8 +34,6 @@
         }
       ]
     },
-
-
     "empty": {
         "allOf": [
             { "type": "object" },
@@ -44,11 +42,14 @@
     }
   }
 }
+-- out.err --
+constraint not allowed because type string is excluded:
+    type.json:39:23
 -- out.cue --
 // Main schema
 intOrString1?: int | string
 intOrString2?: int | string
-intOrString3?: int | string
-disjunction?:  int | string | >=3
+intOrString3?: string | int
+disjunction?:  string | int | int & >=3
 empty?:        _|_
 ...
diff --git a/encoding/jsonschema/testdata/used.txtar b/encoding/jsonschema/testdata/used.txtar
new file mode 100644
index 0000000..b22c92f
--- /dev/null
+++ b/encoding/jsonschema/testdata/used.txtar
@@ -0,0 +1,48 @@
+-- used.json --
+{
+    "$defs": {
+        "enum": {
+            "type": "string",
+            "enum": [ "a", "b", "c" ]
+        },
+        "lists": {
+            "description": "Single item or lists of various lengths.",
+            "oneOf": [
+                {
+                    "type": "string",
+                    "enum": [ "a", "b", "c" ]
+                },
+                {
+                    "type": "array",
+                    "oneOf": [
+                        {
+                            "items": [ { "const": "X" } ]
+                        },
+                        {
+                            "items": [
+                                { "const": "X" },
+                                {
+                                    "type": "string",
+                                    "enum": [ "a", "b", "c" ]
+                                }
+                            ]
+                        },
+                        {
+                            "items": [
+                                { "const": "X" },
+                                { "enum": [ "d", "e", "f" ] }
+                            ]
+                        }
+                    ],
+                    "additionalItems": false
+                }
+            ]
+        }
+    }
+}
+-- out.cue --
+_
+
+#enum: "a" | "b" | "c"
+
+#lists: "a" | "b" | "c" | (["X"] | ["X", "a" | "b" | "c"] | ["X", "d" | "e" | "f"])