cue: keep sane references for embedded disjunctions in Expr
In Expr, simulate a struct with just the fields so that embedded
disjunctions with references to such fields will direct to the unmerged
values of these references.
This also addes OpenAPI tests, which relies on this functionality.
Change-Id: I5d127fee1389a88a45f3705ad1a0c929ee1c2bb0
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9881
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
diff --git a/cue/types.go b/cue/types.go
index 32af926..7957c15 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -2383,38 +2383,34 @@
op = CallOp
case *adt.StructLit:
- // Simulate old embeddings.
- envEmbed := &adt.Environment{
- Up: env,
- Vertex: v.v,
- }
+ hasEmbed := false
fields := []adt.Decl{}
- ctx := v.ctx()
for _, d := range x.Decls {
- switch x := d.(type) {
+ switch d.(type) {
default:
fields = append(fields, d)
case adt.Value:
fields = append(fields, d)
case adt.Expr:
- // embedding
- n := &adt.Vertex{Label: v.v.Label}
- c := adt.MakeRootConjunct(envEmbed, x)
- n.AddConjunct(c)
- n.Finalize(ctx)
- n.Parent = v.v.Parent
- a = append(a, makeValue(v.idx, n, v.parent_))
+ hasEmbed = true
}
}
- if len(a) == 0 {
+
+ if !hasEmbed {
a = append(a, v)
break
}
+ ctx := v.ctx()
+
+ n := v.v
+
if len(fields) > 0 {
- n := &adt.Vertex{
- Label: v.v.Label,
+ n = &adt.Vertex{
+ Parent: v.v.Parent,
+ Label: v.v.Label,
}
+
s := &adt.StructLit{}
if k := v.v.Kind(); k != adt.StructKind && k != BottomKind {
// TODO: we should also add such a declaration for embeddings
@@ -2428,10 +2424,36 @@
n.AddConjunct(c)
n.Finalize(ctx)
n.Parent = v.v.Parent
+ }
+
+ // Simulate old embeddings.
+ envEmbed := &adt.Environment{
+ Up: env,
+ Vertex: n,
+ }
+
+ for _, d := range x.Decls {
+ switch x := d.(type) {
+ case adt.Value:
+ case adt.Expr:
+ // embedding
+ n := &adt.Vertex{Label: v.v.Label}
+ c := adt.MakeRootConjunct(envEmbed, x)
+ n.AddConjunct(c)
+ n.Finalize(ctx)
+ n.Parent = v.v.Parent
+ a = append(a, makeValue(v.idx, n, v.parent_))
+ }
+ }
+
+ // Could be done earlier, but keep struct with fields at end.
+ if len(fields) > 0 {
a = append(a, makeValue(v.idx, n, v.parent_))
}
- op = adt.AndOp
+ if len(a) > 1 {
+ op = adt.AndOp
+ }
default:
a = append(a, v)
diff --git a/cue/types_test.go b/cue/types_test.go
index ba1494d..eda326b 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -3344,6 +3344,9 @@
input: `v: { "foo", #def: 1 }`,
want: `{"foo",#def:1}`,
}, {
+ input: `v: { {} | { a: #A, b: #B}, #A: {} | { c: int} }, #B: int | bool`,
+ want: `&(|({} {a:#A,b:#B}) {#A:({}|{c:int})})`,
+ }, {
input: `v: { {c: a}, b: a }, a: int`,
want: `&({c:a} {b:a})`,
}, {
diff --git a/encoding/openapi/openapi_test.go b/encoding/openapi/openapi_test.go
index 3f96175..41af380 100644
--- a/encoding/openapi/openapi_test.go
+++ b/encoding/openapi/openapi_test.go
@@ -105,6 +105,14 @@
out: "openapi-norefs.json",
config: resolveRefs,
}, {
+ in: "embed.cue",
+ out: "embed.json",
+ config: defaultConfig,
+ }, {
+ in: "embed.cue",
+ out: "embed-norefs.json",
+ config: resolveRefs,
+ }, {
in: "oneof.cue",
out: "oneof-funcs.json",
config: &openapi.Config{
diff --git a/encoding/openapi/testdata/embed-norefs.json b/encoding/openapi/testdata/embed-norefs.json
new file mode 100644
index 0000000..4274bcc
--- /dev/null
+++ b/encoding/openapi/testdata/embed-norefs.json
@@ -0,0 +1,121 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "test",
+ "version": "v1"
+ },
+ "paths": {},
+ "components": {
+ "schemas": {
+ "Foo": {
+ "type": "string"
+ },
+ "LoadBalancerSettings": {
+ "type": "object",
+ "properties": {
+ "consistentHash": {
+ "type": "object",
+ "properties": {
+ "httpHeaderName": {
+ "type": "string"
+ }
+ }
+ },
+ "b": {
+ "type": "string"
+ }
+ },
+ "oneOf": [
+ {
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "consistentHash",
+ "b"
+ ],
+ "properties": {
+ "consistentHash": {
+ "oneOf": [
+ {
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "httpHeaderName"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "required": [
+ "httpHeaderName"
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "required": [
+ "consistentHash",
+ "b"
+ ],
+ "properties": {
+ "consistentHash": {
+ "oneOf": [
+ {
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "httpHeaderName"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "required": [
+ "httpHeaderName"
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ]
+ },
+ "LoadBalancerSettings.ConsistentHashLB": {
+ "type": "object",
+ "properties": {
+ "httpHeaderName": {
+ "type": "string"
+ }
+ },
+ "oneOf": [
+ {
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "httpHeaderName"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "required": [
+ "httpHeaderName"
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/encoding/openapi/testdata/embed.cue b/encoding/openapi/testdata/embed.cue
new file mode 100644
index 0000000..c3bc3b4
--- /dev/null
+++ b/encoding/openapi/testdata/embed.cue
@@ -0,0 +1,11 @@
+#Foo: string
+
+#LoadBalancerSettings: {
+ {} | {
+ consistentHash: #ConsistentHashLB
+ b: #Foo
+ }
+ #ConsistentHashLB: {} | {
+ httpHeaderName: string
+ }
+}
diff --git a/encoding/openapi/testdata/embed.json b/encoding/openapi/testdata/embed.json
new file mode 100644
index 0000000..d88cc2f
--- /dev/null
+++ b/encoding/openapi/testdata/embed.json
@@ -0,0 +1,85 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Generated by cue.",
+ "version": "no version"
+ },
+ "paths": {},
+ "components": {
+ "schemas": {
+ "Foo": {
+ "type": "string"
+ },
+ "LoadBalancerSettings": {
+ "type": "object",
+ "oneOf": [
+ {
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "consistentHash",
+ "b"
+ ],
+ "properties": {
+ "consistentHash": {
+ "$ref": "#/components/schemas/LoadBalancerSettings.ConsistentHashLB"
+ },
+ "b": {
+ "$ref": "#/components/schemas/Foo"
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "required": [
+ "consistentHash",
+ "b"
+ ],
+ "properties": {
+ "consistentHash": {
+ "$ref": "#/components/schemas/LoadBalancerSettings.ConsistentHashLB"
+ },
+ "b": {
+ "$ref": "#/components/schemas/Foo"
+ }
+ }
+ }
+ ]
+ },
+ "LoadBalancerSettings.ConsistentHashLB": {
+ "type": "object",
+ "oneOf": [
+ {
+ "not": {
+ "anyOf": [
+ {
+ "required": [
+ "httpHeaderName"
+ ],
+ "properties": {
+ "httpHeaderName": {
+ "type": "string"
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "required": [
+ "httpHeaderName"
+ ],
+ "properties": {
+ "httpHeaderName": {
+ "type": "string"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file