blob: 3a823621f2c18dee1cf1fcd04b9b5fd13ad0f8c2 [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"
19
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020020 "github.com/cockroachdb/apd/v2"
21
22 "cuelang.org/go/cue"
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010023 "cuelang.org/go/cue/ast"
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020024 "cuelang.org/go/cue/format"
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
29// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#format
30var cueToOpenAPI = map[string]string{
31 "int32": "int32",
32 "int64": "int64",
33
34 "float64": "double",
35 "float32": "float",
36
37 "string": "string",
38 "bytes": "binary",
39
Marcel van Lohuizen6bf36972019-08-06 20:15:58 +020040 "time.Time": "dateTime",
41 "time.Time ()": "dateTime",
42 `time.Format ("2006-01-02")`: "date",
43
44 // TODO: if a format is more strict (e.g. using zeros instead of nines
45 // for fractional seconds), we could still use this as an approximation.
46 `time.Format ("2006-01-02T15:04:05.999999999Z07:00")`: "dateTime",
47
48 // TODO: password.
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020049}
50
51func extractFormat(v cue.Value) string {
52 switch k := v.IncompleteKind(); {
53 case k&cue.NumberKind != 0, k&cue.StringKind != 0, k&cue.BytesKind != 0:
54 default:
55 return ""
56 }
Marcel van Lohuizenf4649b22020-02-28 16:50:45 +010057 b, err := format.Node(v.Syntax(cue.Final()))
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020058 if err != nil {
59 return ""
60 }
Marcel van Lohuizen6bf36972019-08-06 20:15:58 +020061 if s, ok := cueToOpenAPI[string(b)]; ok {
62 return s
63 }
64 s := fmt.Sprint(v)
65 return cueToOpenAPI[s]
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020066}
67
Jason Wang2b56efa2019-08-21 13:35:53 -070068func getDeprecated(v cue.Value) bool {
69 // only looking at protobuf attribute for now.
70 a := v.Attribute("protobuf")
71 r, _ := a.Flag(1, "deprecated")
72 return r
73}
74
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010075func simplify(b *builder, t *ast.StructLit) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020076 if b.format == "" {
77 return
78 }
79 switch b.typ {
80 case "number", "integer":
81 simplifyNumber(t, b.format)
82 }
83}
84
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010085func simplifyNumber(t *ast.StructLit, format string) string {
86 fields := t.Elts
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020087 k := 0
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010088 for i, d := range fields {
89 switch label(d) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020090 case "minimum":
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010091 if decimalEqual(minMap[format], value(d)) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020092 continue
93 }
94 case "maximum":
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010095 if decimalEqual(maxMap[format], value(d)) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +020096 continue
97 }
98 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +010099 fields[k] = fields[i]
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200100 k++
101 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100102 t.Elts = fields[:k]
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200103 return format
104}
105
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100106func decimalEqual(d *apd.Decimal, v ast.Expr) bool {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200107 if d == nil {
108 return false
109 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100110 lit, ok := v.(*ast.BasicLit)
111 if !ok || (lit.Kind != token.INT && lit.Kind != token.FLOAT) {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200112 return false
113 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100114 n := literal.NumInfo{}
115 if literal.ParseNum(lit.Value, &n) != nil {
116 return false
117 }
118 var b apd.Decimal
119 if n.Decimal(&b) != nil {
120 return false
121 }
122 return d.Cmp(&b) == 0
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200123}
124
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100125func mustDecimal(s string) *apd.Decimal {
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200126 d, _, err := apd.NewFromString(s)
127 if err != nil {
128 panic(err)
129 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100130 return d
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200131}
132
133var (
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100134 minMap = map[string]*apd.Decimal{
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200135 "int32": mustDecimal("-2147483648"),
136 "int64": mustDecimal("-9223372036854775808"),
137 "float": mustDecimal("-3.40282346638528859811704183484516925440e+38"),
138 "double": mustDecimal("-1.797693134862315708145274237317043567981e+308"),
139 }
Marcel van Lohuizenf51c8792020-03-05 17:53:29 +0100140 maxMap = map[string]*apd.Decimal{
Marcel van Lohuizena0d2a402019-06-29 14:07:41 +0200141 "int32": mustDecimal("2147483647"),
142 "int64": mustDecimal("9223372036854775807"),
143 "float": mustDecimal("+3.40282346638528859811704183484516925440e+38"),
144 "double": mustDecimal("+1.797693134862315708145274237317043567981e+308"),
145 }
146)