encoding/openapi: simplify based on user-defined format

For instance, don't use minimum and maximum for
int32 boundaries if the user indicated the field is an
int32.

Also move to apd instead of ints.

Issue #56

Change-Id: I472c3c2b1fd8622430595bd062cef3bbd53b62f1
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2376
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index 0107f2d..10f93ef 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"math"
+	"math/big"
 	"path"
 	"sort"
 	"strconv"
@@ -24,6 +25,7 @@
 
 	"cuelang.org/go/cue"
 	"cuelang.org/go/cue/errors"
+	"github.com/cockroachdb/apd/v2"
 )
 
 type buildContext struct {
@@ -143,6 +145,7 @@
 	defer func() { b.ctx.path = oldPath }()
 
 	c := newRootBuilder(b.ctx)
+	c.format = extractFormat(v)
 	isRef := c.value(v, nil)
 	schema := c.finish()
 
@@ -156,6 +159,9 @@
 			schema.Prepend("description", str)
 		}
 	}
+
+	simplify(c, schema)
+
 	return schema
 }
 
@@ -383,6 +389,8 @@
 		b.setType("integer", "") // may be overridden to integer
 		b.number(v)
 
+		// TODO: for JSON schema, consider adding multipleOf: 1.
+
 	case cue.BytesKind:
 		// byte		string	byte	base64 	encoded characters
 		// binary	string	binary	any 	sequence of octets
@@ -555,19 +563,19 @@
 	// setIntConstraint(t, "multipleOf", a)
 
 	case cue.LessThanOp:
-		b.set("exclusiveMaximum", b.int(a[0]))
+		b.set("exclusiveMaximum", b.big(a[0]))
 
 	case cue.LessThanEqualOp:
-		b.set("maximum", b.int(a[0]))
+		b.set("maximum", b.big(a[0]))
 
 	case cue.GreaterThanOp:
-		b.set("exclusiveMinimum", b.int(a[0]))
+		b.set("exclusiveMinimum", b.big(a[0]))
 
 	case cue.GreaterThanEqualOp:
-		b.set("minimum", b.int(a[0]))
+		b.set("minimum", b.big(a[0]))
 
 	case cue.NotEqualOp:
-		i := b.int(a[0])
+		i := b.big(a[0])
 		b.setNot("allOff", []*oaSchema{
 			b.kv("minItems", i),
 			b.kv("maxItems", i),
@@ -705,7 +713,9 @@
 func (b *builder) setType(t, format string) {
 	if b.typ == "" {
 		b.typ = t
-		b.format = format
+		if format != "" {
+			b.format = format
+		}
 	}
 }
 
@@ -816,3 +826,13 @@
 	}
 	return d
 }
+
+func (b *builder) big(v cue.Value) interface{} {
+	var mant big.Int
+	exp, err := v.MantExp(&mant)
+	if err != nil {
+		b.failf(v, "value not a number: %v", err)
+		return nil
+	}
+	return &decimal{apd.NewWithBigInt(&mant, int32(exp))}
+}