encoding/openapi: supported nested definitions

Change-Id: I2c79b5d02fdc9ac22fa93f5e06be46f73cf0be51
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5341
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/types.go b/cue/types.go
index b4398b9..05c0fbb 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -295,6 +295,11 @@
 	return i.cur.path.arc.optional
 }
 
+// IsDefinition reports if a field is a definition.
+func (i *Iterator) IsDefinition() bool {
+	return i.cur.path.arc.definition
+}
+
 // marshalJSON iterates over the list and generates JSON output. HasNext
 // will return false after this operation.
 func marshalList(l *Iterator) (b []byte, err errors.Error) {
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index c3c485d..da14cf0 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -657,14 +657,18 @@
 		properties = &OrderedMap{}
 	}
 
-	for i, _ := v.Fields(cue.Optional(true)); i.Next(); {
+	for i, _ := v.Fields(cue.Optional(true), cue.Definitions(true)); i.Next(); {
 		label := i.Label()
 		var core *builder
 		if b.core != nil {
 			core = b.core.properties[label]
 		}
 		schema := b.schema(core, label, i.Value())
-		if !b.isNonCore() || len(schema.Elts) > 0 {
+		switch {
+		case i.IsDefinition():
+			ref := strings.Join(append(b.ctx.path, label), ".")
+			b.ctx.schemas.Set(ref, schema)
+		case !b.isNonCore() || len(schema.Elts) > 0:
 			properties.Set(label, schema)
 		}
 	}
@@ -1144,7 +1148,7 @@
 	} else {
 		a = append(a, ref...)
 	}
-	return path.Join(a...)
+	return strings.Join(a, ".")
 }
 
 func (b *builder) int64(v cue.Value) int64 {
diff --git a/encoding/openapi/openapi_test.go b/encoding/openapi/openapi_test.go
index dfc0fc9..d2f32e6 100644
--- a/encoding/openapi/openapi_test.go
+++ b/encoding/openapi/openapi_test.go
@@ -50,6 +50,10 @@
 		"structural.json",
 		resolveRefs,
 	}, {
+		"nested.cue",
+		"nested.json",
+		defaultConfig,
+	}, {
 		"simple.cue",
 		"simple.json",
 		resolveRefs,
diff --git a/encoding/openapi/testdata/nested.cue b/encoding/openapi/testdata/nested.cue
new file mode 100644
index 0000000..f1343e0
--- /dev/null
+++ b/encoding/openapi/testdata/nested.cue
@@ -0,0 +1,8 @@
+package foo
+
+Struct :: {
+	T :: int
+
+	a?: T
+	b?: T
+}
diff --git a/encoding/openapi/testdata/nested.json b/encoding/openapi/testdata/nested.json
new file mode 100644
index 0000000..07397c5
--- /dev/null
+++ b/encoding/openapi/testdata/nested.json
@@ -0,0 +1,23 @@
+{
+   "openapi": "3.0.0",
+   "info": {},
+   "paths": {},
+   "components": {
+      "schemas": {
+         "Struct.T": {
+            "type": "integer"
+         },
+         "Struct": {
+            "type": "object",
+            "properties": {
+               "a": {
+                  "$ref": "#/components/schemas/Struct.T"
+               },
+               "b": {
+                  "$ref": "#/components/schemas/Struct.T"
+               }
+            }
+         }
+      }
+   }
+}
\ No newline at end of file