Repository: openconfig/goyang (go)

Issue:
enum values are not exported in rawSchema JSON
When a YANG file contains an enumeration the enumerated values are not output to the RawSchema because the `EnumType` ([link](https://github.com/openconfig/goyang/blob/de640d098cc1ef2b962c0fca3d3b710ebe1113ea/pkg/yang/types_builtin.go#L570)) is defined with fields with lower case initial letter (and hence internal).

For instance calling YGOT `generator` tool on `openconfig-system@2017-07-06.yang` will produce a Schema with out any enumeration values - (3rd last line below)

e.g. for severity
```json
"severity": {
    "Name": "severity",
    "Description": "Specifies that only messages of the given severity (or\ngreater severity) for the corresonding facility are logged",
    "Kind": 0,
    "Config": 0,
    "Prefix": {
        "Name": "oc-log",
        "Source": {
            "Keyword": "prefix",
            "HasArgument": true,
            "Argument": "oc-log"
        }
    },
    "Type": {
        "Name": "syslog-severity",
        "Kind": 14,
        "Enum": {}
    }
}
```

It would be useful to be able to get the list of values from the enum
The expected values would look like the following (it would suffice if either one of `ToString` or `ToInt` was made public):
```json
"Enum": {
    "Last": 7,
    "ToString": {
        "0": "EMERGENCY",
        "1": "ALERT",
        "2": "CRITICAL",
        "3": "ERROR",
        "4": "WARNING",
        "5": "NOTICE",
        "6": "INFORMATIONAL",
        "7": "DEBUG"
    },
    "ToInt": {
        "ALERT": 1,
        "CRITICAL": 2,
        "DEBUG": 7,
        "EMERGENCY": 0,
        "ERROR": 3,
        "INFORMATIONAL": 6,
        "NOTICE": 5,
        "WARNING": 4
    }
}
```

Pull request:
enum values are not exported in rawSchema JSON Fixes #227
Making `ToInt` and `ToString` public on the `EnumType`

Patch:
diff --git a/pkg/yang/types_builtin.go b/pkg/yang/types_builtin.go
index f936a22..108ecaa 100644
--- a/pkg/yang/types_builtin.go
+++ b/pkg/yang/types_builtin.go
@@ -568,12 +568,12 @@ func pow10(e uint8) uint64 {
 // A EnumType represents a mapping of strings to integers.  It is used both
 // for enumerations as well as bitfields.
 type EnumType struct {
-	last     int64 // maximum value assigned thus far
-	min      int64 // minimum value allowed
-	max      int64 // maximum value allowed
-	unique   bool  // numeric values must be unique (enums)
-	toString map[int64]string
-	toInt    map[string]int64
+	last     int64            // maximum value assigned thus far
+	min      int64            // minimum value allowed
+	max      int64            // maximum value allowed
+	unique   bool             // numeric values must be unique (enums)
+	ToString map[int64]string `json:",omitempty"` // map of enum entries by value (integer)
+	ToInt    map[string]int64 `json:",omitempty"` // map of enum entries by name (string)
 }
 
 // NewEnumType returns an initialized EnumType.
@@ -583,8 +583,8 @@ func NewEnumType() *EnumType {
 		min:      MinEnum,
 		max:      MaxEnum,
 		unique:   true,
-		toString: map[int64]string{},
-		toInt:    map[string]int64{},
+		ToString: map[int64]string{},
+		ToInt:    map[string]int64{},
 	}
 }
 
@@ -596,8 +596,8 @@ func NewBitfield() *EnumType {
 		last:     -1, // +1 will start at 0
 		min:      0,
 		max:      MaxBitfieldSize - 1,
-		toString: map[int64]string{},
-		toInt:    map[string]int64{},
+		ToString: map[int64]string{},
+		ToInt:    map[string]int64{},
 	}
 }
 
@@ -607,10 +607,10 @@ func NewBitfield() *EnumType {
 // assigned to the same value, the conversion from value to name will result in
 // the most recently assigned name.
 func (e *EnumType) Set(name string, value int64) error {
-	if _, ok := e.toInt[name]; ok {
+	if _, ok := e.ToInt[name]; ok {
 		return fmt.Errorf("field %s already assigned", name)
 	}
-	if oname, ok := e.toString[value]; e.unique && ok {
+	if oname, ok := e.ToString[value]; e.unique && ok {
 		return fmt.Errorf("fields %s and %s conflict on value %d", name, oname, value)
 	}
 	if value < e.min {
@@ -619,8 +619,8 @@ func (e *EnumType) Set(name string, value int64) error {
 	if value > e.max {
 		return fmt.Errorf("value %d for %s too large (maximum is %d)", value, name, e.max)
 	}
-	e.toString[value] = name
-	e.toInt[name] = value
+	e.ToString[value] = name
+	e.ToInt[name] = value
 	if value >= e.last {
 		e.last = value
 	}
@@ -638,24 +638,24 @@ func (e *EnumType) SetNext(name string) error {
 
 // Name returns the name in e associated with value.  The empty string is
 // returned if no name has been assigned to value.
-func (e *EnumType) Name(value int64) string { return e.toString[value] }
+func (e *EnumType) Name(value int64) string { return e.ToString[value] }
 
 // Value returns the value associated with name in e associated.  0 is returned
 // if name is not in e, or if it is the first value in an unnumbered enum. Use
 // IsDefined to definitively confirm name is in e.
-func (e *EnumType) Value(name string) int64 { return e.toInt[name] }
+func (e *EnumType) Value(name string) int64 { return e.ToInt[name] }
 
 // IsDefined returns true if name is defined in e, else false.
 func (e *EnumType) IsDefined(name string) bool {
-	_, defined := e.toInt[name]
+	_, defined := e.ToInt[name]
 	return defined
 }
 
 // Names returns the sorted list of enum string names.
 func (e *EnumType) Names() []string {
-	names := make([]string, len(e.toInt))
+	names := make([]string, len(e.ToInt))
 	i := 0
-	for name := range e.toInt {
+	for name := range e.ToInt {
 		names[i] = name
 		i++
 	}
@@ -671,9 +671,9 @@ func (p int64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
 // Values returns the sorted list of enum values.
 func (e *EnumType) Values() []int64 {
-	values := make([]int64, len(e.toInt))
+	values := make([]int64, len(e.ToInt))
 	i := 0
-	for _, value := range e.toInt {
+	for _, value := range e.ToInt {
 		values[i] = value
 		i++
 	}
@@ -683,8 +683,8 @@ func (e *EnumType) Values() []int64 {
 
 // NameMap returns a map of names to values.
 func (e *EnumType) NameMap() map[string]int64 {
-	m := make(map[string]int64, len(e.toInt))
-	for name, value := range e.toInt {
+	m := make(map[string]int64, len(e.ToInt))
+	for name, value := range e.ToInt {
 		m[name] = value
 	}
 	return m
@@ -692,8 +692,8 @@ func (e *EnumType) NameMap() map[string]int64 {
 
 // ValueMap returns a map of values to names.
 func (e *EnumType) ValueMap() map[int64]string {
-	m := make(map[int64]string, len(e.toString))
-	for name, value := range e.toString {
+	m := make(map[int64]string, len(e.ToString))
+	for name, value := range e.ToString {
 		m[name] = value
 	}
 	return m
diff --git a/pkg/yang/yangtype.go b/pkg/yang/yangtype.go
index 6795adb..cc1e33f 100644
--- a/pkg/yang/yangtype.go
+++ b/pkg/yang/yangtype.go
@@ -277,7 +277,7 @@ func (y *YangType) Equal(t *YangType) bool {
 		!y.Range.Equal(t.Range),
 		!tsEqual(y.Type, t.Type),
 		!cmp.Equal(y.Enum, t.Enum, cmp.Comparer(func(t, u EnumType) bool {
-			return cmp.Equal(t.unique, u.unique) && cmp.Equal(t.toInt, u.toInt) && cmp.Equal(t.toString, u.toString)
+			return cmp.Equal(t.unique, u.unique) && cmp.Equal(t.ToInt, u.ToInt) && cmp.Equal(t.ToString, u.ToString)
 		})):
 
 		return false


Test patch:
diff --git a/pkg/yang/types_builtin_test.go b/pkg/yang/types_builtin_test.go
index d0920a4..1d92a8e 100644
--- a/pkg/yang/types_builtin_test.go
+++ b/pkg/yang/types_builtin_test.go
@@ -15,6 +15,7 @@
 package yang
 
 import (
+	"encoding/json"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -815,3 +816,52 @@ func TestNumberString(t *testing.T) {
 		})
 	}
 }
+
+func TestEnumToJson(t *testing.T) {
+	tests := []struct {
+		desc    string
+		in      *EnumType
+		want    string
+		wantErr bool
+	}{{
+		"empty enum to JSON",
+		&EnumType{
+			last:     -1, // +1 will start at 0
+			min:      0,
+			max:      MaxBitfieldSize - 1,
+			ToString: map[int64]string{},
+			ToInt:    map[string]int64{},
+		},
+		`{}`,
+		false,
+	}, {
+		"2 value enum to JSON",
+		&EnumType{
+			last: 2,
+			min:  0,
+			max:  MaxBitfieldSize - 1,
+			ToString: map[int64]string{
+				1: "value1",
+				2: "value2",
+			},
+			ToInt: map[string]int64{
+				"value1": 1,
+				"value2": 2,
+			},
+		},
+		`{"ToString":{"1":"value1","2":"value2"},"ToInt":{"value1":1,"value2":2}}`,
+		false,
+	}}
+
+	for _, tt := range tests {
+		t.Run(tt.desc, func(t *testing.T) {
+			got, err := json.Marshal(tt.in)
+			if string(got) != tt.want {
+				t.Errorf("got: %v, want: %v", string(got), tt.want)
+			}
+			if (err != nil) != tt.wantErr {
+				t.Errorf("gotErr: %v, wantErr: %v", err, tt.wantErr)
+			}
+		})
+	}
+}
diff --git a/pkg/yang/types_test.go b/pkg/yang/types_test.go
index 03f7edb..d66a4f4 100644
--- a/pkg/yang/types_test.go
+++ b/pkg/yang/types_test.go
@@ -198,12 +198,12 @@ func TestTypeResolve(t *testing.T) {
 				min:    MinEnum,
 				max:    MaxEnum,
 				unique: true,
-				toString: map[int64]string{
+				ToString: map[int64]string{
 					0: "MERCURY",
 					1: "VENUS",
 					2: "EARTH",
 				},
-				toInt: map[string]int64{
+				ToInt: map[string]int64{
 					"MERCURY": 0,
 					"VENUS":   1,
 					"EARTH":   2,
@@ -228,12 +228,12 @@ func TestTypeResolve(t *testing.T) {
 				min:    MinEnum,
 				max:    MaxEnum,
 				unique: true,
-				toString: map[int64]string{
+				ToString: map[int64]string{
 					-1: "MERCURY",
 					10: "VENUS",
 					30: "EARTH",
 				},
-				toInt: map[string]int64{
+				ToInt: map[string]int64{
 					"MERCURY": -1,
 					"VENUS":   10,
 					"EARTH":   30,
@@ -258,12 +258,12 @@ func TestTypeResolve(t *testing.T) {
 				min:    MinEnum,
 				max:    MaxEnum,
 				unique: true,
-				toString: map[int64]string{
+				ToString: map[int64]string{
 					-1: "MERCURY",
 					10: "VENUS",
 					11: "EARTH",
 				},
-				toInt: map[string]int64{
+				ToInt: map[string]int64{
 					"MERCURY": -1,
 					"VENUS":   10,
 					"EARTH":   11,
@@ -968,7 +968,7 @@ func TestTypeResolveUnions(t *testing.T) {
 
 type testEnumTypeStruct struct {
 	Name string
-	// ToInt is the toInt map representing the enum value (if present).
+	// ToInt is the ToInt map representing the enum value (if present).
 	ToInt map[string]int64
 	Type  []*testEnumTypeStruct
 }
@@ -981,7 +981,7 @@ type testEnumTypeStruct struct {
 func filterTypeNames(ytype *YangType) *testEnumTypeStruct {
 	filteredNames := &testEnumTypeStruct{Name: ytype.Name}
 	if ytype.Enum != nil {
-		filteredNames.ToInt = ytype.Enum.toInt
+		filteredNames.ToInt = ytype.Enum.ToInt
 	}
 	for _, subtype := range ytype.Type {
 		filteredNames.Type = append(filteredNames.Type, filterTypeNames(subtype))