blob: ccc237ae2a04758445e442e894790cb9e1b01ddf [file] [log] [blame]
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +02001// Copyright 2019 CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package openapi
16
17import (
Marcel van Lohuizen6bf36972019-08-06 20:15:58 +020018 "fmt"
Marcel van Lohuizened90d002020-03-30 17:18:12 +020019 "strings"
Marcel van Lohuizen6bf36972019-08-06 20:15:58 +020020
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020021 "github.com/cockroachdb/apd/v2"
22
Marcel van Lohuizen845df052020-07-26 13:15:45 +020023 "cuelang.org/go/cue"
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010024 "cuelang.org/go/cue/ast"
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010025 "cuelang.org/go/cue/literal"
26 "cuelang.org/go/cue/token"
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020027)
28
Malte Wrogemannecfbd722020-06-17 07:02:22 +000029// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020030var cueToOpenAPI = map[string]string{
31 "int32": "int32",
32 "int64": "int64",
33
34 "float64": "double",
35 "float32": "float",
36
Malte Wrogemannecfbd722020-06-17 07:02:22 +000037 "bytes": "binary",
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020038
Malte Wrogemannecfbd722020-06-17 07:02:22 +000039 "time.Time": "date-time",
Marcel van Lohuizen6bf36972019-08-06 20:15:58 +020040 `time.Format ("2006-01-02")`: "date",
41
42 // TODO: if a format is more strict (e.g. using zeros instead of nines
43 // for fractional seconds), we could still use this as an approximation.
Malte Wrogemannecfbd722020-06-17 07:02:22 +000044 `time.Format ("2006-01-02T15:04:05.999999999Z07:00")`: "date-time",
Marcel van Lohuizen6bf36972019-08-06 20:15:58 +020045
46 // TODO: password.
Marcel van Lohuizen05283762020-07-17 09:02:19 +020047
48 ">=-2147483648 & <=2147483647 & int": "int32",
49 ">=-9223372036854775808 & <=9223372036854775807 & int": "int64",
50 ">=-340282346638528859811704183484516925440 & <=340282346638528859811704183484516925440": "float",
51 ">=-1.797693134862315708145274237317043567981e+308 & <=1.797693134862315708145274237317043567981e+308": "double",
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020052}
53
54func extractFormat(v cue.Value) string {
55 switch k := v.IncompleteKind(); {
56 case k&cue.NumberKind != 0, k&cue.StringKind != 0, k&cue.BytesKind != 0:
57 default:
58 return ""
59 }
Marcel van Lohuizened90d002020-03-30 17:18:12 +020060 var expr, arg string
61 op, a := v.Expr()
62 if op == cue.CallOp {
63 v = a[0]
64 if len(a) == 2 {
65 arg = fmt.Sprintf(" (%s)", a[1].Eval())
66 }
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020067 }
Marcel van Lohuizened90d002020-03-30 17:18:12 +020068 if inst, ref := v.Reference(); len(ref) > 0 {
69 expr = inst.ImportPath + "." + strings.Join(ref, ".")
70 expr += arg
71 } else {
Marcel van Lohuizen05283762020-07-17 09:02:19 +020072 expr = fmt.Sprint(v.Eval())
73 expr += arg
Marcel van Lohuizened90d002020-03-30 17:18:12 +020074 }
75 if s, ok := cueToOpenAPI[expr]; ok {
Marcel van Lohuizen6bf36972019-08-06 20:15:58 +020076 return s
77 }
78 s := fmt.Sprint(v)
79 return cueToOpenAPI[s]
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020080}
81
Jason Wang2b56efa2019-08-21 13:35:53 -070082func getDeprecated(v cue.Value) bool {
83 // only looking at protobuf attribute for now.
84 a := v.Attribute("protobuf")
85 r, _ := a.Flag(1, "deprecated")
86 return r
87}
88
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010089func simplify(b *builder, t *ast.StructLit) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020090 if b.format == "" {
91 return
92 }
93 switch b.typ {
94 case "number", "integer":
95 simplifyNumber(t, b.format)
96 }
97}
98
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010099func simplifyNumber(t *ast.StructLit, format string) string {
100 fields := t.Elts
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200101 k := 0
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100102 for i, d := range fields {
103 switch label(d) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200104 case "minimum":
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100105 if decimalEqual(minMap[format], value(d)) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200106 continue
107 }
108 case "maximum":
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100109 if decimalEqual(maxMap[format], value(d)) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200110 continue
111 }
112 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100113 fields[k] = fields[i]
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200114 k++
115 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100116 t.Elts = fields[:k]
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200117 return format
118}
119
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100120func decimalEqual(d *apd.Decimal, v ast.Expr) bool {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200121 if d == nil {
122 return false
123 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100124 lit, ok := v.(*ast.BasicLit)
125 if !ok || (lit.Kind != token.INT && lit.Kind != token.FLOAT) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200126 return false
127 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100128 n := literal.NumInfo{}
129 if literal.ParseNum(lit.Value, &n) != nil {
130 return false
131 }
132 var b apd.Decimal
133 if n.Decimal(&b) != nil {
134 return false
135 }
136 return d.Cmp(&b) == 0
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200137}
138
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100139func mustDecimal(s string) *apd.Decimal {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200140 d, _, err := apd.NewFromString(s)
141 if err != nil {
142 panic(err)
143 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100144 return d
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200145}
146
147var (
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100148 minMap = map[string]*apd.Decimal{
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200149 "int32": mustDecimal("-2147483648"),
150 "int64": mustDecimal("-9223372036854775808"),
151 "float": mustDecimal("-3.40282346638528859811704183484516925440e+38"),
152 "double": mustDecimal("-1.797693134862315708145274237317043567981e+308"),
153 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100154 maxMap = map[string]*apd.Decimal{
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200155 "int32": mustDecimal("2147483647"),
156 "int64": mustDecimal("9223372036854775807"),
157 "float": mustDecimal("+3.40282346638528859811704183484516925440e+38"),
158 "double": mustDecimal("+1.797693134862315708145274237317043567981e+308"),
159 }
160)