encoding/openapi: change naming in line with standard

Issue #56

Change-Id: I382b1a6d35d31ec42a9bdfe8b85037b78b518166
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2441
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index 10f93ef..6b83e78 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -36,7 +36,7 @@
 	expandRefs bool
 	nameFunc   func(inst *cue.Instance, path []string) string
 
-	schemas *orderedMap
+	schemas *OrderedMap
 
 	// Track external schemas.
 	externalRefs map[string]*externalType
@@ -49,17 +49,17 @@
 	value cue.Value
 }
 
-type oaSchema = orderedMap
+type oaSchema = OrderedMap
 
 type typeFunc func(b *builder, a cue.Value)
 
-func components(inst *cue.Instance, cfg *Config) (comps *orderedMap, err error) {
+func schemas(g *Generator, inst *cue.Instance) (schemas OrderedMap, err error) {
 	c := buildContext{
 		inst:         inst,
 		refPrefix:    "components/schema",
-		expandRefs:   cfg.ExpandReferences,
-		nameFunc:     cfg.ReferenceFunc,
-		schemas:      &orderedMap{},
+		expandRefs:   g.ExpandReferences,
+		nameFunc:     g.ReferenceFunc,
+		schemas:      &OrderedMap{},
 		externalRefs: map[string]*externalType{},
 	}
 
@@ -77,11 +77,7 @@
 		}
 	}()
 
-	schemas := &orderedMap{}
-	schemas.Set("schema", c.schemas)
-	comps = &orderedMap{}
-	comps.Set("openapi", "3.0.0")
-	comps.Set("components", schemas)
+	// Although paths is empty for now, it makes it valid OpenAPI spec.
 
 	i, err := inst.Value().Fields()
 	if err != nil {
@@ -93,7 +89,7 @@
 		if c.isInternal(label) {
 			continue
 		}
-		c.schemas.Set(c.makeRef(inst, []string{label}), c.build(label, i.Value()))
+		c.schemas.set(c.makeRef(inst, []string{label}), c.build(label, i.Value()))
 	}
 
 	// keep looping until a fixed point is reached.
@@ -109,10 +105,11 @@
 
 		for _, k := range external {
 			ext := c.externalRefs[k]
-			c.schemas.Set(ext.ref, c.build(ext.ref, ext.value.Eval()))
+			c.schemas.set(ext.ref, c.build(ext.ref, ext.value.Eval()))
 		}
 	}
-	return comps, nil
+
+	return *c.schemas, nil
 }
 
 func (c *buildContext) build(name string, v cue.Value) *oaSchema {
@@ -156,7 +153,7 @@
 		}
 		if len(doc) > 0 {
 			str := strings.TrimSpace(strings.Join(doc, "\n"))
-			schema.Prepend("description", str)
+			schema.prepend("description", str)
 		}
 	}
 
@@ -721,35 +718,35 @@
 
 func setType(t *oaSchema, b *builder) {
 	if b.typ != "" {
-		t.Set("type", b.typ)
+		t.set("type", b.typ)
 		if b.format != "" {
-			t.Set("format", b.format)
+			t.set("format", b.format)
 		}
 	}
 }
 
 func (b *builder) set(key string, v interface{}) {
 	if b.current == nil {
-		b.current = &orderedMap{}
+		b.current = &OrderedMap{}
 		b.allOf = append(b.allOf, b.current)
 		setType(b.current, b)
-	} else if b.current.Exists(key) {
-		b.current = &orderedMap{}
+	} else if b.current.exists(key) {
+		b.current = &OrderedMap{}
 		b.allOf = append(b.allOf, b.current)
 	}
-	b.current.Set(key, v)
+	b.current.set(key, v)
 }
 
 func (b *builder) kv(key string, value interface{}) *oaSchema {
-	constraint := &orderedMap{}
+	constraint := &OrderedMap{}
 	setType(constraint, b)
-	constraint.Set(key, value)
+	constraint.set(key, value)
 	return constraint
 }
 
 func (b *builder) setNot(key string, value interface{}) {
-	not := &orderedMap{}
-	not.Set("not", b.kv(key, value))
+	not := &OrderedMap{}
+	not.set("not", b.kv(key, value))
 	b.add(not)
 }
 
@@ -760,7 +757,7 @@
 			b.failf(cue.Value{}, "no type specified at finish")
 			return nil
 		}
-		t := &orderedMap{}
+		t := &OrderedMap{}
 		setType(t, b)
 		return t
 
@@ -769,8 +766,8 @@
 		return b.allOf[0]
 
 	default:
-		t := &orderedMap{}
-		t.Set("allOf", b.allOf)
+		t := &OrderedMap{}
+		t.set("allOf", b.allOf)
 		return t
 	}
 }
diff --git a/encoding/openapi/openapi.go b/encoding/openapi/openapi.go
index b1a4628..8d518a7 100644
--- a/encoding/openapi/openapi.go
+++ b/encoding/openapi/openapi.go
@@ -20,8 +20,8 @@
 	"cuelang.org/go/cue"
 )
 
