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/types.go b/encoding/openapi/types.go
new file mode 100644
index 0000000..03eada8
--- /dev/null
+++ b/encoding/openapi/types.go
@@ -0,0 +1,122 @@
+// Copyright 2019 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package openapi
+
+import (
+ "github.com/cockroachdb/apd/v2"
+
+ "cuelang.org/go/cue"
+ "cuelang.org/go/cue/format"
+)
+
+// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#format
+var cueToOpenAPI = map[string]string{
+ "int32": "int32",
+ "int64": "int64",
+
+ "float64": "double",
+ "float32": "float",
+
+ "string": "string",
+ "bytes": "binary",
+
+ // TODO: date, date-time, password.
+}
+
+func extractFormat(v cue.Value) string {
+ switch k := v.IncompleteKind(); {
+ case k&cue.NumberKind != 0, k&cue.StringKind != 0, k&cue.BytesKind != 0:
+ default:
+ return ""
+ }
+ b, err := format.Node(v.Syntax())
+ if err != nil {
+ return ""
+ }
+ return cueToOpenAPI[string(b)]
+}
+
+func simplify(b *builder, t *orderedMap) {
+ if b.format == "" {
+ return
+ }
+ switch b.typ {
+ case "number", "integer":
+ simplifyNumber(t, b.format)
+ }
+}
+
+func simplifyNumber(t *orderedMap, format string) string {
+ pairs := *t
+ k := 0
+ for i, kv := range pairs {
+ switch kv.key {
+ case "minimum":
+ if decimalEqual(minMap[format], kv.value) {
+ continue
+ }
+ case "maximum":
+ if decimalEqual(maxMap[format], kv.value) {
+ continue
+ }
+ }
+ pairs[i] = pairs[k]
+ k++
+ }
+ *t = pairs[:k]
+ return format
+}
+
+func decimalEqual(d *decimal, v interface{}) bool {
+ if d == nil {
+ return false
+ }
+ b, ok := v.(*decimal)
+ if !ok {
+ return false
+ }
+ return d.Cmp(b.Decimal) == 0
+}
+
+type decimal struct {
+ *apd.Decimal
+}
+
+func (d *decimal) MarshalJSON() (b []byte, err error) {
+ return d.MarshalText()
+}
+
+func mustDecimal(s string) *decimal {
+ d, _, err := apd.NewFromString(s)
+ if err != nil {
+ panic(err)
+ }
+ return &decimal{d}
+}
+
+var (
+ minMap = map[string]*decimal{
+ "int32": mustDecimal("-2147483648"),
+ "int64": mustDecimal("-9223372036854775808"),
+ "float": mustDecimal("-3.40282346638528859811704183484516925440e+38"),
+ "double": mustDecimal("-1.797693134862315708145274237317043567981e+308"),
+ }
+ maxMap = map[string]*decimal{
+ "int32": mustDecimal("2147483647"),
+ "int64": mustDecimal("9223372036854775807"),
+ "float": mustDecimal("+3.40282346638528859811704183484516925440e+38"),
+ "double": mustDecimal("+1.797693134862315708145274237317043567981e+308"),
+ }
+)