encoding/jsonschema: introduce extra pass for metadata

Prepares for Issue #378

Change-Id: I6f5b315ea15ae1871fe2079a7e7f900c9468be11
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6141
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/jsonschema/constraints.go b/encoding/jsonschema/constraints.go
index 08822a9..ffac830 100644
--- a/encoding/jsonschema/constraints.go
+++ b/encoding/jsonschema/constraints.go
@@ -49,8 +49,8 @@
 	return &constraint{key: name, fn: f}
 }
 
-func p0d(name string, draft int, f constraintFunc) *constraint {
-	return &constraint{key: name, draft: draft, fn: f}
+func p1d(name string, draft int, f constraintFunc) *constraint {
+	return &constraint{key: name, phase: 1, draft: draft, fn: f}
 }
 
 func p1(name string, f constraintFunc) *constraint {
@@ -61,6 +61,10 @@
 	return &constraint{key: name, phase: 2, fn: f}
 }
 
+func p3(name string, f constraintFunc) *constraint {
+	return &constraint{key: name, phase: 3, fn: f}
+}
+
 // TODO:
 // writeOnly, readOnly
 
@@ -119,7 +123,7 @@
 
 	// Generic constraint
 
-	p0("type", func(n cue.Value, s *state) {
+	p1("type", func(n cue.Value, s *state) {
 		var types cue.Kind
 		set := func(n cue.Value) {
 			str, ok := s.strValue(n)
@@ -162,7 +166,7 @@
 		s.allowedTypes &= types
 	}),
 
-	p0("enum", func(n cue.Value, s *state) {
+	p1("enum", func(n cue.Value, s *state) {
 		var a []ast.Expr
 		for _, x := range s.listItems("enum", n, true) {
 			a = append(a, s.value(x))
@@ -171,11 +175,11 @@
 		s.typeOptional = true
 	}),
 
-	p0d("const", 6, func(n cue.Value, s *state) {
+	p1d("const", 6, func(n cue.Value, s *state) {
 		s.addConjunct(s.value(n))
 	}),
 
-	p0("default", func(n cue.Value, s *state) {
+	p1("default", func(n cue.Value, s *state) {
 		allowed, used := s.allowedTypes, s.usedTypes
 		s.default_ = s.value(n)
 		s.allowedTypes, s.usedTypes = allowed, used
@@ -184,13 +188,13 @@
 		s.examples = append(s.examples, s.default_)
 	}),
 
-	p0("deprecated", func(n cue.Value, s *state) {
+	p1("deprecated", func(n cue.Value, s *state) {
 		if s.boolValue(n) {
 			s.deprecated = true
 		}
 	}),
 
-	p0("examples", func(n cue.Value, s *state) {
+	p1("examples", func(n cue.Value, s *state) {
 		if n.Kind() != cue.ListKind {
 			s.errf(n, `value of "examples" must be an array, found %v`, n.Kind)
 		}
@@ -202,20 +206,20 @@
 		// }
 	}),
 
-	p0("description", func(n cue.Value, s *state) {
+	p1("description", func(n cue.Value, s *state) {
 		s.description, _ = s.strValue(n)
 	}),
 
-	p0("title", func(n cue.Value, s *state) {
+	p1("title", func(n cue.Value, s *state) {
 		s.title, _ = s.strValue(n)
 	}),
 
-	p0d("$comment", 7, func(n cue.Value, s *state) {
+	p1d("$comment", 7, func(n cue.Value, s *state) {
 	}),
 
-	p0("$def", addDefinitions),
-	p0("definitions", addDefinitions),
-	p0("$ref", func(n cue.Value, s *state) {
+	p1("$defs", addDefinitions),
+	p1("definitions", addDefinitions),
+	p1("$ref", func(n cue.Value, s *state) {
 		s.usedTypes = allTypes
 		str, _ := s.strValue(n)
 		refs := s.parseRef(n.Pos(), str)
@@ -263,7 +267,7 @@
 	// This is not necessary if the values are mutually exclusive/ have a
 	// discriminator.
 
-	p1("allOf", func(n cue.Value, s *state) {
+	p2("allOf", func(n cue.Value, s *state) {
 		var a []ast.Expr
 		for _, v := range s.listItems("allOf", n, false) {
 			x, sub := s.schemaState(v, s.allowedTypes, true)
@@ -278,7 +282,7 @@
 		}
 	}),
 
-	p1("anyOf", func(n cue.Value, s *state) {
+	p2("anyOf", func(n cue.Value, s *state) {
 		var types cue.Kind
 		var a []ast.Expr
 		for _, v := range s.listItems("anyOf", n, false) {
@@ -294,7 +298,7 @@
 		}
 	}),
 
-	p1("oneOf", func(n cue.Value, s *state) {
+	p2("oneOf", func(n cue.Value, s *state) {
 		var types cue.Kind
 		var a []ast.Expr
 		hasSome := false
@@ -323,12 +327,12 @@
 
 	// String constraints
 
-	p0("pattern", func(n cue.Value, s *state) {
+	p1("pattern", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.StringKind
 		s.addConjunct(&ast.UnaryExpr{Op: token.MAT, X: s.string(n)})
 	}),
 
-	p0("minLength", func(n cue.Value, s *state) {
+	p1("minLength", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.StringKind
 		min := s.number(n)
 		strings := s.addImport("strings")
@@ -336,19 +340,19 @@
 
 	}),
 
-	p0("maxLength", func(n cue.Value, s *state) {
+	p1("maxLength", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.StringKind
 		max := s.number(n)
 		strings := s.addImport("strings")
 		s.addConjunct(ast.NewCall(ast.NewSel(strings, "MaxRunes"), max))
 	}),
 
-	p0d("contentMediaType", 7, func(n cue.Value, s *state) {
+	p1d("contentMediaType", 7, func(n cue.Value, s *state) {
 		// TODO: only mark as used if it generates something.
 		// s.usedTypes |= cue.StringKind
 	}),
 
-	p0d("contentEncoding", 7, func(n cue.Value, s *state) {
+	p1d("contentEncoding", 7, func(n cue.Value, s *state) {
 		// TODO: only mark as used if it generates something.
 		// s.usedTypes |= cue.StringKind
 		// 7bit, 8bit, binary, quoted-printable and base64.
@@ -359,29 +363,29 @@
 
 	// Number constraints
 
-	p0("minimum", func(n cue.Value, s *state) {
+	p1("minimum", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.NumberKind
 		s.addConjunct(&ast.UnaryExpr{Op: token.GEQ, X: s.number(n)})
 	}),
 
-	p0("exclusiveMinimum", func(n cue.Value, s *state) {
+	p1("exclusiveMinimum", func(n cue.Value, s *state) {
 		// TODO: should we support Draft 4 booleans?
 		s.usedTypes |= cue.NumberKind
 		s.addConjunct(&ast.UnaryExpr{Op: token.GTR, X: s.number(n)})
 	}),
 
-	p0("maximum", func(n cue.Value, s *state) {
+	p1("maximum", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.NumberKind
 		s.addConjunct(&ast.UnaryExpr{Op: token.LEQ, X: s.number(n)})
 	}),
 
-	p0("exclusiveMaximum", func(n cue.Value, s *state) {
+	p1("exclusiveMaximum", func(n cue.Value, s *state) {
 		// TODO: should we support Draft 4 booleans?
 		s.usedTypes |= cue.NumberKind
 		s.addConjunct(&ast.UnaryExpr{Op: token.LSS, X: s.number(n)})
 	}),
 
-	p0("multipleOf", func(n cue.Value, s *state) {
+	p1("multipleOf", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.NumberKind
 		multiple := s.number(n)
 		var x big.Int
@@ -395,7 +399,7 @@
 
 	// Object constraints
 
-	p0("properties", func(n cue.Value, s *state) {
+	p1("properties", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.StructKind
 
 		if s.obj == nil {
@@ -429,7 +433,7 @@
 		})
 	}),
 
-	p1("required", func(n cue.Value, s *state) {
+	p2("required", func(n cue.Value, s *state) {
 		if n.Kind() != cue.ListKind {
 			s.errf(n, `value of "required" must be list of strings, found %v`, n.Kind)
 			return
@@ -475,7 +479,7 @@
 		}
 	}),
 
-	p0d("propertyNames", 6, func(n cue.Value, s *state) {
+	p1d("propertyNames", 6, func(n cue.Value, s *state) {
 		// [=~pattern]: _
 		if names, _ := s.schemaState(n, cue.StringKind, false); !isAny(names) {
 			s.usedTypes |= cue.StructKind
@@ -491,14 +495,14 @@
 	// 	s.addConjunct(ast.NewCall(ast.NewSel(pkg, "MinFields"), s.uint(n)))
 	// }),
 
-	p0("maxProperties", func(n cue.Value, s *state) {
+	p1("maxProperties", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.StructKind
 
 		pkg := s.addImport("struct")
 		s.addConjunct(ast.NewCall(ast.NewSel(pkg, "MaxFields"), s.uint(n)))
 	}),
 
-	p0("dependencies", func(n cue.Value, s *state) {
+	p1("dependencies", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.StructKind
 
 		// Schema and property dependencies.
@@ -513,7 +517,7 @@
 		*/
 	}),
 
-	p1("patternProperties", func(n cue.Value, s *state) {
+	p2("patternProperties", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.StructKind
 		if n.Kind() != cue.StructKind {
 			s.errf(n, `value of "patternProperties" must be an an object, found %v`, n.Kind)
@@ -537,7 +541,7 @@
 		})
 	}),
 
-	p2("additionalProperties", func(n cue.Value, s *state) {
+	p3("additionalProperties", func(n cue.Value, s *state) {
 		switch n.Kind() {
 		case cue.BoolKind:
 			s.closeStruct = !s.boolValue(n)
@@ -574,7 +578,7 @@
 
 	// Array constraints.
 
-	p0("items", func(n cue.Value, s *state) {
+	p1("items", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.ListKind
 		switch n.Kind() {
 		case cue.StructKind:
@@ -597,7 +601,7 @@
 		}
 	}),
 
-	p0("additionalItems", func(n cue.Value, s *state) {
+	p1("additionalItems", func(n cue.Value, s *state) {
 		switch n.Kind() {
 		case cue.BoolKind:
 			// TODO: support
@@ -614,7 +618,7 @@
 		}
 	}),
 
-	p0("contains", func(n cue.Value, s *state) {
+	p1("contains", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.ListKind
 		list := s.addImport("list")
 		// TODO: Passing non-concrete values is not yet supported in CUE.
@@ -625,7 +629,7 @@
 
 	// TODO: min/maxContains
 
-	p0("minItems", func(n cue.Value, s *state) {
+	p1("minItems", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.ListKind
 		a := []ast.Expr{}
 		p, err := n.Uint64()
@@ -642,13 +646,13 @@
 		// s.addConjunct(ast.NewCall(ast.NewSel(list, "MinItems"), clearPos(s.uint(n))))
 	}),
 
-	p0("maxItems", func(n cue.Value, s *state) {
+	p1("maxItems", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.ListKind
 		list := s.addImport("list")
 		s.addConjunct(ast.NewCall(ast.NewSel(list, "MaxItems"), clearPos(s.uint(n))))
 	}),
 
-	p0("uniqueItems", func(n cue.Value, s *state) {
+	p1("uniqueItems", func(n cue.Value, s *state) {
 		s.usedTypes |= cue.ListKind
 		if s.boolValue(n) {
 			list := s.addImport("list")
diff --git a/encoding/jsonschema/decode.go b/encoding/jsonschema/decode.go
index 1e62b7a..a4cd51a 100644
--- a/encoding/jsonschema/decode.go
+++ b/encoding/jsonschema/decode.go
@@ -395,7 +395,7 @@
 	}
 
 	// do multiple passes over the constraints to ensure they are done in order.
-	for pass := 0; pass < 3; pass++ {
+	for pass := 0; pass < 4; pass++ {
 		state.processMap(n, func(key string, value cue.Value) {
 			// Convert each constraint into a either a value or a functor.
 			c := constraintMap[key]