-// A Config defines options for mapping CUE to and from OpenAPI.
-type Config struct {
+// A Generator converts CUE to OpenAPI.
+type Generator struct {
 	// ReferenceFunc allows users to specify an alternative representation
 	// for references.
 	ReferenceFunc func(inst *cue.Instance, path []string) string
@@ -31,23 +31,55 @@
 	SelfContained bool
 
 	// ExpandReferences replaces references with actual objects when generating
-	// OpenAPI Schema. It is an error for an CUE value to refer to itself
-	// when this object is used.
+	// OpenAPI Schema. It is an error for an CUE value to refer to itself.
 	ExpandReferences bool
 }
 
-// Gen generates the set OpenAPI schema for all top-level types of the given
-// instance.
+// Config is now Generator
+// Deprecated: use Generator
+type Config = Generator
+
+// Gen generates the set OpenAPI schema for all top-level types of the
+// given instance.
 //
+// Deprecated: use Generator.All.
 func Gen(inst *cue.Instance, c *Config) ([]byte, error) {
 	if c == nil {
 		c = defaultConfig
 	}
-	comps, err := components(inst, c)
+	all, err := c.All(inst)
 	if err != nil {
 		return nil, err
 	}
-	return json.Marshal(comps)
+	return json.Marshal(all)
+}
+
+// All generates an OpenAPI definition from the given instance.
+//
+// Note: only a limited number of top-level types are supported so far.
+func (g *Generator) All(inst *cue.Instance) (OrderedMap, error) {
+	all, err := g.Schemas(inst)
+	if err != nil {
+		return nil, err
+	}
+
+	schemas := &OrderedMap{}
+	schemas.set("schema", all)
+
+	top := OrderedMap{}
+	top.set("openapi", "3.0.0")
+	top.set("components", schemas)
+
+	return top, nil
+}
+
+// Schemas extracts component/schemas from the CUE top-level types.
+func (g *Generator) Schemas(inst *cue.Instance) (OrderedMap, error) {
+	comps, err := schemas(g, inst)
+	if err != nil {
+		return nil, err
+	}
+	return comps, err
 }
 
 var defaultConfig = &Config{}
diff --git a/encoding/openapi/orderedmap.go b/encoding/openapi/orderedmap.go
index 74e789f..8d419a8 100644
--- a/encoding/openapi/orderedmap.go
+++ b/encoding/openapi/orderedmap.go
@@ -16,56 +16,64 @@
 
 import "encoding/json"
 
-type orderedMap []kvPair
+// An OrderedMap is a set of key-value pairs that preserves the order in which
+// items were added. It marshals to JSON as an object.
+type OrderedMap []KeyValue
 
-type kvPair struct {
-	key   string
-	value interface{}
+// KeyValue associates a value with a key.
+type KeyValue struct {
+	Key   string
+	Value interface{}
 }
 
-func (m *orderedMap) Prepend(key string, value interface{}) {
-	*m = append([]kvPair{{key, value}}, (*m)...)
+func (m *OrderedMap) prepend(key string, value interface{}) {
+	*m = append([]KeyValue{{key, value}}, (*m)...)
 }
 
-func (m *orderedMap) Set(key string, value interface{}) {
+// 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, value interface{}) {
 	for i, v := range *m {
-		if v.key == key {
-			(*m)[i].value = value
+		if v.Key == key {
+			(*m)[i].Value = value
 			return
 		}
 	}
-	*m = append(*m, kvPair{key, value})
+	*m = append(*m, KeyValue{key, value})
 }
 
-func (m *orderedMap) Exists(key string) bool {
-	for _, v := range *m {
-		if v.key == key {
+// exists reports whether a key-value pair exists for the given key.
+func (m OrderedMap) exists(key string) bool {
+	for _, v := range m {
+		if v.Key == key {
 			return true
 		}
 	}
 	return false
 }
 
-func (m *orderedMap) MarshalJSON() (b []byte, err error) {
+// MarshalJSON implements Marshal
+func (m OrderedMap) MarshalJSON() (b []byte, err error) {
 	b = append(b, '{')
-	for i, v := range *m {
+	for i, v := range m {
 		if i > 0 {
 			b = append(b, ",\n"...)
 		}
-		key, err := json.Marshal(v.key)
+		key, err := json.Marshal(v.Key)
 		if je, ok := err.(*json.MarshalerError); ok {
 			return nil, je.Err
 		}
 		b = append(b, key...)
 		b = append(b, ": "...)
 
-		value, err := json.Marshal(v.value)
+		value, jerr := json.Marshal(v.Value)
 		if je, ok := err.(*json.MarshalerError); ok {
-			// return nil, je.Err
+			err = jerr
 			value, _ = json.Marshal(je.Err.Error())
 		}
 		b = append(b, value...)
 	}
 	b = append(b, '}')
-	return b, nil
+	return b, err
 }
diff --git a/encoding/openapi/types.go b/encoding/openapi/types.go
index 03eada8..5cf481d 100644
--- a/encoding/openapi/types.go
+++ b/encoding/openapi/types.go
@@ -48,7 +48,7 @@
 	return cueToOpenAPI[string(b)]
 }
 
-func simplify(b *builder, t *orderedMap) {
+func simplify(b *builder, t *OrderedMap) {
 	if b.format == "" {
 		return
 	}
@@ -58,17 +58,17 @@
 	}
 }
 
-func simplifyNumber(t *orderedMap, format string) string {
+func simplifyNumber(t *OrderedMap, format string) string {
 	pairs := *t
 	k := 0
 	for i, kv := range pairs {
-		switch kv.key {
+		switch kv.Key {
 		case "minimum":
-			if decimalEqual(minMap[format], kv.value) {
+			if decimalEqual(minMap[format], kv.Value) {
 				continue
 			}
 		case "maximum":
-			if decimalEqual(maxMap[format], kv.value) {
+			if decimalEqual(maxMap[format], kv.Value) {
 				continue
 			}
 		}