encoding/openapi: make API backwards compatible for Istio
This allows a smooth transition for Istio to move from
the old to new API.
Change-Id: If5f6a95a6a3c685a844757fd602974ec39a8969e
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5320
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/openapi/openapi.go b/encoding/openapi/openapi.go
index 49f33fd..4f4a92f 100644
--- a/encoding/openapi/openapi.go
+++ b/encoding/openapi/openapi.go
@@ -107,7 +107,20 @@
return (*OrderedMap)(top), err
}
-func (c *Config) compose(schemas *ast.StructLit) (*ast.StructLit, error) {
+func toCUE(name string, x interface{}) (v ast.Expr, err error) {
+ b, err := json.Marshal(x)
+ if err == nil {
+ v, err = cuejson.Extract(name, b)
+ }
+ if err != nil {
+ return nil, errors.Wrapf(err, token.NoPos,
+ "openapi: could not encode %s", name)
+ }
+ return v, nil
+
+}
+
+func (c *Config) compose(schemas *ast.StructLit) (x *ast.StructLit, err error) {
// Support of OrderedMap is mostly for backwards compatibility.
var info ast.Expr
switch x := c.Info.(type) {
@@ -120,13 +133,9 @@
case OrderedMap:
info = (*ast.StructLit)(&x)
default:
- b, err := json.Marshal(x)
- if err == nil {
- info, err = cuejson.Extract("info", b)
- }
+ info, err = toCUE("info section", x)
if err != nil {
- return nil, errors.Wrapf(err, token.NoPos,
- "openapi: could not encode info section")
+ return nil, err
}
}
diff --git a/encoding/openapi/orderedmap.go b/encoding/openapi/orderedmap.go
index a0313ac..9f8bdd0 100644
--- a/encoding/openapi/orderedmap.go
+++ b/encoding/openapi/orderedmap.go
@@ -15,7 +15,11 @@
package openapi
import (
+ "fmt"
+
"cuelang.org/go/cue/ast"
+ "cuelang.org/go/cue/literal"
+ "cuelang.org/go/cue/token"
"cuelang.org/go/internal/encoding/json"
)
@@ -33,6 +37,54 @@
Value interface{}
}
+// TODO: these functions are here to support backwards compatibility with Istio.
+// At some point, once this is removed from Istio, this can be removed.
+
+func fromLegacy(x interface{}) ast.Expr {
+ switch x := x.(type) {
+ case *OrderedMap:
+ return (*ast.StructLit)(x)
+ case []*OrderedMap:
+ a := make([]ast.Expr, len(x))
+ for i, v := range x {
+ a[i] = fromLegacy(v)
+ }
+ return ast.NewList(a...)
+ case string:
+ return ast.NewString(x)
+ case ast.Expr:
+ return x
+ default:
+ panic(fmt.Sprintf("unsupported type %T", x))
+ }
+}
+
+func toLegacy(x ast.Expr) interface{} {
+ switch x := x.(type) {
+ case *ast.StructLit:
+ return (*OrderedMap)(x)
+ case *ast.ListLit:
+ a := make([]*OrderedMap, len(x.Elts))
+ for i, v := range x.Elts {
+ e, ok := v.(*ast.StructLit)
+ if !ok {
+ return x
+ }
+ a[i] = (*OrderedMap)(e)
+ }
+ return a
+ case *ast.BasicLit:
+ if x.Kind == token.STRING {
+ str, err := literal.Unquote(x.Value)
+ if err != nil {
+ return x
+ }
+ return str
+ }
+ }
+ return x
+}
+
func (m *OrderedMap) len() int {
return len(m.Elts)
}
@@ -42,7 +94,7 @@
kvs := make([]KeyValue, len(m.Elts))
for i, e := range m.Elts {
kvs[i].Key = label(e)
- kvs[i].Value = e.(*ast.Field).Value
+ kvs[i].Value = toLegacy(e.(*ast.Field).Value)
}
return kvs
}
@@ -63,8 +115,27 @@
// Set sets a key value pair. If a pair with the same key already existed, it
// will be replaced with the new value. Otherwise, the new value is added to
-// the end.
-func (m *OrderedMap) Set(key string, expr ast.Expr) {
+// the end. The value must be of type string, ast.Expr, or *OrderedMap.
+//
+// Deprecated: use cuelang.org/go/cue/ast to manipulate ASTs.
+func (m *OrderedMap) Set(key string, x interface{}) {
+ switch x := x.(type) {
+ case *OrderedMap:
+ m.setExpr(key, (*ast.StructLit)(x))
+ case string:
+ m.setExpr(key, ast.NewString(x))
+ case ast.Expr:
+ m.setExpr(key, x)
+ default:
+ v, err := toCUE("Set", x)
+ if err != nil {
+ panic(err)
+ }
+ m.setExpr(key, v)
+ }
+}
+
+func (m *OrderedMap) setExpr(key string, expr ast.Expr) {
if f := m.find(key); f != nil {
f.Value = expr
return
@@ -75,6 +146,19 @@
})
}
+// SetAll replaces existing key-value pairs with the given ones. The keys must
+// be unique.
+func (m *OrderedMap) SetAll(kvs []KeyValue) {
+ var a []ast.Decl
+ for _, kv := range kvs {
+ a = append(a, &ast.Field{
+ Label: ast.NewString(kv.Key),
+ Value: fromLegacy(kv.Value),
+ })
+ }
+ m.Elts = a
+}
+
// exists reports whether a key-value pair exists for the given key.
func (m *OrderedMap) exists(key string) bool {
return m.find(key) != nil