From c2cff924068a25036c18630f4ce77f3ba3885749 Mon Sep 17 00:00:00 2001 From: wenovus Date: Thu, 26 May 2022 17:11:27 -0700 Subject: [PATCH 01/19] Make `EnumeratedYANGType.IdentityBaseName` exported. I think it makes sense for this to be exported. Not sure why I wanted it to be extracted from the plugin originally. --- ygen/genir.go | 6 +++--- ygen/genir_test.go | 14 ++++++++------ ygen/ir.go | 6 ++++-- ygen/protogen.go | 4 ++-- ygen/protogen_test.go | 2 +- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/ygen/genir.go b/ygen/genir.go index fb1f78eab..442e38f49 100644 --- a/ygen/genir.go +++ b/ygen/genir.go @@ -131,9 +131,9 @@ func GenerateIR(yangFiles, includePaths []string, langMapper LangMapper, opts IR continue } - switch { - case enum.entry.Type.IdentityBase != nil: - et.identityBaseName = enum.entry.Type.IdentityBase.Name + switch enum.kind { + case IdentityType: + et.IdentityBaseName = enum.entry.Type.IdentityBase.Name // enum corresponds to an identityref - hence the values are defined // based on the values that the identity has. Since there is no explicit ordering // in an identity, then we go through and put the values in alphabetical order in diff --git a/ygen/genir_test.go b/ygen/genir_test.go index 0d36a0771..0e13998a7 100644 --- a/ygen/genir_test.go +++ b/ygen/genir_test.go @@ -849,9 +849,10 @@ func protoIR(nestedDirectories bool) *IR { }, }, "/openconfig-complex/SOFTWARE": { - Name: "ComplexSOFTWARE", - Kind: IdentityType, - TypeName: "identityref", + Name: "ComplexSOFTWARE", + Kind: IdentityType, + IdentityBaseName: "SOFTWARE", + TypeName: "identityref", ValToYANGDetails: []ygot.EnumDefinition{ {Name: "OS", DefiningModule: "openconfig-complex"}, }, @@ -2710,9 +2711,10 @@ func TestGenerateIR(t *testing.T) { }, }, "/openconfig-complex/SOFTWARE": { - Name: "Complex_SOFTWARE", - Kind: IdentityType, - TypeName: "identityref", + Name: "Complex_SOFTWARE", + Kind: IdentityType, + IdentityBaseName: "SOFTWARE", + TypeName: "identityref", ValToYANGDetails: []ygot.EnumDefinition{ {Name: "OS", DefiningModule: "openconfig-complex"}, }, diff --git a/ygen/ir.go b/ygen/ir.go index be0874608..dff95fa09 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -550,8 +550,10 @@ type EnumeratedYANGType struct { // generation mechanism to select how different enumerated // value types are output. Kind EnumeratedValueType - // TODO(wenbli): This needs to be replaced using a plugin mechanism. - identityBaseName string + // IdentityBaseName, which is present only when the enumerated type is + // an IdentityType, is the name of the base identity from which all + // valid identity values are derived. + IdentityBaseName string // TypeName stores the original YANG type name for the enumeration. TypeName string // TypeDefaultValue stores the default value of the enum type's default diff --git a/ygen/protogen.go b/ygen/protogen.go index 7fd3c6492..d6300165c 100644 --- a/ygen/protogen.go +++ b/ygen/protogen.go @@ -785,7 +785,7 @@ func writeProtoEnums(enums map[string]*EnumeratedYANGType, annotateEnumNames boo for _, enumDef := range enum.ValToYANGDetails { // Calculate a tag value for the identity values, since otherwise when another // module augments this module then the enum values may be subject to change. - tag, err := fieldTag(fmt.Sprintf("%s%s", enum.identityBaseName, enumDef.Name)) + tag, err := fieldTag(fmt.Sprintf("%s%s", enum.IdentityBaseName, enumDef.Name)) if err != nil { errs = append(errs, fmt.Errorf("cannot calculate tag for %s: %v", enumDef.Name, err)) } @@ -795,7 +795,7 @@ func writeProtoEnums(enums map[string]*EnumeratedYANGType, annotateEnumNames boo } p.Values = values p.ValuePrefix = strings.ToUpper(enum.Name) - p.Description = fmt.Sprintf("YANG identity %s", enum.identityBaseName) + p.Description = fmt.Sprintf("YANG identity %s", enum.IdentityBaseName) case DerivedEnumerationType, DerivedUnionEnumerationType: ge, err := genProtoEnum(enum, annotateEnumNames, true) if err != nil { diff --git a/ygen/protogen_test.go b/ygen/protogen_test.go index d71aeeed5..4d503cd7f 100644 --- a/ygen/protogen_test.go +++ b/ygen/protogen_test.go @@ -1779,7 +1779,7 @@ func TestWriteProtoEnums(t *testing.T) { "/field-name|enum": { Name: "EnumeratedValue", Kind: IdentityType, - identityBaseName: "IdentityValue", + IdentityBaseName: "IdentityValue", ValToYANGDetails: []ygot.EnumDefinition{{ Name: "VALUE_A", DefiningModule: "mod", From ef433fab686b29077ada40b2dc6107e7f4a24abe Mon Sep 17 00:00:00 2001 From: wenovus Date: Thu, 26 May 2022 17:16:55 -0700 Subject: [PATCH 02/19] Add `LangMapperExt` and `Flags` fields to each level of the `IR`. The idea is to to call a different method from the interface for each level of hierarchy of the IR to populate the `Flag` fields for that level of the IR. So within `getOrderedDirDetails` for each `ParsedDirectory` and `NodeDetails`, and within the loop of genEnums (in `GenerateIR`) for each `EnumeratedYANGType`. This way the caller of `GenerateIR` is able to produce extra information for any level of the `IR`. Tests added in genir_test.go --- ygen/directory.go | 2 ++ ygen/genir.go | 2 ++ ygen/genir_test.go | 25 +++++++++++++++++++++- ygen/goelements.go | 4 ++++ ygen/ir.go | 48 +++++++++++++++++++++++++++++++++++++++---- ygen/protoelements.go | 4 ++++ 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/ygen/directory.go b/ygen/directory.go index 7ba857d7c..bd3b4073a 100644 --- a/ygen/directory.go +++ b/ygen/directory.go @@ -292,6 +292,8 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory } } + nd.Flags = langMapper.PopulateFieldFlags(*nd, field) + pd.Fields[fn] = nd } dirDets[dir.Entry.Path()] = pd diff --git a/ygen/genir.go b/ygen/genir.go index 442e38f49..b83449644 100644 --- a/ygen/genir.go +++ b/ygen/genir.go @@ -169,6 +169,8 @@ func GenerateIR(yangFiles, includePaths []string, langMapper LangMapper, opts IR } } + et.Flags = langMapper.PopulateEnumFlags(*et, enum.entry.Type) + enumDefinitionMap[enum.id] = et } diff --git a/ygen/genir_test.go b/ygen/genir_test.go index 0e13998a7..344c18663 100644 --- a/ygen/genir_test.go +++ b/ygen/genir_test.go @@ -22,6 +22,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/openconfig/gnmi/errdiff" gpb "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" "github.com/openconfig/ygot/ygot" "google.golang.org/protobuf/testing/protocmp" @@ -940,6 +941,26 @@ func protoIR(nestedDirectories bool) *IR { } } +type goLangMapper struct { + *GoLangMapper +} + +// PopulateFieldFlags populates extra information given a particular +// field of a ParsedDirectory and the corresponding AST node. +func (goLangMapper) PopulateFieldFlags(nd NodeDetails, field *yang.Entry) map[string]string { + if field.Path() == "/openconfig-simple/parent" { + return map[string]string{"foo": "bar"} + } else { + return nil + } +} + +// PopulateEnumFlags populates extra information given a particular +// enumerated type its corresponding AST representation. +func (goLangMapper) PopulateEnumFlags(et EnumeratedYANGType, yangtype *yang.YangType) map[string]string { + return map[string]string{"typename": yangtype.Name} +} + func TestGenerateIR(t *testing.T) { tests := []struct { desc string @@ -956,7 +977,7 @@ func TestGenerateIR(t *testing.T) { filepath.Join(datapath, "openconfig-simple.yang"), filepath.Join(datapath, "openconfig-simple-augment2.yang"), }, - inLangMapper: NewGoLangMapper(true), + inLangMapper: goLangMapper{GoLangMapper: NewGoLangMapper(true)}, inOpts: IROptions{ TransformationOptions: TransformationOpts{ CompressBehaviour: genutil.PreferIntendedConfig, @@ -994,6 +1015,7 @@ func TestGenerateIR(t *testing.T) { MappedPathModules: [][]string{{"openconfig-simple"}}, ShadowMappedPaths: nil, ShadowMappedPathModules: nil, + Flags: map[string]string{"foo": "bar"}, }, "remote-container": { Name: "RemoteContainer", @@ -1244,6 +1266,7 @@ func TestGenerateIR(t *testing.T) { Name: "TWO", Value: 1, }}, + Flags: map[string]string{"typename": "enumeration"}, }, }, ModelData: []*gpb.ModelData{{Name: "openconfig-remote"}, {Name: "openconfig-simple"}, {Name: "openconfig-simple-augment2"}, {Name: "openconfig-simple-grouping"}}, diff --git a/ygen/goelements.go b/ygen/goelements.go index f9df6d3e0..3ec288917 100644 --- a/ygen/goelements.go +++ b/ygen/goelements.go @@ -154,6 +154,10 @@ type GoLangMapper struct { // union subtypes in the generated code instead of using wrapper types. // NOTE: This flag will be removed as part of ygot's v1 release. simpleUnions bool + + // UnimplementedLangMapperExt ensures GoLangMapper implements the + // LangMapperExt interface for forwards compatibility. + UnimplementedLangMapperExt } // NewGoLangMapper creates a new GoLangMapper instance, initialised with the diff --git a/ygen/ir.go b/ygen/ir.go index dff95fa09..28653a516 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -89,6 +89,45 @@ type LangMapper interface { // the mapped such that leaves of type leafref can be resolved to // their target leaves. SetSchemaTree(*schemaTree) + + // LangMapperExt contains extensions that the LangMapper instance + // should implement if extra information from the IR is required. + // When implementing this, UnimplementedLangMapperExt should be + // embedded in the implementation type in order to ensure forward + // compatibility. + LangMapperExt +} + +// LangMapperExt contains extensions that the LangMapper instance should +// implement if extra information from the IR is required. These flag values +// are expected to contain information rarely used but needed from goyang's +// AST. Values that are expected to be used more often should be placed in the +// IR itself so that other users can get access to the same information without +// implementing it themselves. +type LangMapperExt interface { + // PopulateFieldFlags populates extra information given a particular + // field of a ParsedDirectory and the corresponding AST node. + PopulateFieldFlags(NodeDetails, *yang.Entry) map[string]string + // PopulateEnumFlags populates extra information given a particular + // enumerated type its corresponding AST representation. + PopulateEnumFlags(EnumeratedYANGType, *yang.YangType) map[string]string +} + +// UnimplementedLangMapperExt should be embedded to have forward compatible +// implementations. +type UnimplementedLangMapperExt struct { +} + +// PopulateFieldFlags populates extra information given a particular +// field of a ParsedDirectory and the corresponding AST node. +func (UnimplementedLangMapperExt) PopulateFieldFlags(NodeDetails, *yang.Entry) map[string]string { + return nil +} + +// PopulateEnumFlags populates extra information given a particular +// enumerated type its corresponding AST representation. +func (UnimplementedLangMapperExt) PopulateEnumFlags(EnumeratedYANGType, *yang.YangType) map[string]string { + return nil } // IR represents the returned intermediate representation produced by ygen to @@ -364,10 +403,11 @@ type NodeDetails struct { // Shadow paths are paths that have sibling config/state values // that have been compressed out due to path compression. ShadowMappedPathModules [][]string - // TODO(wenbli): Think about how to add this in an easily-usable way. + // LangMapper during IR generation to assist the code generation stage. // Flags contains extra information that can be populated by the // LangMapper during IR generation to assist the code generation stage. - //Flags map[string]string + // It needs to implement the LangMapperExt interface. + Flags map[string]string } // NodeType describes the different types of node that can @@ -564,8 +604,8 @@ type EnumeratedYANGType struct { // and its YANG-specific details (as defined by the // ygot.EnumDefinition). ValToYANGDetails []ygot.EnumDefinition - // TODO(wenbli): Think about how to add this in an easily-usable way. // Flags contains extra information that can be populated by the // LangMapper during IR generation to assist the code generation stage. - //Flags map[string]string + // It needs to implement the LangMapperExt interface. + Flags map[string]string } diff --git a/ygen/protoelements.go b/ygen/protoelements.go index 5333941bf..93f9096e8 100644 --- a/ygen/protoelements.go +++ b/ygen/protoelements.go @@ -57,6 +57,10 @@ type ProtoLangMapper struct { // enumPackageName is the name of the package within which global enumerated values // are defined (i.e., typedefs that contain enumerations, or YANG identities). enumPackageName string + + // UnimplementedLangMapperExt ensures GoLangMapper implements the + // LangMapperExt interface for forwards compatibility. + UnimplementedLangMapperExt } // NewProtoLangMapper creates a new ProtoLangMapper instance, initialised with the From a6d7d012d5b65046a68c07524a80336d754af42c Mon Sep 17 00:00:00 2001 From: wenovus Date: Thu, 26 May 2022 17:29:56 -0700 Subject: [PATCH 03/19] Generate `YANGType` using `LangMapperExt` mechanism. --- ygen/directory.go | 3 -- ygen/genir_test.go | 89 ---------------------------------------- ygen/ir.go | 15 ------- ypathgen/pathgen.go | 24 +++++++++-- ypathgen/pathgen_test.go | 42 ++++++++----------- 5 files changed, 39 insertions(+), 134 deletions(-) diff --git a/ygen/directory.go b/ygen/directory.go index bd3b4073a..29aec2049 100644 --- a/ygen/directory.go +++ b/ygen/directory.go @@ -271,9 +271,6 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory nd.Type = t nd.LangType = mtype - nd.YANGDetails.Type = &YANGType{ - Name: field.Type.Name, - } case field.IsList(): nd.Type = ListNode case util.IsAnydata(field): diff --git a/ygen/genir_test.go b/ygen/genir_test.go index 344c18663..4e5895945 100644 --- a/ygen/genir_test.go +++ b/ygen/genir_test.go @@ -53,7 +53,6 @@ func protoIR(nestedDirectories bool) *IR { SchemaPath: "/model", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, LangType: nil, @@ -75,7 +74,6 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", PresenceStatement: ygot.String("This is an example presence container"), Description: "", - Type: nil, }, Type: ContainerNode, LangType: nil, @@ -117,7 +115,6 @@ func protoIR(nestedDirectories bool) *IR { SchemaPath: "/model/anydata-leaf", LeafrefTargetPath: "", Description: "some anydata leaf", - Type: nil, }, Type: AnyDataNode, LangType: nil, @@ -138,7 +135,6 @@ func protoIR(nestedDirectories bool) *IR { SchemaPath: "/model/dateref", LeafrefTargetPath: "/openconfig-complex/model/a/single-key/config/dates", Description: "", - Type: &YANGType{Name: "leafref"}, }, Type: LeafNode, LangType: &MappedType{ @@ -163,7 +159,6 @@ func protoIR(nestedDirectories bool) *IR { SchemaPath: "/model/b/multi-key", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ListNode, LangType: nil, @@ -184,7 +179,6 @@ func protoIR(nestedDirectories bool) *IR { SchemaPath: "/model/a/single-key", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ListNode, LangType: nil, @@ -232,7 +226,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/a/single-key/state/dates", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "weekday"}, }, Type: LeafListNode, LangType: &MappedType{ @@ -258,7 +251,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/a/single-key/state/dates-with-defaults", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "weekday"}, }, Type: LeafListNode, LangType: &MappedType{ @@ -284,7 +276,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/a/single-key/state/iref", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "identityref"}, }, Type: LeafNode, LangType: &MappedType{ @@ -328,7 +319,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/a/single-key/state/key", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "days-of-week"}, }, Type: LeafNode, LangType: &MappedType{ @@ -398,7 +388,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/a/single-key/state/leaf-default-override", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "union"}, }, Type: LeafNode, LangType: &MappedType{ @@ -452,7 +441,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/a/single-key/state/simple-union-enum", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "union"}, }, Type: LeafNode, LangType: &MappedType{ @@ -506,7 +494,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/a/single-key/state/singleton-union-enum", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "union"}, }, Type: LeafNode, LangType: &MappedType{ @@ -550,7 +537,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/a/single-key/state/typedef-enum", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "weekend-days"}, }, Type: LeafNode, LangType: &MappedType{ @@ -594,7 +580,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/a/single-key/state/typedef-union-enum", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "cyclone-scales"}, }, Type: LeafNode, LangType: &MappedType{ @@ -683,7 +668,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/b/multi-key/state/key1", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "union"}, }, Type: LeafNode, LangType: &MappedType{NativeType: "ywrapper.UintValue"}, @@ -735,7 +719,6 @@ func protoIR(nestedDirectories bool) *IR { ShadowSchemaPath: "/model/b/multi-key/state/key2", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "enumeration"}, }, Type: LeafNode, LangType: &MappedType{ @@ -816,7 +799,6 @@ func protoIR(nestedDirectories bool) *IR { DefiningModule: "openconfig-complex", Path: "/openconfig-complex/model/c/unkeyed-list/field", SchemaPath: "/model/c/unkeyed-list/field", - Type: &YANGType{Name: "binary"}, }, Type: LeafNode, LangType: &MappedType{NativeType: "ywrapper.BytesValue"}, @@ -1008,7 +990,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent", LeafrefTargetPath: "", Description: "I am a parent container\nthat has 4 children.", - Type: nil, }, Type: ContainerNode, MappedPaths: [][]string{{"parent"}}, @@ -1029,7 +1010,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, MappedPaths: [][]string{{"remote-container"}}, @@ -1057,7 +1037,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, LangType: nil, @@ -1087,7 +1066,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/zero", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1112,7 +1090,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/parent/child/state/one", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1139,7 +1116,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/two", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1167,7 +1143,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/parent/child/state/three", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "enumeration"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1195,7 +1170,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/parent/child/state/four", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "binary"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1233,7 +1207,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/remote-container/state/a-leaf", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1308,7 +1281,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent", LeafrefTargetPath: "", Description: "I am a parent container\nthat has 4 children.", - Type: nil, }, Type: ContainerNode, MappedPaths: [][]string{{"parent"}}, @@ -1328,7 +1300,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, MappedPaths: [][]string{{"remote-container"}}, @@ -1356,7 +1327,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, LangType: nil, @@ -1386,7 +1356,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/zero", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: LeafNode, LangType: &MappedType{NativeType: "string", ZeroValue: `""`}, @@ -1408,7 +1377,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/parent/child/config/one", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1435,7 +1403,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/two", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1463,7 +1430,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/parent/child/config/three", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "enumeration"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1491,7 +1457,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/parent/child/config/four", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "binary"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1529,7 +1494,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/remote-container/config/a-leaf", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: LeafNode, LangType: &MappedType{ @@ -1603,7 +1567,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent", LeafrefTargetPath: "", Description: "I am a parent container\nthat has 4 children.", - Type: nil, }, Type: ContainerNode, MappedPaths: [][]string{{"parent"}}, @@ -1623,7 +1586,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, MappedPaths: [][]string{{"remote-container"}}, @@ -1651,7 +1613,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, LangType: nil, @@ -1681,7 +1642,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/config", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: 1, MappedPaths: [][]string{{"config"}}, @@ -1698,7 +1658,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: 1, MappedPaths: [][]string{{"state"}}, @@ -1728,7 +1687,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/config/four", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "binary"}, }, Type: 3, LangType: &MappedType{ @@ -1755,7 +1713,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/config/one", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -1782,7 +1739,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/config/three", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "enumeration"}, }, Type: 3, LangType: &MappedType{ @@ -1821,7 +1777,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/four", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "binary"}, }, Type: 3, LangType: &MappedType{ @@ -1848,7 +1803,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/one", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -1875,7 +1829,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/three", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "enumeration"}, }, Type: 3, LangType: &MappedType{ @@ -1902,7 +1855,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/two", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -1929,7 +1881,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/zero", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -1968,7 +1919,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container/config", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: 1, MappedPaths: [][]string{{"config"}}, @@ -1985,7 +1935,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container/state", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: 1, MappedPaths: [][]string{{"state"}}, @@ -2015,7 +1964,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container/config/a-leaf", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -2052,7 +2000,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container/state/a-leaf", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -2124,7 +2071,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/e1", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2180,7 +2126,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/model", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, LangType: nil, @@ -2202,7 +2147,6 @@ func TestGenerateIR(t *testing.T) { LeafrefTargetPath: "", PresenceStatement: ygot.String("This is an example presence container"), Description: "", - Type: nil, }, Type: ContainerNode, LangType: nil, @@ -2243,7 +2187,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/model/anydata-leaf", LeafrefTargetPath: "", Description: "some anydata leaf", - Type: nil, }, Type: AnyDataNode, LangType: nil, @@ -2264,7 +2207,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/model/dateref", LeafrefTargetPath: "/openconfig-complex/model/a/single-key/config/dates", Description: "", - Type: &YANGType{Name: "leafref"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2289,7 +2231,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/model/b/multi-key", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ListNode, LangType: nil, @@ -2310,7 +2251,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/model/a/single-key", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ListNode, LangType: nil, @@ -2358,7 +2298,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/a/single-key/state/dates", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "weekday"}, }, Type: LeafListNode, LangType: &MappedType{NativeType: "uint8", ZeroValue: "0", DefaultValue: ygot.String("[]uint8{5}")}, @@ -2380,7 +2319,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/a/single-key/state/dates-with-defaults", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "weekday"}, }, Type: LeafListNode, LangType: &MappedType{NativeType: "uint8", ZeroValue: "0", DefaultValue: ygot.String("[]uint8{1, 2}")}, @@ -2402,7 +2340,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/a/single-key/state/iref", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "identityref"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2428,7 +2365,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/a/single-key/state/key", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "days-of-week"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2457,7 +2393,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/a/single-key/state/leaf-default-override", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "union"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2487,7 +2422,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/a/single-key/state/simple-union-enum", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "union"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2517,7 +2451,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/a/single-key/state/singleton-union-enum", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "union"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2547,7 +2480,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/a/single-key/state/typedef-enum", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "weekend-days"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2574,7 +2506,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/a/single-key/state/typedef-union-enum", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "cyclone-scales"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2630,7 +2561,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/b/multi-key/state/key1", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "union"}, }, Type: LeafNode, LangType: &MappedType{ @@ -2656,7 +2586,6 @@ func TestGenerateIR(t *testing.T) { ShadowSchemaPath: "/model/b/multi-key/state/key2", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "enumeration"}, }, Type: LeafNode, LangType: &MappedType{NativeType: "E_MultiKey_Key2", IsEnumeratedValue: true, ZeroValue: "0"}, @@ -2701,7 +2630,6 @@ func TestGenerateIR(t *testing.T) { DefiningModule: "openconfig-complex", Path: "/openconfig-complex/model/c/unkeyed-list/field", SchemaPath: "/model/c/unkeyed-list/field", - Type: &YANGType{Name: "binary"}, }, Type: LeafNode, LangType: &MappedType{NativeType: "Binary", ZeroValue: "nil"}, @@ -2902,7 +2830,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent", LeafrefTargetPath: "", Description: "I am a parent container\nthat has 4 children.", - Type: nil, }, Type: ContainerNode, MappedPaths: [][]string{{"parent"}}, @@ -2922,7 +2849,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, MappedPaths: [][]string{{"remote-container"}}, @@ -2950,7 +2876,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ContainerNode, LangType: nil, @@ -2981,7 +2906,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/config", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: 1, MappedPaths: [][]string{{"config"}}, @@ -2998,7 +2922,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: 1, MappedPaths: [][]string{{"state"}}, @@ -3028,7 +2951,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/config/four", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "binary"}, }, Type: 3, LangType: &MappedType{ @@ -3055,7 +2977,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/config/one", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -3082,7 +3003,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/config/three", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "enumeration"}, }, Type: 3, LangType: &MappedType{ @@ -3121,7 +3041,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/four", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "binary"}, }, Type: 3, LangType: &MappedType{ @@ -3148,7 +3067,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/one", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -3175,7 +3093,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/three", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "enumeration"}, }, Type: 3, LangType: &MappedType{ @@ -3203,7 +3120,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/two", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -3230,7 +3146,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/parent/child/state/zero", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -3269,7 +3184,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container/config", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: 1, MappedPaths: [][]string{{"config"}}, @@ -3286,7 +3200,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container/state", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: 1, MappedPaths: [][]string{{"state"}}, @@ -3316,7 +3229,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container/config/a-leaf", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ @@ -3354,7 +3266,6 @@ func TestGenerateIR(t *testing.T) { SchemaPath: "/remote-container/state/a-leaf", LeafrefTargetPath: "", Description: "", - Type: &YANGType{Name: "string"}, }, Type: 3, LangType: &MappedType{ diff --git a/ygen/ir.go b/ygen/ir.go index 28653a516..a5d7b8236 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -520,21 +520,6 @@ type YANGNodeDetails struct { PresenceStatement *string // Description contains the description of the node. Description string - // Type is the YANG type which represents the node. It is only - // applicable for leaf or leaf-list nodes because only these nodes can - // have type statements. - // TODO(wenbli): This needs to be replaced using a plugin mechanism. - Type *YANGType -} - -// YANGType represents a YANG type. -type YANGType struct { - // Name is the YANG type name of the type. - Name string - // TODO(wenbli): Add this. - // Module is the name of the module which defined the type. This is - // only applicable if the type were a typedef. - //Module string } // EnumeratedValueType is used to indicate the source YANG type diff --git a/ypathgen/pathgen.go b/ypathgen/pathgen.go index 3ebd6a969..131f8d698 100644 --- a/ypathgen/pathgen.go +++ b/ypathgen/pathgen.go @@ -65,6 +65,10 @@ const ( // NOTE: This cannot be "", as the builder method name would conflict // with the child constructor method for the keys. BuilderKeyPrefix = "With" + + // yangTypeNameFlagKey is a custom flag for storing the YANG type's + // name for a YANG node. + yangTypeNameFlagKey = "YANG:typename" ) // NewDefaultConfig creates a GenConfig with default configuration. @@ -198,6 +202,20 @@ type GoImports struct { YgotImportPath string } +type goLangMapper struct { + *ygen.GoLangMapper +} + +// PopulateFieldFlags populates extra field information for pathgen. +func (goLangMapper) PopulateFieldFlags(nd ygen.NodeDetails, field *yang.Entry) map[string]string { + flags := map[string]string{} + if nd.Type == ygen.LeafNode || nd.Type == ygen.LeafListNode { + // Only leaf or leaf-list nodes can have type statements. + flags[yangTypeNameFlagKey] = field.Type.Name + } + return flags +} + // GeneratePathCode takes a slice of strings containing the path to a set of YANG // files which contain YANG modules, and a second slice of strings which // specifies the set of paths that are to be searched for associated models (e.g., @@ -249,7 +267,7 @@ func (cg *GenConfig) GeneratePathCode(yangFiles, includePaths []string) (map[str } var errs util.Errors - ir, err := ygen.GenerateIR(yangFiles, includePaths, ygen.NewGoLangMapper(true), opts) + ir, err := ygen.GenerateIR(yangFiles, includePaths, goLangMapper{GoLangMapper: ygen.NewGoLangMapper(true)}, opts) if err != nil { return nil, nil, util.AppendErr(errs, err) } @@ -660,8 +678,8 @@ func getNodeDataMap(ir *ygen.IR, fakeRootName, schemaStructPkgAccessor, pathStru } var yangTypeName string - if field.YANGDetails.Type != nil { - yangTypeName = field.YANGDetails.Type.Name + if field.Flags != nil { + yangTypeName = field.Flags[yangTypeNameFlagKey] } nodeDataMap[pathStructName] = &NodeData{ GoTypeName: goTypeName, diff --git a/ypathgen/pathgen_test.go b/ypathgen/pathgen_test.go index adce578f5..10863417b 100644 --- a/ypathgen/pathgen_test.go +++ b/ypathgen/pathgen_test.go @@ -1236,8 +1236,8 @@ func getIR() *ygen.IR { SchemaPath: "/leaf", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "ieeefloat32"}, }, + Flags: map[string]string{yangTypeNameFlagKey: "ieeefloat32"}, Type: ygen.LeafNode, LangType: &ygen.MappedType{NativeType: "Binary"}, MappedPaths: [][]string{{"leaf"}}, @@ -1257,8 +1257,8 @@ func getIR() *ygen.IR { SchemaPath: "/leaf-with-default", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "string"}, }, + Flags: map[string]string{yangTypeNameFlagKey: "string"}, Type: ygen.LeafNode, LangType: &ygen.MappedType{NativeType: "string", DefaultValue: ygot.String(`"foo"`)}, MappedPaths: [][]string{{"leaf-with-default"}}, @@ -1278,7 +1278,6 @@ func getIR() *ygen.IR { SchemaPath: "/container", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ygen.ContainerNode, LangType: nil, @@ -1299,7 +1298,6 @@ func getIR() *ygen.IR { SchemaPath: "/container-with-config", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ygen.ContainerNode, LangType: nil, @@ -1320,7 +1318,6 @@ func getIR() *ygen.IR { SchemaPath: "/list-container/list", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ygen.ListNode, LangType: nil, @@ -1342,7 +1339,6 @@ func getIR() *ygen.IR { SchemaPath: "/list-container-with-state/list-with-state", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ygen.ListNode, LangType: nil, @@ -1363,7 +1359,6 @@ func getIR() *ygen.IR { SchemaPath: "/keyless-list-container/keyless-list", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ygen.ListNode, LangType: nil, @@ -1391,9 +1386,9 @@ func getIR() *ygen.IR { SchemaPath: "/container/leaf", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "int32"}, }, - Type: ygen.LeafNode, + Flags: map[string]string{yangTypeNameFlagKey: "int32"}, + Type: ygen.LeafNode, LangType: &ygen.MappedType{ NativeType: "int32", }, @@ -1426,9 +1421,9 @@ func getIR() *ygen.IR { SchemaPath: "/container-with-config/state/leaflist", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "uint32"}, }, - Type: ygen.LeafListNode, + Flags: map[string]string{yangTypeNameFlagKey: "uint32"}, + Type: ygen.LeafListNode, LangType: &ygen.MappedType{ NativeType: "uint32", }, @@ -1461,9 +1456,9 @@ func getIR() *ygen.IR { SchemaPath: "/list-container-with-state/list-with-state/state/key", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "float64"}, }, - Type: ygen.LeafNode, + Flags: map[string]string{yangTypeNameFlagKey: "float64"}, + Type: ygen.LeafNode, LangType: &ygen.MappedType{ NativeType: "float64", }, @@ -1501,9 +1496,9 @@ func getIR() *ygen.IR { SchemaPath: "/list-container/list/key1", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "string"}, }, - Type: ygen.LeafNode, + Flags: map[string]string{yangTypeNameFlagKey: "string"}, + Type: ygen.LeafNode, LangType: &ygen.MappedType{ NativeType: "string", }, @@ -1524,9 +1519,9 @@ func getIR() *ygen.IR { SchemaPath: "/list-container/list/key2", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "binary"}, }, - Type: ygen.LeafNode, + Flags: map[string]string{yangTypeNameFlagKey: "binary"}, + Type: ygen.LeafNode, LangType: &ygen.MappedType{ NativeType: "Binary", }, @@ -1547,9 +1542,9 @@ func getIR() *ygen.IR { SchemaPath: "/list-container/list/union-key", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "union"}, }, - Type: ygen.LeafNode, + Flags: map[string]string{yangTypeNameFlagKey: "union"}, + Type: ygen.LeafNode, LangType: &ygen.MappedType{ NativeType: "RootElementModule_List_UnionKey_Union", UnionTypes: map[string]int{"string": 0, "Binary": 1}, @@ -1607,9 +1602,9 @@ func getIR() *ygen.IR { SchemaPath: "/container/leaf", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "int32"}, }, - Type: ygen.LeafNode, + Flags: map[string]string{yangTypeNameFlagKey: "int32"}, + Type: ygen.LeafNode, LangType: &ygen.MappedType{ NativeType: "int32", }, @@ -1656,7 +1651,6 @@ func TestGetNodeDataMap(t *testing.T) { SchemaPath: "/container", LeafrefTargetPath: "", Description: "", - Type: nil, }, Type: ygen.ContainerNode, LangType: nil, @@ -1684,9 +1678,9 @@ func TestGetNodeDataMap(t *testing.T) { SchemaPath: "/container/leaf", LeafrefTargetPath: "", Description: "", - Type: &ygen.YANGType{Name: "int32"}, }, - Type: ygen.LeafNode, + Flags: map[string]string{yangTypeNameFlagKey: "int32"}, + Type: ygen.LeafNode, LangType: &ygen.MappedType{ NativeType: "int32", }, From 57b04d9d4035d0b9e977874750672969aea07aa1 Mon Sep 17 00:00:00 2001 From: wenovus Date: Thu, 2 Jun 2022 12:25:06 -0700 Subject: [PATCH 04/19] Remove dependency of enumgen on MappedType --- ygen/enumgen.go | 22 ++++++++++------------ ygen/goelements.go | 30 +++++++++++++++--------------- ygen/protoelements.go | 28 ++++++++++++++++++---------- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/ygen/enumgen.go b/ygen/enumgen.go index e0daa5e04..ba8bb3a33 100644 --- a/ygen/enumgen.go +++ b/ygen/enumgen.go @@ -204,16 +204,18 @@ func (s *enumSet) enumName(e *yang.Entry, compressPaths, noUnderscores, skipDedu // a typedef which is either an identityref or an enumeration). The resolved // name is prefixed with the prefix supplied. If the type that was supplied // within the resolveTypeArgs struct is not a type definition which includes an -// enumerated type, the MappedType returned is nil, otherwise it should be -// populated. -func (s *enumSet) enumeratedTypedefTypeName(args resolveTypeArgs, prefix string, noUnderscores, useDefiningModuleForTypedefEnumNames bool) (*MappedType, error) { +// enumerated type, an empty string is returned as the first returned value; +// otherwise it should be populated. The second value returned is a string key +// that uniquely identifies this enumerated value among all possible enumerated +// values in the input set of YANG files. +func (s *enumSet) enumeratedTypedefTypeName(args resolveTypeArgs, prefix string, noUnderscores, useDefiningModuleForTypedefEnumNames bool) (string, string, error) { switch args.yangType.Kind { case yang.Yenum, yang.Yidentityref: // In the case of a typedef that specifies an enumeration or identityref // then generate a enumerated type in the Go code according to the contextEntry // which has been provided by the calling code. if args.contextEntry == nil { - return nil, fmt.Errorf("error mapping node %s due to lack of context", args.yangType.Name) + return "", "", fmt.Errorf("error mapping node %s due to lack of context", args.yangType.Name) } // If the type that is specified is not a built-in type (i.e., one of those // types which is defined in RFC6020/RFC7950) then we establish what the type @@ -221,23 +223,19 @@ func (s *enumSet) enumeratedTypedefTypeName(args resolveTypeArgs, prefix string, // the type that is specified in the schema. definingType, err := util.DefiningType(args.yangType, args.contextEntry.Type) if err != nil { - return nil, err + return "", "", err } enumIsTypedef := args.yangType.Kind == yang.Yenum && !util.IsYANGBaseType(definingType) if !util.IsYANGBaseType(args.yangType) || (useDefiningModuleForTypedefEnumNames && enumIsTypedef) { tn, key, err := s.typedefEnumeratedName(args, noUnderscores, useDefiningModuleForTypedefEnumNames) if err != nil { - return nil, err + return "", "", err } - return &MappedType{ - NativeType: fmt.Sprintf("%s%s", prefix, tn), - IsEnumeratedValue: true, - EnumeratedYANGTypeKey: key, - }, nil + return fmt.Sprintf("%s%s", prefix, tn), key, nil } } - return nil, nil + return "", "", nil } // typedefEnumeratedName retrieves the generated name of the input *yang.Entry diff --git a/ygen/goelements.go b/ygen/goelements.go index 3ec288917..416021bd0 100644 --- a/ygen/goelements.go +++ b/ygen/goelements.go @@ -310,23 +310,23 @@ func (s *GoLangMapper) SetSchemaTree(st *schemaTree) { func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (*MappedType, error) { defVal := genutil.TypeDefaultValue(args.yangType) // Handle the case of a typedef which is actually an enumeration. - mtype, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) + typedefName, _, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) if err != nil { // err is non nil when this was a typedef which included // an invalid enumerated type. return nil, err } - if mtype != nil { - // mtype is set to non-nil when this was a valid enumeration - // within a typedef. We explicitly set the zero and default values - // here. - mtype.ZeroValue = "0" - mtype.DefaultValue = defVal - // Erase this since we don't need it for Go's IR. - mtype.EnumeratedYANGTypeKey = "" - - return mtype, nil + if typedefName != "" { + return &MappedType{ + NativeType: typedefName, + IsEnumeratedValue: true, + // mtype is set to non-nil when this was a valid enumeration + // within a typedef. We explicitly set the zero and default values + // here. + ZeroValue: "0", + DefaultValue: defVal, + }, nil } // Perform the actual mapping of the type to the Go type. @@ -404,7 +404,7 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s if err != nil { return nil, err } - mtype, err = s.yangTypeToGoType(resolveTypeArgs{yangType: target.Type, contextEntry: target}, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, enumOrgPrefixesToTrim) + mtype, err := s.yangTypeToGoType(resolveTypeArgs{yangType: target.Type, contextEntry: target}, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, enumOrgPrefixesToTrim) if err != nil { return nil, err } @@ -621,13 +621,13 @@ func generateGoDefaultValue(field *yang.Entry, mtype *MappedType, gogen *GoLangM // type for each leaf is created. func (s *GoLangMapper) yangDefaultValueToGo(value string, args resolveTypeArgs, isSingletonUnion, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (string, yang.TypeKind, error) { // Handle the case of a typedef which is actually an enumeration. - mtype, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) + typedefName, _, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) if err != nil { // err is non nil when this was a typedef which included // an invalid enumerated type. return "", yang.Ynone, err } - if mtype != nil { + if typedefName != "" { if strings.Contains(value, ":") { value = strings.Split(value, ":")[1] } @@ -641,7 +641,7 @@ func (s *GoLangMapper) yangDefaultValueToGo(value string, args resolveTypeArgs, return "", yang.Ynone, fmt.Errorf("default value conversion: typedef identity value %q not found in enum with type name %q", value, args.yangType.Name) } } - return enumDefaultValue(mtype.NativeType, value, goEnumPrefix), args.yangType.Kind, nil + return enumDefaultValue(typedefName, value, goEnumPrefix), args.yangType.Kind, nil } signed := false diff --git a/ygen/protoelements.go b/ygen/protoelements.go index 93f9096e8..3cd9e8991 100644 --- a/ygen/protoelements.go +++ b/ygen/protoelements.go @@ -260,14 +260,18 @@ func yangEnumTypeToProtoType(args resolveTypeArgs) (*MappedType, error) { // for additional details as to the transformation from YANG to Protobuf. func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { // Handle typedef cases. - mtype, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) + typedefName, key, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { return nil, err } - if mtype != nil { - // mtype is set to non-nil when this was a valid enumeration - // within a typedef. - return mtype, nil + // typedefName is set to non-empty-string when this was a valid enumeration + // within a typedef. + if typedefName != "" { + return &MappedType{ + NativeType: typedefName, + IsEnumeratedValue: true, + EnumeratedYANGTypeKey: key, + }, nil } switch args.yangType.Kind { @@ -335,14 +339,18 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv // value cannot be nil/unset. func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { // Handle typedef cases. - mtype, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) + typedefName, key, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { return nil, err } - if mtype != nil { - // mtype is set to non-nil when this was a valid enumeration - // within a typedef. - return mtype, nil + // typedefName is set to non-empty-string when this was a valid enumeration + // within a typedef. + if typedefName != "" { + return &MappedType{ + NativeType: typedefName, + IsEnumeratedValue: true, + EnumeratedYANGTypeKey: key, + }, nil } switch args.yangType.Kind { case yang.Yint8, yang.Yint16, yang.Yint32, yang.Yint64: From d9055c83fdb53e847b8b8e63141d55c3c50997c8 Mon Sep 17 00:00:00 2001 From: wenovus Date: Wed, 1 Jun 2022 12:20:04 -0700 Subject: [PATCH 05/19] Move enumset and schematree into `LangMapperBase`. Currently, the individual LangMapper implementation instances (GoLangMapper and ProtoLangMapper) are required to hold these types, which can be confusing to the user since these are not fundamentally part of the LangMapper API -- rather it's their methods (i.e. enum name look-up and leafref resolution) that are part of the LangMapper API interface. Embedding a special interface (`LangMapperBase`) not only decouples the implementation from the interface, but also provides a clear forward compatibility contract of methods (incl. enum name look-up and leafref resolution) available to implementors should more functionality become available in the future. The proposed vision is: * `LangMapper`-defined methods need to be implemented by LangMapper implementors. * `LangMapperBaseSetup`-defined methods are set-up methods that are called either within `GenerateIR`, or by the unit tests of LangMapper methods. * `LangMapperBase`-defined methods NOT part of the overall `LangMapper` interface are helper methods available to `LangMapper` implementors in their method implementations. I will modify the design doc once all is agreed-upon and merged. --- ygen/goelements.go | 34 ++++++------- ygen/ir.go | 115 +++++++++++++++++++++++++++++++++++++++--- ygen/protoelements.go | 40 ++++++--------- 3 files changed, 140 insertions(+), 49 deletions(-) diff --git a/ygen/goelements.go b/ygen/goelements.go index 416021bd0..289b1cc34 100644 --- a/ygen/goelements.go +++ b/ygen/goelements.go @@ -133,12 +133,10 @@ var _ LangMapper = &GoLangMapper{} // GoLangMapper contains the functionality and state for generating Go names for // the generated code. type GoLangMapper struct { - // enumSet contains the generated enum names which can be queried. - enumSet *enumSet - - // schematree is a copy of the YANG schema tree, containing only leaf - // entries, such that schema paths can be referenced. - schematree *schemaTree + // LangMapperBase being embedded is a requirement for GoLangMapper to + // implement the LangMapper interface, and also gives it access to + // built-in methods. + LangMapperBase // definedGlobals specifies the global Go names used during code // generation to avoid conflicts. @@ -310,7 +308,7 @@ func (s *GoLangMapper) SetSchemaTree(st *schemaTree) { func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (*MappedType, error) { defVal := genutil.TypeDefaultValue(args.yangType) // Handle the case of a typedef which is actually an enumeration. - typedefName, _, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) + typedefName, _, err := s.EnumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) if err != nil { // err is non nil when this was a typedef which included // an invalid enumerated type. @@ -368,7 +366,7 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s if args.contextEntry == nil { return nil, fmt.Errorf("cannot map enum without context") } - n, _, err := s.enumSet.enumName(args.contextEntry, compressOCPaths, false, skipEnumDedup, shortenEnumLeafNames, false, enumOrgPrefixesToTrim) + n, _, err := s.EnumName(args.contextEntry, compressOCPaths, false, skipEnumDedup, shortenEnumLeafNames, false, enumOrgPrefixesToTrim) if err != nil { return nil, err } @@ -381,11 +379,11 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s case yang.Yidentityref: // Identityref leaves are mapped according to the base identity that they // refer to - this is stored in the IdentityBase field of the context leaf - // which is determined by the identityrefBaseTypeFromLeaf. + // which is determined by the IdentityrefBaseTypeFromLeaf. if args.contextEntry == nil { return nil, fmt.Errorf("cannot map identityref without context") } - n, _, err := s.enumSet.identityrefBaseTypeFromLeaf(args.contextEntry) + n, _, err := s.IdentityrefBaseTypeFromLeaf(args.contextEntry) if err != nil { return nil, err } @@ -399,8 +397,8 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s return &MappedType{NativeType: "float64", ZeroValue: goZeroValues["float64"], DefaultValue: defVal}, nil case yang.Yleafref: // This is a leafref, so we check what the type of the leaf that it - // references is by looking it up in the schematree. - target, err := s.schematree.resolveLeafrefTarget(args.yangType.Path, args.contextEntry) + // references is by looking it up. + target, err := s.ResolveLeafrefTarget(args.yangType.Path, args.contextEntry) if err != nil { return nil, err } @@ -521,7 +519,7 @@ func (s *GoLangMapper) goUnionSubTypes(subtype *yang.YangType, ctx *yang.Entry, // to map enumerated types to their module. This occurs in the case that the subtype // is an identityref - in this case, the context entry that we are carrying is the // leaf that refers to the union, not the specific subtype that is now being examined. - baseType, _, err := s.enumSet.identityrefBaseTypeFromIdentity(subtype.IdentityBase) + baseType, _, err := s.IdentityrefBaseTypeFromIdentity(subtype.IdentityBase) if err != nil { return append(errs, err) } @@ -621,7 +619,7 @@ func generateGoDefaultValue(field *yang.Entry, mtype *MappedType, gogen *GoLangM // type for each leaf is created. func (s *GoLangMapper) yangDefaultValueToGo(value string, args resolveTypeArgs, isSingletonUnion, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (string, yang.TypeKind, error) { // Handle the case of a typedef which is actually an enumeration. - typedefName, _, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) + typedefName, _, err := s.EnumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) if err != nil { // err is non nil when this was a typedef which included // an invalid enumerated type. @@ -719,7 +717,7 @@ func (s *GoLangMapper) yangDefaultValueToGo(value string, args resolveTypeArgs, if !args.yangType.Enum.IsDefined(value) { return "", yang.Ynone, fmt.Errorf("default value conversion: enum value %q not found in enum with type name %q", value, args.yangType.Name) } - n, _, err := s.enumSet.enumName(args.contextEntry, compressOCPaths, false, skipEnumDedup, shortenEnumLeafNames, false, enumOrgPrefixesToTrim) + n, _, err := s.EnumName(args.contextEntry, compressOCPaths, false, skipEnumDedup, shortenEnumLeafNames, false, enumOrgPrefixesToTrim) if err != nil { return "", yang.Ynone, err } @@ -737,15 +735,15 @@ func (s *GoLangMapper) yangDefaultValueToGo(value string, args resolveTypeArgs, if !args.yangType.IdentityBase.IsDefined(value) { return "", yang.Ynone, fmt.Errorf("default value conversion: identity value %q not found in enum with type name %q", value, args.yangType.Name) } - n, _, err := s.enumSet.identityrefBaseTypeFromIdentity(args.yangType.IdentityBase) + n, _, err := s.IdentityrefBaseTypeFromIdentity(args.yangType.IdentityBase) if err != nil { return "", yang.Ynone, err } return enumDefaultValue(n, value, ""), ykind, nil case yang.Yleafref: // This is a leafref, so we check what the type of the leaf that it - // references is by looking it up in the schematree. - target, err := s.schematree.resolveLeafrefTarget(args.yangType.Path, args.contextEntry) + // references is by looking it up. + target, err := s.ResolveLeafrefTarget(args.yangType.Path, args.contextEntry) if err != nil { return "", yang.Ynone, err } diff --git a/ygen/ir.go b/ygen/ir.go index a5d7b8236..70323f42c 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -47,6 +47,10 @@ import ( // parameter does not affect the output. Do not depend on the same order of // method calls on langMapper by GenerateIR. type LangMapper interface { + // LangMapperBaseSetup defines setup methods that are required for all + // LangMapper instances. + LangMapperBaseSetup + // FieldName maps an input yang.Entry to the name that should be used // in the intermediate representation. It is called for each field of // a defined directory. @@ -74,6 +78,17 @@ type LangMapper interface { // structs. PackageName(*yang.Entry, genutil.CompressBehaviour, bool) (string, error) + // LangMapperExt contains extensions that the LangMapper instance + // should implement if extra information from the IR is required. + // When implementing this, UnimplementedLangMapperExt should be + // embedded in the implementation type in order to ensure forward + // compatibility. + LangMapperExt +} + +// LangMapperBaseSetup defines setup methods that are required for all +// LangMapper instances. +type LangMapperBaseSetup interface { // SetEnumSet is used to supply a set of enumerated values to the // mapper such that leaves that have enumerated types can be looked up. // An enumSet provides lookup methods that allow: @@ -89,13 +104,101 @@ type LangMapper interface { // the mapped such that leaves of type leafref can be resolved to // their target leaves. SetSchemaTree(*schemaTree) +} - // LangMapperExt contains extensions that the LangMapper instance - // should implement if extra information from the IR is required. - // When implementing this, UnimplementedLangMapperExt should be - // embedded in the implementation type in order to ensure forward - // compatibility. - LangMapperExt +// LangMapperBase contains unexported base types and exported built-in methods +// that all LangMapper implementation instances should embed. These built-in +// methods are available for use anywhere in the LangMapper implementation +// instance. +type LangMapperBase struct { + // enumSet contains the generated enum names which can be queried. + enumSet *enumSet + + // schematree is a copy of the YANG schema tree, containing only leaf + // entries, such that schema paths can be referenced. + schematree *schemaTree +} + +// SetEnumSet is used to supply a set of enumerated values to the +// mapper such that leaves that have enumerated types can be looked up. +// +// NB: This method is a set-up method that the user does not need to be invoked +// within the GenerateIR context. In testing contexts outside of GenerateIR, +// however, this needs to be called prior to certain built-in methods of +// LangMapperBase are available for use. +func (s *LangMapperBase) SetEnumSet(e *enumSet) { + s.enumSet = e +} + +// SetSchemaTree is used to supply a copy of the YANG schema tree to +// the mapped such that leaves of type leafref can be resolved to +// their target leaves. +// +// NB: This method is a set-up method that the user does not need to be invoked +// within the GenerateIR context. In testing contexts outside of GenerateIR, +// however, this needs to be called prior to certain built-in methods of +// LangMapperBase are available for use. +func (s *LangMapperBase) SetSchemaTree(st *schemaTree) { + s.schematree = st +} + +// ResolveLeafrefTarget takes an input path and context entry and +// determines the type of the leaf that is referred to by the path, such that +// it can be mapped to a native language type. It returns the yang.YangType that +// is associated with the target, and the target yang.Entry, such that the +// caller can map this to the relevant language type. +// +// This function requires SetSchemaTree to be called prior to being usable. +func (b *LangMapperBase) ResolveLeafrefTarget(path string, contextEntry *yang.Entry) (*yang.Entry, error) { + return b.schematree.resolveLeafrefTarget(path, contextEntry) +} + +// EnumeratedTypedefTypeName retrieves the name of an enumerated typedef (i.e., +// a typedef which is either an identityref or an enumeration). The resolved +// name is prefixed with the prefix supplied. If the type that was supplied +// within the resolveTypeArgs struct is not a type definition which includes an +// enumerated type, the MappedType returned is nil, otherwise it should be +// populated. +// +// This function requires SetEnumSet to be called prior to being usable. +func (b *LangMapperBase) EnumeratedTypedefTypeName(args resolveTypeArgs, prefix string, noUnderscores, useDefiningModuleForTypedefEnumNames bool) (string, string, error) { + return b.enumSet.enumeratedTypedefTypeName(args, prefix, noUnderscores, useDefiningModuleForTypedefEnumNames) +} + +// EnumName retrieves the type name of the input enum *yang.Entry that will be +// used in the generated code, which is the first returned value. The second +// value returned is a string key that uniquely identifies this enumerated +// value among all possible enumerated values in the input set of YANG files. +// +// This function requires SetEnumSet to be called prior to being usable. +func (b *LangMapperBase) EnumName(e *yang.Entry, compressPaths, noUnderscores, skipDedup, shortenEnumLeafNames, addEnumeratedUnionSuffix bool, enumOrgPrefixesToTrim []string) (string, string, error) { + return b.enumSet.enumName(e, compressPaths, noUnderscores, skipDedup, shortenEnumLeafNames, addEnumeratedUnionSuffix, enumOrgPrefixesToTrim) +} + +// IdentityrefBaseTypeFromIdentity retrieves the generated type name of the +// input *yang.Identity. The first value returned is the defining module +// followed by the CamelCase-ified version of the identity's name. The second +// value returned is a string key that uniquely identifies this enumerated +// value among all possible enumerated values in the input set of YANG files. +// +// This function requires SetEnumSet to be called prior to being usable. +func (b *LangMapperBase) IdentityrefBaseTypeFromIdentity(i *yang.Identity) (string, string, error) { + return b.enumSet.identityrefBaseTypeFromIdentity(i) +} + +// IdentityrefBaseTypeFromLeaf retrieves the mapped name of an identityref's +// base such that it can be used in generated code. The first value returned is +// the defining module name followed by the CamelCase-ified version of the +// base's name. The second value returned is a string key that uniquely +// identifies this enumerated value among all possible enumerated values in the +// input set of YANG files. +// This function wraps the identityrefBaseTypeFromIdentity function since it +// covers the common case that the caller is interested in determining the name +// from an identityref leaf, rather than directly from the identity. +// +// This function requires SetEnumSet to be called prior to being usable. +func (b *LangMapperBase) IdentityrefBaseTypeFromLeaf(idr *yang.Entry) (string, string, error) { + return b.enumSet.identityrefBaseTypeFromIdentity(idr.Type.IdentityBase) } // LangMapperExt contains extensions that the LangMapper instance should diff --git a/ygen/protoelements.go b/ygen/protoelements.go index 3cd9e8991..aeb21dad2 100644 --- a/ygen/protoelements.go +++ b/ygen/protoelements.go @@ -24,14 +24,17 @@ import ( "github.com/openconfig/ygot/util" ) +// Ensure at compile time that the ProtoLangMapper implements the LangMapper interface. +var _ LangMapper = &ProtoLangMapper{} + // ProtoLangMapper contains the functionality and state for generating proto // names for the generated code. type ProtoLangMapper struct { - // enumSet contains the generated enum names which can be queried. - enumSet *enumSet - // schematree is a copy of the YANG schema tree, containing only leaf - // entries, such that schema paths can be referenced. - schematree *schemaTree + // LangMapperBase being embedded is a requirement for ProtoLangMapper + // to implement the LangMapper interface, and also gives it access to + // built-in methods. + LangMapperBase + // definedGlobals specifies the global proto names used during code // generation to avoid conflicts. definedGlobals map[string]bool @@ -185,19 +188,6 @@ func (s *ProtoLangMapper) PackageName(e *yang.Entry, compressBehaviour genutil.C return s.protobufPackage(e, compressPaths), nil } -// SetEnumSet is used to supply a set of enumerated values to the -// mapper such that leaves that have enumerated types can be looked up. -func (s *ProtoLangMapper) SetEnumSet(e *enumSet) { - s.enumSet = e -} - -// SetSchemaTree is used to supply a copy of the YANG schema tree to -// the mapped such that leaves of type leafref can be resolved to -// their target leaves. -func (s *ProtoLangMapper) SetSchemaTree(st *schemaTree) { - s.schematree = st -} - // resolveProtoTypeArgs specifies input parameters required for resolving types // from YANG to protobuf. // TODO(robjs): Consider embedding resolveProtoTypeArgs in this struct per @@ -260,7 +250,7 @@ func yangEnumTypeToProtoType(args resolveTypeArgs) (*MappedType, error) { // for additional details as to the transformation from YANG to Protobuf. func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { // Handle typedef cases. - typedefName, key, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) + typedefName, key, err := s.EnumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { return nil, err } @@ -290,7 +280,7 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv case yang.Yleafref: // We look up the leafref in the schema tree to be able to // determine what type to map to. - target, err := s.schematree.resolveLeafrefTarget(args.yangType.Path, args.contextEntry) + target, err := s.ResolveLeafrefTarget(args.yangType.Path, args.contextEntry) if err != nil { return nil, err } @@ -300,7 +290,7 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv if err != nil { return nil, err } - _, key, err := s.enumSet.enumName(args.contextEntry, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, false, opts.TransformationOptions.EnumOrgPrefixesToTrim) + _, key, err := s.EnumName(args.contextEntry, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, false, opts.TransformationOptions.EnumOrgPrefixesToTrim) if err != nil { return nil, err } @@ -339,7 +329,7 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv // value cannot be nil/unset. func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { // Handle typedef cases. - typedefName, key, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) + typedefName, key, err := s.EnumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { return nil, err } @@ -368,7 +358,7 @@ func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs // as there is not an equivalent Protobuf type. return &MappedType{NativeType: ywrapperAccessor + "Decimal64Value"}, nil case yang.Yleafref: - target, err := s.schematree.resolveLeafrefTarget(args.yangType.Path, args.contextEntry) + target, err := s.ResolveLeafrefTarget(args.yangType.Path, args.contextEntry) if err != nil { return nil, err } @@ -378,7 +368,7 @@ func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs if err != nil { return nil, err } - _, key, err := s.enumSet.enumName(args.contextEntry, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, false, opts.TransformationOptions.EnumOrgPrefixesToTrim) + _, key, err := s.EnumName(args.contextEntry, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, false, opts.TransformationOptions.EnumOrgPrefixesToTrim) if err != nil { return nil, err } @@ -623,7 +613,7 @@ func (s *ProtoLangMapper) protobufPackage(e *yang.Entry, compressPaths bool) str // protoIdentityName returns the name that should be used for an identityref base. func (s *ProtoLangMapper) protoIdentityName(pargs resolveProtoTypeArgs, i *yang.Identity) (string, string, error) { - n, key, err := s.enumSet.identityrefBaseTypeFromIdentity(i) + n, key, err := s.IdentityrefBaseTypeFromIdentity(i) if err != nil { return "", "", err } From 7c549e74b3f7e47a97454e179cf3bca2f4b5cb46 Mon Sep 17 00:00:00 2001 From: wenovus Date: Fri, 3 Jun 2022 10:47:16 -0700 Subject: [PATCH 06/19] Add `SetupEnumSet` and `SetupSchemaTree` to `LangMapperBase`. This allows unit tests to be created by custom LangMappers on their methods (e.g. `FieldName`, `KeyLeafType`). --- ygen/codegen.go | 4 +-- ygen/genir.go | 4 +-- ygen/genstate_test.go | 33 ++++++++--------- ygen/goelements.go | 13 ------- ygen/goelements_test.go | 40 ++++++++------------- ygen/ir.go | 72 +++++++++++++++++++++++++++++++------- ygen/protoelements_test.go | 18 ++++------ 7 files changed, 100 insertions(+), 84 deletions(-) diff --git a/ygen/codegen.go b/ygen/codegen.go index 6595e8dfc..315cb0695 100644 --- a/ygen/codegen.go +++ b/ygen/codegen.go @@ -709,8 +709,8 @@ func (dcg *DirectoryGenConfig) GetDirectoriesAndLeafTypes(yangFiles, includePath // Store the returned schematree and enumSet within the state for this code generation. gogen := NewGoLangMapper(cg.GoOptions.GenerateSimpleUnions) - gogen.SetEnumSet(enumSet) - gogen.SetSchemaTree(mdef.schematree) + gogen.setEnumSet(enumSet) + gogen.setSchemaTree(mdef.schematree) directoryMap, errs := buildDirectoryDefinitions(gogen, mdef.directoryEntries, opts) if errs != nil { diff --git a/ygen/genir.go b/ygen/genir.go index b83449644..866445006 100644 --- a/ygen/genir.go +++ b/ygen/genir.go @@ -84,8 +84,8 @@ func GenerateIR(yangFiles, includePaths []string, langMapper LangMapper, opts IR return nil, errs } - langMapper.SetEnumSet(enumSet) - langMapper.SetSchemaTree(mdef.schematree) + langMapper.setEnumSet(enumSet) + langMapper.setSchemaTree(mdef.schematree) directoryMap, errs := buildDirectoryDefinitions(langMapper, mdef.directoryEntries, opts) if errs != nil { diff --git a/ygen/genstate_test.go b/ygen/genstate_test.go index 88c504d2e..a6f2fdd91 100644 --- a/ygen/genstate_test.go +++ b/ygen/genstate_test.go @@ -1357,14 +1357,14 @@ func TestBuildDirectoryDefinitions(t *testing.T) { } t.Run(fmt.Sprintf("%s:buildDirectoryDefinitions(CompressBehaviour:%v,Language:%s,excludeState:%v)", tt.name, c.compressBehaviour, langName(c.lang), c.excludeState), func(t *testing.T) { - st, err := buildSchemaTree(tt.in) - if err != nil { + gogen := NewGoLangMapper(true) + if err := gogen.SetupSchemaTree(tt.in); err != nil { t.Fatalf("buildSchemaTree(%v), got unexpected err: %v", tt.in, err) } - gogen := NewGoLangMapper(true) - gogen.SetSchemaTree(st) protogen := NewProtoLangMapper(DefaultBasePackageName, DefaultEnumPackageName) - protogen.SetSchemaTree(st) + if err := protogen.SetupSchemaTree(tt.in); err != nil { + t.Fatalf("buildSchemaTree(%v), got unexpected err: %v", tt.in, err) + } structs := make(map[string]*yang.Entry) enums := make(map[string]*yang.Entry) @@ -2250,25 +2250,22 @@ func TestBuildListKey(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var st *schemaTree - if tt.inEntries != nil { - var err error - if st, err = buildSchemaTree(tt.inEntries); err != nil { - t.Fatalf("%s: buildSchemaTree(%v), could not build tree: %v", tt.name, tt.inEntries, err) - } - } + s := NewGoLangMapper(true) + enumMap := enumMapFromEntries(tt.inEnumEntries) addEnumsToEnumMap(tt.in, enumMap) - enumSet, _, errs := findEnumSet(enumMap, tt.inCompress, false, tt.inSkipEnumDedup, true, true, true, nil) - if errs != nil { + if err := s.SetupEnumSet(enumMap, tt.inCompress, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { if !tt.wantErr { - t.Errorf("findEnumSet failed: %v", errs) + t.Errorf("SetupEnumSet failed: %v", err) } return } - s := NewGoLangMapper(true) - s.SetEnumSet(enumSet) - s.SetSchemaTree(st) + + if tt.inEntries != nil { + if err := s.SetupSchemaTree(tt.inEntries); err != nil { + t.Fatalf("%s: SetupSchemaTree(%v), could not build tree: %v", tt.name, tt.inEntries, err) + } + } compressBehaviour := genutil.Uncompressed if tt.inCompress { diff --git a/ygen/goelements.go b/ygen/goelements.go index 289b1cc34..0e374d0fd 100644 --- a/ygen/goelements.go +++ b/ygen/goelements.go @@ -281,19 +281,6 @@ func (s *GoLangMapper) PackageName(*yang.Entry, genutil.CompressBehaviour, bool) return "", nil } -// SetEnumSet is used to supply a set of enumerated values to the -// mapper such that leaves that have enumerated types can be looked up. -func (s *GoLangMapper) SetEnumSet(e *enumSet) { - s.enumSet = e -} - -// SetSchemaTree is used to supply a copy of the YANG schema tree to -// the mapped such that leaves of type leafref can be resolved to -// their target leaves. -func (s *GoLangMapper) SetSchemaTree(st *schemaTree) { - s.schematree = st -} - // yangTypeToGoType takes a yang.YangType (YANG type definition) and maps it // to the type that should be used to represent it in the generated Go code. // A resolveTypeArgs structure is used as the input argument which specifies a diff --git a/ygen/goelements_test.go b/ygen/goelements_test.go index a1660edea..b841c0426 100644 --- a/ygen/goelements_test.go +++ b/ygen/goelements_test.go @@ -440,12 +440,10 @@ func TestUnionSubTypes(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - enumSet, _, errs := findEnumSet(enumMapFromEntry(tt.inCtxEntry), false, false, false, true, true, true, nil) - if errs != nil { - t.Fatal(errs) - } s := NewGoLangMapper(true) - s.SetEnumSet(enumSet) + if err := s.SetupEnumSet(enumMapFromEntry(tt.inCtxEntry), false, false, false, true, true, true, nil); err != nil { + t.Fatal(err) + } mtypes := make(map[int]*MappedType) ctypes := make(map[string]int) @@ -1067,17 +1065,15 @@ func TestYangTypeToGoType(t *testing.T) { tt.in = tt.ctx.Type } + s := NewGoLangMapper(true) enumMap := enumMapFromEntries(tt.inEnumEntries) addEnumsToEnumMap(tt.ctx, enumMap) - enumSet, _, errs := findEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil) - if errs != nil { + if err := s.SetupEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { if !tt.wantErr { - t.Errorf("findEnumSet failed: %v", errs) + t.Errorf("findEnumSet failed: %v", err) } return } - s := NewGoLangMapper(true) - s.SetEnumSet(enumSet) if tt.inEntries != nil { st, err := buildSchemaTree(tt.inEntries) @@ -1448,12 +1444,10 @@ func TestTypeResolutionManyToOne(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - enumSet, _, errs := findEnumSet(enumMapFromEntries(tt.inLeaves), tt.inCompressOCPaths, false, tt.inSkipEnumDedup, true, true, true, nil) - if errs != nil { - t.Fatalf("findEnumSet failed: %v", errs) - } s := NewGoLangMapper(true) - s.SetEnumSet(enumSet) + if err := s.SetupEnumSet(enumMapFromEntries(tt.inLeaves), tt.inCompressOCPaths, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { + t.Fatalf("findEnumSet failed: %v", err) + } gotTypes := make(map[string]*MappedType) for _, leaf := range tt.inLeaves { @@ -2452,17 +2446,15 @@ func TestYangDefaultValueToGo(t *testing.T) { // --- Test --- t.Run(tt.name, func(t *testing.T) { + s := NewGoLangMapper(true) enumMap := enumMapFromEntries(tt.inEnumEntries) addEnumsToEnumMap(tt.inCtx, enumMap) - enumSet, _, errs := findEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil) - if errs != nil { + if err := s.SetupEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { if !tt.wantErr { - t.Errorf("findEnumSet failed: %v", errs) + t.Errorf("findEnumSet failed: %v", err) } return } - s := NewGoLangMapper(true) - s.SetEnumSet(enumSet) if tt.inEntries != nil { st, err := buildSchemaTree(tt.inEntries) @@ -2813,17 +2805,15 @@ func TestYangDefaultValueToGo(t *testing.T) { // --- Test --- t.Run("singleton union "+tt.name, func(t *testing.T) { + s := NewGoLangMapper(true) enumMap := enumMapFromEntries(tt.inEnumEntries) addEnumsToEnumMap(tt.inCtx, enumMap) - enumSet, _, errs := findEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil) - if errs != nil { + if err := s.SetupEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { if !tt.wantErr { - t.Errorf("findEnumSet failed: %v", errs) + t.Errorf("findEnumSet failed: %v", err) } return } - s := NewGoLangMapper(true) - s.SetEnumSet(enumSet) if tt.inEntries != nil { st, err := buildSchemaTree(tt.inEntries) diff --git a/ygen/ir.go b/ygen/ir.go index 70323f42c..d727ed81b 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -89,7 +89,7 @@ type LangMapper interface { // LangMapperBaseSetup defines setup methods that are required for all // LangMapper instances. type LangMapperBaseSetup interface { - // SetEnumSet is used to supply a set of enumerated values to the + // setEnumSet is used to supply a set of enumerated values to the // mapper such that leaves that have enumerated types can be looked up. // An enumSet provides lookup methods that allow: // - simple enumerated types @@ -98,12 +98,24 @@ type LangMapperBaseSetup interface { // - identityrefs within typedefs // to be resolved to the corresponding type that is to be used in // the IR. - SetEnumSet(*enumSet) + setEnumSet(*enumSet) - // SetSchemaTree is used to supply a copy of the YANG schema tree to + // setSchemaTree is used to supply a copy of the YANG schema tree to // the mapped such that leaves of type leafref can be resolved to // their target leaves. - SetSchemaTree(*schemaTree) + setSchemaTree(*schemaTree) + + // SetupEnumSet is intended to be called by unit tests in order to set up the + // LangMapperBase such that generated enumeration/identity names can be looked + // up. The input parameters correspond to fields in IROptions. + // It returns an error if there is a failure to generate the enumerated + // values' names. + SetupEnumSet(entries map[string]*yang.Entry, compressPaths, noUnderscores, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, appendEnumSuffixForSimpleUnionEnums bool, enumOrgPrefixesToTrim []string) error + + // SetupSchemaTree is intended to be called by unit tests in order to set up + // the LangMapperBase such that leafrefs targets may be looked up. + // It returns an error if there is duplication within the set of entries. + SetupSchemaTree(entries []*yang.Entry) error } // LangMapperBase contains unexported base types and exported built-in methods @@ -119,18 +131,18 @@ type LangMapperBase struct { schematree *schemaTree } -// SetEnumSet is used to supply a set of enumerated values to the +// setEnumSet is used to supply a set of enumerated values to the // mapper such that leaves that have enumerated types can be looked up. // // NB: This method is a set-up method that the user does not need to be invoked // within the GenerateIR context. In testing contexts outside of GenerateIR, // however, this needs to be called prior to certain built-in methods of // LangMapperBase are available for use. -func (s *LangMapperBase) SetEnumSet(e *enumSet) { +func (s *LangMapperBase) setEnumSet(e *enumSet) { s.enumSet = e } -// SetSchemaTree is used to supply a copy of the YANG schema tree to +// setSchemaTree is used to supply a copy of the YANG schema tree to // the mapped such that leaves of type leafref can be resolved to // their target leaves. // @@ -138,17 +150,47 @@ func (s *LangMapperBase) SetEnumSet(e *enumSet) { // within the GenerateIR context. In testing contexts outside of GenerateIR, // however, this needs to be called prior to certain built-in methods of // LangMapperBase are available for use. -func (s *LangMapperBase) SetSchemaTree(st *schemaTree) { +func (s *LangMapperBase) setSchemaTree(st *schemaTree) { s.schematree = st } +// SetupEnumSet is intended to be called by unit tests in order to set up the +// LangMapperBase such that generated enumeration/identity names can be looked +// up. It walks the input map of enumerated value leaves keyed by path and +// creates generates names for them. The input parameters correspond to fields +// in IROptions. +// It returns an error if there is a failure to generate the enumerated values' +// names. +func (s *LangMapperBase) SetupEnumSet(entries map[string]*yang.Entry, compressPaths, noUnderscores, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, appendEnumSuffixForSimpleUnionEnums bool, enumOrgPrefixesToTrim []string) error { + enumSet, _, errs := findEnumSet(entries, compressPaths, noUnderscores, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, appendEnumSuffixForSimpleUnionEnums, enumOrgPrefixesToTrim) + if errs != nil { + return fmt.Errorf("%v", errs) + } + s.setEnumSet(enumSet) + return nil +} + +// SetupSchemaTree is intended to be called by unit tests in order to set up +// the LangMapperBase such that leafrefs targets may be looked up. It maps a +// set of yang.Entry pointers into a ctree structure. +// It returns an error if there is duplication within the set of entries. +func (s *LangMapperBase) SetupSchemaTree(entries []*yang.Entry) error { + schematree, err := buildSchemaTree(entries) + if err != nil { + return err + } + s.setSchemaTree(schematree) + return nil +} + // ResolveLeafrefTarget takes an input path and context entry and // determines the type of the leaf that is referred to by the path, such that // it can be mapped to a native language type. It returns the yang.YangType that // is associated with the target, and the target yang.Entry, such that the // caller can map this to the relevant language type. // -// This function requires SetSchemaTree to be called prior to being usable. +// In testing contexts, this function requires SetupSchemaTree to be called +// prior to being usable. func (b *LangMapperBase) ResolveLeafrefTarget(path string, contextEntry *yang.Entry) (*yang.Entry, error) { return b.schematree.resolveLeafrefTarget(path, contextEntry) } @@ -160,7 +202,8 @@ func (b *LangMapperBase) ResolveLeafrefTarget(path string, contextEntry *yang.En // enumerated type, the MappedType returned is nil, otherwise it should be // populated. // -// This function requires SetEnumSet to be called prior to being usable. +// In testing contexts, this function requires SetupEnumSet to be called prior +// to being usable. func (b *LangMapperBase) EnumeratedTypedefTypeName(args resolveTypeArgs, prefix string, noUnderscores, useDefiningModuleForTypedefEnumNames bool) (string, string, error) { return b.enumSet.enumeratedTypedefTypeName(args, prefix, noUnderscores, useDefiningModuleForTypedefEnumNames) } @@ -170,7 +213,8 @@ func (b *LangMapperBase) EnumeratedTypedefTypeName(args resolveTypeArgs, prefix // value returned is a string key that uniquely identifies this enumerated // value among all possible enumerated values in the input set of YANG files. // -// This function requires SetEnumSet to be called prior to being usable. +// In testing contexts, this function requires SetupEnumSet to be called prior +// to being usable. func (b *LangMapperBase) EnumName(e *yang.Entry, compressPaths, noUnderscores, skipDedup, shortenEnumLeafNames, addEnumeratedUnionSuffix bool, enumOrgPrefixesToTrim []string) (string, string, error) { return b.enumSet.enumName(e, compressPaths, noUnderscores, skipDedup, shortenEnumLeafNames, addEnumeratedUnionSuffix, enumOrgPrefixesToTrim) } @@ -181,7 +225,8 @@ func (b *LangMapperBase) EnumName(e *yang.Entry, compressPaths, noUnderscores, s // value returned is a string key that uniquely identifies this enumerated // value among all possible enumerated values in the input set of YANG files. // -// This function requires SetEnumSet to be called prior to being usable. +// In testing contexts, this function requires SetupEnumSet to be called prior +// to being usable. func (b *LangMapperBase) IdentityrefBaseTypeFromIdentity(i *yang.Identity) (string, string, error) { return b.enumSet.identityrefBaseTypeFromIdentity(i) } @@ -196,7 +241,8 @@ func (b *LangMapperBase) IdentityrefBaseTypeFromIdentity(i *yang.Identity) (stri // covers the common case that the caller is interested in determining the name // from an identityref leaf, rather than directly from the identity. // -// This function requires SetEnumSet to be called prior to being usable. +// In testing contexts, this function requires SetupEnumSet to be called prior +// to being usable. func (b *LangMapperBase) IdentityrefBaseTypeFromLeaf(idr *yang.Entry) (string, string, error) { return b.enumSet.identityrefBaseTypeFromIdentity(idr.Type.IdentityBase) } diff --git a/ygen/protoelements_test.go b/ygen/protoelements_test.go index 82c80728b..3f14f52ad 100644 --- a/ygen/protoelements_test.go +++ b/ygen/protoelements_test.go @@ -584,31 +584,27 @@ func TestYangTypeToProtoType(t *testing.T) { rpt = *tt.inResolveProtoTypeArgs } + s := NewProtoLangMapper(DefaultBasePackageName, DefaultEnumPackageName) // Seed the schema tree with the injected entries, used to ensure leafrefs can // be resolved. - var st *schemaTree if tt.inEntries != nil { - var err error - if st, err = buildSchemaTree(tt.inEntries); err != nil { - t.Fatalf("%s: buildSchemaTree(%v): got unexpected error, got: %v, want: nil", tt.name, tt.inEntries, err) + if err := s.SetupSchemaTree(tt.inEntries); err != nil { + t.Fatalf("%s: SetupSchemaTree(%v): got unexpected error, got: %v, want: nil", tt.name, tt.inEntries, err) } } + // Seed the enumSet with the injected enum entries, + // used to ensure that enum names can be resolved. enumMap := enumMapFromArgs(tt.in) for _, e := range enumMapFromEntries(tt.inEntries) { addEnumsToEnumMap(e, enumMap) } - enumSet, _, errs := findEnumSet(enumMap, false, true, false, true, true, true, nil) - if errs != nil { + if err := s.SetupEnumSet(enumMap, false, true, false, true, true, true, nil); err != nil { if !tt.wantErr { - t.Errorf("findEnumSet failed: %v", errs) + t.Errorf("SetupEnumSet failed: %v", err) } return } - s := NewProtoLangMapper(DefaultBasePackageName, DefaultEnumPackageName) - s.SetSchemaTree(st) - s.SetEnumSet(enumSet) - for _, st := range tt.in { gotWrapper, err := s.yangTypeToProtoType(st, rpt, IROptions{ TransformationOptions: TransformationOpts{ From 7fbf31dcc4e780861076fabe34f9f64f309cc5ed Mon Sep 17 00:00:00 2001 From: wenovus Date: Fri, 3 Jun 2022 17:48:27 -0700 Subject: [PATCH 07/19] Delete `YANGCodeGenerator.GetDirectoriesAndLeafTypes` --- ygen/codegen.go | 84 ------- ygen/codegen_test.go | 556 ------------------------------------------- 2 files changed, 640 deletions(-) diff --git a/ygen/codegen.go b/ygen/codegen.go index 315cb0695..dd4f7ed00 100644 --- a/ygen/codegen.go +++ b/ygen/codegen.go @@ -667,90 +667,6 @@ func writeGoEnumeratedTypes(enums map[string]*goEnumeratedType, usedEnums map[st }, nil } -// GetDirectoriesAndLeafTypes parses YANG files and returns two path-keyed -// maps. The first contains Directory entries that is the intermediate -// representation used by ygen for subsequent code generation, and the second -// contains the *MappedType for each field of the same corresponding Directory -// entries, with non-leafs having a nil value. This representation may be -// useful to external code generation libraries that make use of such -// information, e.g. if their output code depends on a ygen-generated type. -// yangFiles is a slice of strings containing the path to a set of YANG files -// which contain YANG modules, includePaths is slice of strings which specifies -// the set of paths that are to be searched for associated models (e.g., -// modules that are included by the specified set of modules, or submodules of -// those modules). Any errors encountered during code generation are returned. -func (dcg *DirectoryGenConfig) GetDirectoriesAndLeafTypes(yangFiles, includePaths []string) (map[string]*Directory, map[string]map[string]*MappedType, util.Errors) { - if !dcg.TransformationOptions.CompressBehaviour.CompressEnabled() { - return nil, nil, util.Errors{fmt.Errorf("GetDirectoriesAndLeafTypes currently does not have unit tests for when compression is disabled; if support needed, add unit tests and remove this error")} - } - - cg := &GeneratorConfig{ParseOptions: dcg.ParseOptions, TransformationOptions: dcg.TransformationOptions, GoOptions: dcg.GoOptions} - - opts := IROptions{ - ParseOptions: cg.ParseOptions, - TransformationOptions: cg.TransformationOptions, - NestedDirectories: false, - AbsoluteMapPaths: false, - AppendEnumSuffixForSimpleUnionEnums: cg.GoOptions.AppendEnumSuffixForSimpleUnionEnums, - } - - // Extract the entities to be mapped into structs and enumerations in the output - // Go code. Extract the schematree from the modules provided such that it can be - // used to reference entities within the tree. - mdef, errs := mappedDefinitions(yangFiles, includePaths, cg) - if errs != nil { - return nil, nil, errs - } - - enumSet, _, errs := findEnumSet(mdef.enumEntries, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.AppendEnumSuffixForSimpleUnionEnums, opts.TransformationOptions.EnumOrgPrefixesToTrim) - if errs != nil { - return nil, nil, errs - } - - // Store the returned schematree and enumSet within the state for this code generation. - gogen := NewGoLangMapper(cg.GoOptions.GenerateSimpleUnions) - gogen.setEnumSet(enumSet) - gogen.setSchemaTree(mdef.schematree) - - directoryMap, errs := buildDirectoryDefinitions(gogen, mdef.directoryEntries, opts) - if errs != nil { - return nil, nil, errs - } - - // Alphabetically order directories to produce deterministic output. - orderedDirNames, dirNameMap, err := GetOrderedDirectories(directoryMap) - if err != nil { - return nil, nil, util.AppendErr(errs, err) - } - - // Populate map of leaf types for returning. - leafTypeMap := make(map[string]map[string]*MappedType, len(directoryMap)) - for _, directoryName := range orderedDirNames { - dir := dirNameMap[directoryName] - path := dir.Entry.Path() - leafTypeMap[path] = make(map[string]*MappedType, len(dir.Fields)) - // Alphabetically order fields to produce deterministic output. - for _, fieldName := range GetOrderedFieldNames(dir) { - field := dir.Fields[fieldName] - if isLeaf := field.IsLeaf() || field.IsLeafList(); isLeaf { - mtype, err := gogen.yangTypeToGoType(resolveTypeArgs{yangType: field.Type, contextEntry: field}, dcg.TransformationOptions.CompressBehaviour.CompressEnabled(), cg.ParseOptions.SkipEnumDeduplication, cg.TransformationOptions.ShortenEnumLeafNames, cg.TransformationOptions.UseDefiningModuleForTypedefEnumNames, cg.TransformationOptions.EnumOrgPrefixesToTrim) - if err != nil { - errs = util.AppendErr(errs, err) - continue - } - leafTypeMap[path][fieldName] = mtype - } else { - leafTypeMap[path][fieldName] = nil - } - } - } - - if errs != nil { - return nil, nil, errs - } - return directoryMap, leafTypeMap, nil -} - // GenerateProto3 generates Protobuf 3 code for the input set of YANG files. // The YANG schemas for which protobufs are to be created is supplied as the // yangFiles argument, with included modules being searched for in includePaths. diff --git a/ygen/codegen_test.go b/ygen/codegen_test.go index 04016c0d4..5626e0d6e 100644 --- a/ygen/codegen_test.go +++ b/ygen/codegen_test.go @@ -24,7 +24,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "github.com/kylelemons/godebug/pretty" "github.com/openconfig/gnmi/errdiff" "github.com/openconfig/goyang/pkg/yang" @@ -1412,561 +1411,6 @@ func TestGenerateErrs(t *testing.T) { } } -func TestGetDirectoriesAndLeafTypes(t *testing.T) { - tests := []struct { - name string - inFiles []string - inIncludePaths []string - inConfig *DirectoryGenConfig - wantDirMap map[string]*Directory - wantFieldPath map[string]map[string]string - wantTypeMap map[string]map[string]*MappedType - }{{ - name: "simple openconfig test", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inIncludePaths: []string{filepath.Join(TestRoot, "testdata", "structs")}, - inConfig: &DirectoryGenConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - ParseOptions: ParseOpts{ - ExcludeModules: []string{}, - }, - }, - wantDirMap: map[string]*Directory{ - "/openconfig-simple/parent": { - Name: "Parent", - Fields: map[string]*yang.Entry{ - "child": {Name: "child", Type: nil}, - }, - Path: []string{"", "openconfig-simple", "parent"}, - }, - "/openconfig-simple/parent/child": { - Name: "Parent_Child", - Fields: map[string]*yang.Entry{ - "one": {Name: "one", Type: &yang.YangType{Kind: yang.Ystring}}, - "two": {Name: "two", Type: &yang.YangType{Kind: yang.Ystring}}, - "three": {Name: "three", Type: &yang.YangType{Kind: yang.Yenum}}, - "four": {Name: "four", Type: &yang.YangType{Kind: yang.Ybinary}}, - }, - Path: []string{"", "openconfig-simple", "parent", "child"}, - }, - "/openconfig-simple/remote-container": { - Name: "RemoteContainer", - Fields: map[string]*yang.Entry{ - "a-leaf": {Name: "a-leaf", Type: &yang.YangType{Kind: yang.Ystring}}, - }, - Path: []string{"", "openconfig-simple", "remote-container"}, - }, - }, - wantFieldPath: map[string]map[string]string{ - "/openconfig-simple/parent": { - "child": "/openconfig-simple/parent/child", - }, - "/openconfig-simple/parent/child": { - "one": "/openconfig-simple/parent/child/config/one", - "two": "/openconfig-simple/parent/child/state/two", - "three": "/openconfig-simple/parent/child/config/three", - "four": "/openconfig-simple/parent/child/config/four", - }, - "/openconfig-simple/remote-container": { - "a-leaf": "/openconfig-simple/remote-container/config/a-leaf", - }, - }, - wantTypeMap: map[string]map[string]*MappedType{ - "/openconfig-simple/parent": { - "child": nil, - }, - "/openconfig-simple/parent/child": { - "one": {NativeType: "string"}, - "two": {NativeType: "string"}, - "three": {NativeType: "E_Child_Three", IsEnumeratedValue: true}, - "four": {NativeType: "Binary"}, - }, - "/openconfig-simple/remote-container": { - "a-leaf": {NativeType: "string"}, - }, - }, - }, { - name: "simple openconfig test with state prioritized", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inIncludePaths: []string{filepath.Join(TestRoot, "testdata", "structs")}, - inConfig: &DirectoryGenConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferOperationalState, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - ParseOptions: ParseOpts{ - ExcludeModules: []string{}, - }, - }, - wantDirMap: map[string]*Directory{ - "/openconfig-simple/parent": { - Name: "Parent", - Fields: map[string]*yang.Entry{ - "child": {Name: "child", Type: nil}, - }, - Path: []string{"", "openconfig-simple", "parent"}, - }, - "/openconfig-simple/parent/child": { - Name: "Parent_Child", - Fields: map[string]*yang.Entry{ - "one": {Name: "one", Type: &yang.YangType{Kind: yang.Ystring}}, - "two": {Name: "two", Type: &yang.YangType{Kind: yang.Ystring}}, - "three": {Name: "three", Type: &yang.YangType{Kind: yang.Yenum}}, - "four": {Name: "four", Type: &yang.YangType{Kind: yang.Ybinary}}, - }, - Path: []string{"", "openconfig-simple", "parent", "child"}, - }, - "/openconfig-simple/remote-container": { - Name: "RemoteContainer", - Fields: map[string]*yang.Entry{ - "a-leaf": {Name: "a-leaf", Type: &yang.YangType{Kind: yang.Ystring}}, - }, - Path: []string{"", "openconfig-simple", "remote-container"}, - }, - }, - wantFieldPath: map[string]map[string]string{ - "/openconfig-simple/parent": { - "child": "/openconfig-simple/parent/child", - }, - "/openconfig-simple/parent/child": { - "one": "/openconfig-simple/parent/child/state/one", - "two": "/openconfig-simple/parent/child/state/two", - "three": "/openconfig-simple/parent/child/state/three", - "four": "/openconfig-simple/parent/child/state/four", - }, - "/openconfig-simple/remote-container": { - "a-leaf": "/openconfig-simple/remote-container/state/a-leaf", - }, - }, - wantTypeMap: map[string]map[string]*MappedType{ - "/openconfig-simple/parent": { - "child": nil, - }, - "/openconfig-simple/parent/child": { - "one": {NativeType: "string"}, - "two": {NativeType: "string"}, - "three": {NativeType: "E_Child_Three", IsEnumeratedValue: true}, - "four": {NativeType: "Binary"}, - }, - "/openconfig-simple/remote-container": { - "a-leaf": {NativeType: "string"}, - }, - }, - }, { - name: "enum openconfig test with enum-types module excluded", - inFiles: []string{filepath.Join(datapath, "enum-module.yang")}, - inIncludePaths: []string{filepath.Join(TestRoot, "testdata", "structs")}, - inConfig: &DirectoryGenConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - ParseOptions: ParseOpts{ - ExcludeModules: []string{"enum-types"}, - }, - }, - wantDirMap: map[string]*Directory{ - "/enum-module/parent": { - Name: "Parent", - Fields: map[string]*yang.Entry{ - "child": {Name: "child", Type: nil}, - }, - Path: []string{"", "enum-module", "parent"}, - }, - "/enum-module/c": { - Name: "C", - Fields: map[string]*yang.Entry{ - "cl": {Name: "cl", Type: &yang.YangType{Kind: yang.Yenum}}, - }, - Path: []string{"", "enum-module", "c"}, - }, - "/enum-module/parent/child": { - Name: "Parent_Child", - Fields: map[string]*yang.Entry{ - "id": {Name: "id", Type: &yang.YangType{Kind: yang.Yidentityref}}, - "enum": {Name: "enum", Type: &yang.YangType{Kind: yang.Yenum}}, - "id2": {Name: "id2", Type: &yang.YangType{Kind: yang.Yidentityref}}, - "inline-enum": {Name: "inline-enum", Type: &yang.YangType{Kind: yang.Yenum}}, - }, - Path: []string{"", "enum-module", "parent", "child"}, - }, - "/enum-module/a-lists/a-list": { - Name: "AList", - Fields: map[string]*yang.Entry{ - "value": {Name: "value", Type: &yang.YangType{Kind: yang.Yunion}}, - }, - Path: []string{"", "enum-module", "a-lists", "a-list"}, - }, - "/enum-module/b-lists/b-list": { - Name: "BList", - Fields: map[string]*yang.Entry{ - "value": {Name: "value", Type: &yang.YangType{Kind: yang.Yunion}}, - }, - Path: []string{"", "enum-module", "b-lists", "b-list"}, - }, - }, - wantTypeMap: map[string]map[string]*MappedType{ - "/enum-module/parent": { - "child": nil, - }, - "/enum-module/c": { - "cl": {NativeType: "E_EnumModule_Cl", IsEnumeratedValue: true}, - }, - "/enum-module/parent/child": { - "id": {NativeType: "E_EnumTypes_ID", IsEnumeratedValue: true}, - "enum": {NativeType: "E_EnumTypes_TdEnum", IsEnumeratedValue: true}, - "id2": {NativeType: "E_EnumTypes_ID", IsEnumeratedValue: true}, - "inline-enum": {NativeType: "E_Child_InlineEnum", IsEnumeratedValue: true}, - }, - "/enum-module/a-lists/a-list": { - "value": {NativeType: "AList_Value_Union"}, - }, - "/enum-module/b-lists/b-list": { - "value": {NativeType: "BList_Value_Union"}, - }, - }, - }, { - name: "enum openconfig test with enum-types module and state excluded", - inFiles: []string{filepath.Join(datapath, "enum-module.yang")}, - inIncludePaths: []string{filepath.Join(TestRoot, "testdata", "structs")}, - inConfig: &DirectoryGenConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.ExcludeDerivedState, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - ParseOptions: ParseOpts{ - ExcludeModules: []string{"enum-types"}, - }, - }, - wantDirMap: map[string]*Directory{ - "/enum-module/parent": { - Name: "Parent", - Fields: map[string]*yang.Entry{ - "child": {Name: "child", Type: nil}, - }, - Path: []string{"", "enum-module", "parent"}, - }, - "/enum-module/c": { - Name: "C", - Fields: map[string]*yang.Entry{ - "cl": {Name: "cl", Type: &yang.YangType{Kind: yang.Yenum}}, - }, - Path: []string{"", "enum-module", "c"}, - }, - "/enum-module/parent/child": { - Name: "Parent_Child", - Fields: map[string]*yang.Entry{ - "id": {Name: "id", Type: &yang.YangType{Kind: yang.Yidentityref}}, - "id2": {Name: "id2", Type: &yang.YangType{Kind: yang.Yidentityref}}, - "inline-enum": {Name: "inline-enum", Type: &yang.YangType{Kind: yang.Yenum}}, - }, - Path: []string{"", "enum-module", "parent", "child"}, - }, - "/enum-module/a-lists/a-list": { - Name: "AList", - Fields: map[string]*yang.Entry{}, // Key is only part of state and thus is excluded. - Path: []string{"", "enum-module", "a-lists", "a-list"}, - }, - "/enum-module/b-lists/b-list": { - Name: "BList", - Fields: map[string]*yang.Entry{}, - Path: []string{"", "enum-module", "b-lists", "b-list"}, - }, - }, - wantTypeMap: map[string]map[string]*MappedType{ - "/enum-module/parent": { - "child": nil, - }, - "/enum-module/c": { - "cl": {NativeType: "E_EnumModule_Cl", IsEnumeratedValue: true}, - }, - "/enum-module/parent/child": { - "id": {NativeType: "E_EnumTypes_ID", IsEnumeratedValue: true}, - "id2": {NativeType: "E_EnumTypes_ID", IsEnumeratedValue: true}, - "inline-enum": {NativeType: "E_Child_InlineEnum", IsEnumeratedValue: true}, - }, - "/enum-module/a-lists/a-list": {}, - "/enum-module/b-lists/b-list": {}, - }, - }, { - name: "simple openconfig test with openconfig-simple module excluded", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inIncludePaths: []string{filepath.Join(TestRoot, "testdata", "structs")}, - inConfig: &DirectoryGenConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - ParseOptions: ParseOpts{ - ExcludeModules: []string{"openconfig-simple"}, - }, - }, - wantDirMap: map[string]*Directory{}, - wantTypeMap: map[string]map[string]*MappedType{}, - }, { - name: "simple openconfig test with fakeroot", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inIncludePaths: []string{filepath.Join(TestRoot, "testdata", "structs")}, - inConfig: &DirectoryGenConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - ParseOptions: ParseOpts{ - ExcludeModules: []string{}, - }, - }, - wantDirMap: map[string]*Directory{ - "/device": { - Name: "Device", - Fields: map[string]*yang.Entry{ - "parent": {Name: "parent", Type: nil}, - "remote-container": {Name: "remote-container", Type: nil}, - }, - Path: []string{"", "device"}, - }, - "/openconfig-simple/parent": { - Name: "Parent", - Fields: map[string]*yang.Entry{ - "child": {Name: "child", Type: nil}, - }, - Path: []string{"", "openconfig-simple", "parent"}, - }, - "/openconfig-simple/parent/child": { - Name: "Parent_Child", - Fields: map[string]*yang.Entry{ - "one": {Name: "one", Type: &yang.YangType{Kind: yang.Ystring}}, - "two": {Name: "two", Type: &yang.YangType{Kind: yang.Ystring}}, - "three": {Name: "three", Type: &yang.YangType{Kind: yang.Yenum}}, - "four": {Name: "four", Type: &yang.YangType{Kind: yang.Ybinary}}, - }, - Path: []string{"", "openconfig-simple", "parent", "child"}, - }, - "/openconfig-simple/remote-container": { - Name: "RemoteContainer", - Fields: map[string]*yang.Entry{ - "a-leaf": {Name: "a-leaf", Type: &yang.YangType{Kind: yang.Ystring}}, - }, - Path: []string{"", "openconfig-simple", "remote-container"}, - }, - }, - wantTypeMap: map[string]map[string]*MappedType{ - "/device": { - "parent": nil, - "remote-container": nil, - }, - "/openconfig-simple/parent": { - "child": nil, - }, - "/openconfig-simple/parent/child": { - "one": {NativeType: "string"}, - "two": {NativeType: "string"}, - "three": {NativeType: "E_Child_Three", IsEnumeratedValue: true}, - "four": {NativeType: "Binary"}, - }, - "/openconfig-simple/remote-container": { - "a-leaf": {NativeType: "string"}, - }, - }, - }, { - name: "enum openconfig test with enum-types module excluded with fakeroot", - inFiles: []string{filepath.Join(datapath, "enum-module.yang")}, - inIncludePaths: []string{filepath.Join(TestRoot, "testdata", "structs")}, - inConfig: &DirectoryGenConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - ParseOptions: ParseOpts{ - ExcludeModules: []string{"enum-types"}, - }, - }, - wantDirMap: map[string]*Directory{ - "/device": { - Name: "Device", - Fields: map[string]*yang.Entry{ - "parent": {Name: "parent", Type: nil}, - "c": {Name: "c", Type: nil}, - "a-list": {Name: "a-list", Type: nil}, - "b-list": {Name: "b-list", Type: nil}, - }, - Path: []string{"", "device"}, - }, - "/enum-module/parent": { - Name: "Parent", - Fields: map[string]*yang.Entry{ - "child": {Name: "child", Type: nil}, - }, - Path: []string{"", "enum-module", "parent"}, - }, - "/enum-module/c": { - Name: "C", - Fields: map[string]*yang.Entry{ - "cl": {Name: "cl", Type: &yang.YangType{Kind: yang.Yenum}}, - }, - Path: []string{"", "enum-module", "c"}, - }, - "/enum-module/parent/child": { - Name: "Parent_Child", - Fields: map[string]*yang.Entry{ - "id": {Name: "id", Type: &yang.YangType{Kind: yang.Yidentityref}}, - "enum": {Name: "enum", Type: &yang.YangType{Kind: yang.Yenum}}, - "id2": {Name: "id2", Type: &yang.YangType{Kind: yang.Yidentityref}}, - "inline-enum": {Name: "inline-enum", Type: &yang.YangType{Kind: yang.Yenum}}, - }, - Path: []string{"", "enum-module", "parent", "child"}, - }, - "/enum-module/a-lists/a-list": { - Name: "AList", - Fields: map[string]*yang.Entry{ - "value": {Name: "value", Type: &yang.YangType{Kind: yang.Yunion}}, - }, - Path: []string{"", "enum-module", "a-lists", "a-list"}, - }, - "/enum-module/b-lists/b-list": { - Name: "BList", - Fields: map[string]*yang.Entry{ - "value": {Name: "value", Type: &yang.YangType{Kind: yang.Yunion}}, - }, - Path: []string{"", "enum-module", "b-lists", "b-list"}, - }, - }, - wantTypeMap: map[string]map[string]*MappedType{ - "/device": { - "parent": nil, - "c": nil, - "a-list": nil, - "b-list": nil, - }, - "/enum-module/parent": { - "child": nil, - }, - "/enum-module/c": { - "cl": {NativeType: "E_EnumModule_Cl", IsEnumeratedValue: true}, - }, - "/enum-module/parent/child": { - "id": {NativeType: "E_EnumTypes_ID", IsEnumeratedValue: true}, - "enum": {NativeType: "E_EnumTypes_TdEnum", IsEnumeratedValue: true}, - "id2": {NativeType: "E_EnumTypes_ID", IsEnumeratedValue: true}, - "inline-enum": {NativeType: "E_Child_InlineEnum", IsEnumeratedValue: true}, - }, - "/enum-module/a-lists/a-list": { - "value": {NativeType: "AList_Value_Union"}, - }, - "/enum-module/b-lists/b-list": { - "value": {NativeType: "BList_Value_Union"}, - }, - }, - }, { - name: "simple openconfig test with openconfig-simple module excluded with fakeroot", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inIncludePaths: []string{filepath.Join(TestRoot, "testdata", "structs")}, - inConfig: &DirectoryGenConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - ParseOptions: ParseOpts{ - ExcludeModules: []string{"openconfig-simple"}, - }, - }, - wantDirMap: map[string]*Directory{ - "/device": { - Name: "Device", - Fields: map[string]*yang.Entry{}, - Path: []string{"", "device"}, - }, - }, - wantTypeMap: map[string]map[string]*MappedType{ - "/device": {}, - }, - }} - - // Simple helper function for error messages - fieldNames := func(dir *Directory) []string { - names := []string{} - for k := range dir.Fields { - names = append(names, k) - } - return names - } - - for _, tt := range tests { - c := tt.inConfig - t.Run(fmt.Sprintf("%s:GetDirectoriesAndLeafTypes(compressBehaviour:%v,GenerateFakeRoot:%v)", tt.name, c.TransformationOptions.CompressBehaviour, c.TransformationOptions.GenerateFakeRoot), func(t *testing.T) { - gotDirMap, gotTypeMap, errs := c.GetDirectoriesAndLeafTypes(tt.inFiles, tt.inIncludePaths) - if errs != nil { - t.Fatal(errs) - } - - // This checks the "Name" and "Path" attributes of the output Directories. - if diff := cmp.Diff(tt.wantDirMap, gotDirMap, cmpopts.IgnoreFields(Directory{}, "Entry", "Fields", "ShadowedFields", "ListAttr", "IsFakeRoot"), cmpopts.EquateEmpty()); diff != "" { - t.Fatalf("(-want +got):\n%s", diff) - } - - // Verify certain fields of the "Fields" attribute -- there are too many fields to ignore to use cmp.Diff for comparison. - for gotDirName, gotDir := range gotDirMap { - // Note that any missing or extra Directories would've been caught with the previous check. - wantDir := tt.wantDirMap[gotDirName] - if len(gotDir.Fields) != len(wantDir.Fields) { - t.Fatalf("director %q: Did not get expected set of fields, got: %v, want: %v", gotDirName, fieldNames(gotDir), fieldNames(wantDir)) - } - for fieldk, wantField := range wantDir.Fields { - gotField, ok := gotDir.Fields[fieldk] - if !ok { - t.Errorf("Could not find expected field %q in %q, gotDir.Fields: %v", fieldk, gotDirName, gotDir.Fields) - continue // Fatal error for this field only. - } - - if gotField.Name != wantField.Name { - t.Errorf("Field %q of %q did not have expected name, got: %v, want: %v", fieldk, gotDirName, gotField.Name, wantField.Name) - } - - if gotField.Type != nil && wantField.Type != nil && gotField.Type.Kind != wantField.Type.Kind { - t.Errorf("Field %q of %q did not have expected type, got: %v, want: %v", fieldk, gotDirName, gotField.Type.Kind, wantField.Type.Kind) - } - - if tt.wantFieldPath != nil && gotField.Path() != tt.wantFieldPath[gotDirName][fieldk] { - t.Errorf("Field %q of %q did not have expected path, got: %v, want: %v", fieldk, gotDirName, gotField.Path(), tt.wantFieldPath[gotDirName][fieldk]) - } - } - } - // The other attributes for wantDir are not tested, as - // most of the work is passed to mappedDefinitions() - // and buildDirectoryDefinitions(), making a good - // quick check here sufficient. - - // This checks the "NativeType" and "IsEnumeratedValue" attributes of the output leaf types. - // Since this is an integration test, many lower-level detail checks are omitted. - if diff := cmp.Diff(tt.wantTypeMap, gotTypeMap, cmpopts.IgnoreFields(MappedType{}, "UnionTypes", "ZeroValue", "DefaultValue")); diff != "" { - t.Errorf("(-want +got):\n%s", diff) - } - }) - } -} - func TestFindRootEntries(t *testing.T) { tests := []struct { name string From 12a5e38438922aaa9a6d134b1ae83b80d32329cc Mon Sep 17 00:00:00 2001 From: wenovus Date: Mon, 6 Jun 2022 09:20:00 -0700 Subject: [PATCH 08/19] Split Go generation into `gogen` package. The change includes the following notable changes: * gogen/codegen.go: Instead of using `ygen.NewYANGCodeGenerator` as the struct for all code generation from the single `ygen` package, `gogen.NewGoCodeGenerator` is created for Go generation. `GoOpts` is moved in `gogen`. * gogen/genir_test: Since IR generation requires a suitable `LangMapper` implementation, the tests using `GoLangMapper` are moved to here. * gogen/gogen.go, goelements.go: These are moved to this package. * gogen/helpers.go: Go generation-specific helpers are moved here. * internal/igenutil/genutil.go: Non-Go-specific generation helpers are moved here. * ygen/genstate_test.go: A fake LangMapper implementation is created for unit testing. --- demo/getting_started/interfaces_test.go | 16 +- generator/generator.go | 14 +- generator/generator_test.go | 50 +- gogen/codegen.go | 429 ++++ gogen/codegen_test.go | 969 +++++++++ gogen/genir_test.go | 1863 +++++++++++++++++ {ygen => gogen}/goelements.go | 73 +- {ygen => gogen}/goelements_test.go | 166 +- {ygen => gogen}/gogen.go | 145 +- {ygen => gogen}/gogen_test.go | 309 +-- gogen/helpers.go | 57 + gogen/helpers_test.go | 32 + .../schema/openconfig-extensions.yang | 0 ...nfig-options-compress-fakeroot-schema.json | 0 ...ig-options-compress-fakeroot.formatted-txt | 0 .../openconfig-options-compress-schema.json | 0 .../openconfig-options-compress.formatted-txt | 0 .../openconfig-options-explicit-schema.json | 0 .../openconfig-options-explicit.formatted-txt | 0 ...ig-options-nocompress-fakeroot-schema.json | 0 ...-options-nocompress-fakeroot.formatted-txt | 0 .../openconfig-options-nocompress-schema.json | 0 ...penconfig-options-nocompress.formatted-txt | 0 .../testdata/schema/openconfig-options.yang | 0 .../structs/choice-case-example.formatted-txt | 0 .../testdata/structs/empty.formatted-txt | 0 .../enum-duplication-dedup.formatted-txt | 0 .../enum-duplication-dup.formatted-txt | 0 .../enum-list-uncompressed.formatted-txt | 0 ...-uncompressed.wrapper-unions.formatted-txt | 0 .../structs/enum-module.formatted-txt | 0 .../enum-module.long-enum-names.formatted-txt | 0 ...ing-module-typedef-enum-name.formatted-txt | 0 ...def-enum-name.wrapper-unions.formatted-txt | 0 ...ing-module-typedef-enum-name.formatted-txt | 0 .../enum-module.wrapper-unions.formatted-txt | 0 .../structs/enum-multi-module.formatted-txt | 0 ...num-union-with-enum-defaults.formatted-txt | 0 .../enum-union.consistent.formatted-txt | 0 .../testdata/structs/enum-union.formatted-txt | 0 .../exclude-state-ro-list.formatted-txt | 0 .../structs/excluded-module.formatted-txt | 0 .../openconfig-augmented.formatted-txt | 0 ...penconfig-camelcase-compress.formatted-txt | 0 ...nfig-config-false-compressed.formatted-txt | 0 ...ig-config-false-uncompressed.formatted-txt | 0 ...onfig-enumcamelcase-compress.formatted-txt | 0 .../openconfig-fakeroot-nc.formatted-txt | 0 .../structs/openconfig-fakeroot.formatted-txt | 0 .../openconfig-leaflist-default.formatted-txt | 0 .../openconfig-list-enum-key.formatted-txt | 0 ...list-enum-key.getters-append.formatted-txt | 0 ...g-list-enum-key.leaf-getters.formatted-txt | 0 ...g-list-enum-key.trimmed-enum.formatted-txt | 0 ...-multikey-list-name-conflict.formatted-txt | 0 ...penconfig-simple-annotations.formatted-txt | 0 ...enconfig-simple-excludestate.formatted-txt | 0 ...penconfig-simple-no-compress.formatted-txt | 0 ...ple-no-compress.trimmed-enum.formatted-txt | 0 .../structs/openconfig-simple.formatted-txt | 0 ...long-enum-names.trimmed-enum.formatted-txt | 0 .../structs/openconfig-unione.formatted-txt | 0 ...config-unione.wrapper-unions.formatted-txt | 0 .../openconfig-versioned-mod.formatted-txt | 0 .../openconfig-withlist-opstate.formatted-txt | 0 .../structs/openconfig-withlist.formatted-txt | 0 .../presence-container-example.formatted-txt | 0 .../structs/root-entities.formatted-txt | 0 internal/igenutil/genutil.go | 119 ++ ygen/codegen.go | 474 +---- ygen/codegen_test.go | 979 +-------- ygen/genir_test.go | 1819 ---------------- ygen/genstate.go | 3 +- ygen/genstate_test.go | 217 +- ygen/helpers.go | 56 - ygen/helpers_test.go | 29 - ygen/ir.go | 4 +- ygen/protoelements.go | 25 +- ygen/protogen.go | 7 +- ypathgen/pathgen.go | 7 +- 80 files changed, 3972 insertions(+), 3890 deletions(-) create mode 100644 gogen/codegen.go create mode 100644 gogen/codegen_test.go create mode 100644 gogen/genir_test.go rename {ygen => gogen}/goelements.go (91%) rename {ygen => gogen}/goelements_test.go (94%) rename {ygen => gogen}/gogen.go (94%) rename {ygen => gogen}/gogen_test.go (91%) create mode 100644 gogen/helpers.go create mode 100644 gogen/helpers_test.go rename {ygen => gogen}/testdata/schema/openconfig-extensions.yang (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-compress-fakeroot-schema.json (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-compress-fakeroot.formatted-txt (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-compress-schema.json (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-compress.formatted-txt (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-explicit-schema.json (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-explicit.formatted-txt (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-nocompress-fakeroot-schema.json (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-nocompress-fakeroot.formatted-txt (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-nocompress-schema.json (100%) rename {ygen => gogen}/testdata/schema/openconfig-options-nocompress.formatted-txt (100%) rename {ygen => gogen}/testdata/schema/openconfig-options.yang (100%) rename {ygen => gogen}/testdata/structs/choice-case-example.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/empty.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-duplication-dedup.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-duplication-dup.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-list-uncompressed.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-list-uncompressed.wrapper-unions.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-module.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-module.long-enum-names.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.wrapper-unions.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-module.residing-module-typedef-enum-name.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-module.wrapper-unions.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-multi-module.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-union-with-enum-defaults.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-union.consistent.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/enum-union.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/exclude-state-ro-list.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/excluded-module.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-augmented.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-camelcase-compress.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-config-false-compressed.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-config-false-uncompressed.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-enumcamelcase-compress.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-fakeroot-nc.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-fakeroot.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-leaflist-default.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-list-enum-key.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-list-enum-key.getters-append.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-list-enum-key.leaf-getters.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-list-enum-key.trimmed-enum.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-multikey-list-name-conflict.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-simple-annotations.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-simple-excludestate.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-simple-no-compress.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-simple-no-compress.trimmed-enum.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-simple.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-simple.long-enum-names.trimmed-enum.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-unione.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-unione.wrapper-unions.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-versioned-mod.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-withlist-opstate.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/openconfig-withlist.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/presence-container-example.formatted-txt (100%) rename {ygen => gogen}/testdata/structs/root-entities.formatted-txt (100%) create mode 100644 internal/igenutil/genutil.go diff --git a/demo/getting_started/interfaces_test.go b/demo/getting_started/interfaces_test.go index f604c2a0a..0963be6e4 100644 --- a/demo/getting_started/interfaces_test.go +++ b/demo/getting_started/interfaces_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/gogen" "github.com/openconfig/ygot/ygen" ) @@ -22,14 +23,12 @@ func TestGenerateCode(t *testing.T) { tests := []struct { name string inConfig *ygen.GeneratorConfig + inGoOpts *gogen.GoOpts inFiles []string inPaths []string }{{ name: "openconfig interfaces", inConfig: &ygen.GeneratorConfig{ - GoOptions: ygen.GoOpts{ - GenerateSimpleUnions: true, - }, ParseOptions: ygen.ParseOpts{ ExcludeModules: []string{"ietf-interfaces"}, }, @@ -39,6 +38,9 @@ func TestGenerateCode(t *testing.T) { }, GenerateJSONSchema: true, }, + inGoOpts: &gogen.GoOpts{ + GenerateSimpleUnions: true, + }, inFiles: []string{ filepath.Join(TestRoot, "yang", "openconfig-interfaces.yang"), filepath.Join(TestRoot, "yang", "openconfig-if-ip.yang"), @@ -47,9 +49,6 @@ func TestGenerateCode(t *testing.T) { }, { name: "openconfig interfaces with no compression", inConfig: &ygen.GeneratorConfig{ - GoOptions: ygen.GoOpts{ - GenerateSimpleUnions: true, - }, ParseOptions: ygen.ParseOpts{ ExcludeModules: []string{"ietf-interfaces"}, }, @@ -58,6 +57,9 @@ func TestGenerateCode(t *testing.T) { }, GenerateJSONSchema: true, }, + inGoOpts: &gogen.GoOpts{ + GenerateSimpleUnions: true, + }, inFiles: []string{ filepath.Join(TestRoot, "yang", "openconfig-interfaces.yang"), filepath.Join(TestRoot, "yang", "openconfig-if-ip.yang"), @@ -66,7 +68,7 @@ func TestGenerateCode(t *testing.T) { }} for _, tt := range tests { - cg := ygen.NewYANGCodeGenerator(tt.inConfig) + cg := gogen.NewGoCodeGenerator(tt.inConfig, tt.inGoOpts) got, err := cg.GenerateGoCode(tt.inFiles, tt.inPaths) if err != nil { t.Errorf("%s: GenerateGoCode(%v, %v): Config: %v, got unexpected error: %v", tt.name, tt.inFiles, tt.inPaths, tt.inConfig, err) diff --git a/generator/generator.go b/generator/generator.go index bd4037e57..91cbd4523 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -29,6 +29,7 @@ import ( log "github.com/golang/glog" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/gogen" "github.com/openconfig/ygot/ygen" "github.com/openconfig/ygot/ypathgen" ) @@ -85,7 +86,7 @@ var ( goyangImportPath = flag.String("goyang_path", genutil.GoDefaultGoyangImportPath, "The import path to use for goyang's yang package.") generateRename = flag.Bool("generate_rename", false, "If set to true, rename methods are generated for lists within the Go code.") addAnnotations = flag.Bool("annotations", false, "If set to true, metadata annotations are added within the generated structs.") - annotationPrefix = flag.String("annotation_prefix", ygen.DefaultAnnotationPrefix, "String to be appended to each metadata field within the generated structs if annoations is set to true.") + annotationPrefix = flag.String("annotation_prefix", gogen.DefaultAnnotationPrefix, "String to be appended to each metadata field within the generated structs if annoations is set to true.") addYangPresence = flag.Bool("yangpresence", false, "If set to true, a tag will be added to the field of a generated Go struct to indicate when a YANG presence container is being used.") generateAppend = flag.Bool("generate_append", false, "If set to true, append methods are generated for YANG lists (Go maps) within the Go code.") generateGetters = flag.Bool("generate_getters", false, "If set to true, getter methdos that retrieve or create an element are generated for YANG container (Go struct pointer) or list (Go map) fields within the generated code.") @@ -111,7 +112,7 @@ var ( // writeGoCodeSingleFile takes a ygen.GeneratedGoCode struct and writes the Go code // snippets contained within it to the io.Writer, w, provided as an argument. // The output includes a package header which is generated. -func writeGoCodeSingleFile(w io.Writer, goCode *ygen.GeneratedGoCode) error { +func writeGoCodeSingleFile(w io.Writer, goCode *gogen.GeneratedGoCode) error { // Write the package header to the supplier writer. fmt.Fprint(w, goCode.CommonHeader) fmt.Fprint(w, goCode.OneOffHeader) @@ -155,7 +156,7 @@ func writeGoPathCodeSingleFile(w io.Writer, pathCode *ypathgen.GeneratedPathCode // methods, interfaces, and enumeration code snippets into their own files. // Structs are output into files by splitting them evenly among the input split // number. -func splitCodeByFileN(goCode *ygen.GeneratedGoCode, fileN int) (map[string]string, error) { +func splitCodeByFileN(goCode *gogen.GeneratedGoCode, fileN int) (map[string]string, error) { structN := len(goCode.Structs) if fileN < 1 || fileN > structN { return nil, fmt.Errorf("requested %d files, but must be between 1 and %d (number of schema structs)", fileN, structN) @@ -324,7 +325,7 @@ func main() { } // Perform the code generation. - cg := ygen.NewYANGCodeGenerator(&ygen.GeneratorConfig{ + cg := gogen.NewGoCodeGenerator(&ygen.GeneratorConfig{ ParseOptions: ygen.ParseOpts{ ExcludeModules: modsExcluded, SkipEnumDeduplication: *skipEnumDedup, @@ -345,7 +346,8 @@ func main() { PackageName: *packageName, GenerateJSONSchema: *generateSchema, IncludeDescriptions: *includeDescriptions, - GoOptions: ygen.GoOpts{ + }, + &gogen.GoOpts{ YgotImportPath: *ygotImportPath, YtypesImportPath: *ytypesImportPath, GoyangImportPath: *goyangImportPath, @@ -363,7 +365,7 @@ func main() { IncludeModelData: *includeModelData, AppendEnumSuffixForSimpleUnionEnums: *appendEnumSuffixForSimpleUnionEnums, }, - }) + ) generatedGoCode, errs := cg.GenerateGoCode(generateModules, includePaths) if errs != nil { diff --git a/generator/generator_test.go b/generator/generator_test.go index 3a320829f..c8342bffb 100644 --- a/generator/generator_test.go +++ b/generator/generator_test.go @@ -22,19 +22,19 @@ import ( "github.com/google/go-cmp/cmp" "github.com/kylelemons/godebug/pretty" "github.com/openconfig/gnmi/errdiff" - "github.com/openconfig/ygot/ygen" + "github.com/openconfig/ygot/gogen" "github.com/openconfig/ygot/ypathgen" ) func TestWriteGoCode(t *testing.T) { tests := []struct { name string - inGoCode *ygen.GeneratedGoCode + inGoCode *gogen.GeneratedGoCode wantCode string }{{ name: "single element structs and enums", - inGoCode: &ygen.GeneratedGoCode{ - Structs: []ygen.GoStructCodeSnippet{{ + inGoCode: &gogen.GeneratedGoCode{ + Structs: []gogen.GoStructCodeSnippet{{ StructDef: `structOne`, }}, Enums: []string{`enumOne`}, @@ -46,8 +46,8 @@ enumOne `, }, { name: "multi-element structs and enums", - inGoCode: &ygen.GeneratedGoCode{ - Structs: []ygen.GoStructCodeSnippet{{ + inGoCode: &gogen.GeneratedGoCode{ + Structs: []gogen.GoStructCodeSnippet{{ StructDef: "structOne", }, { StructDef: "structTwo", @@ -63,7 +63,7 @@ enumTwo `, }, { name: "json string code", - inGoCode: &ygen.GeneratedGoCode{ + inGoCode: &gogen.GeneratedGoCode{ JSONSchemaCode: "foo", }, wantCode: ` @@ -71,7 +71,7 @@ foo `, }, { name: "enum type map", - inGoCode: &ygen.GeneratedGoCode{ + inGoCode: &gogen.GeneratedGoCode{ EnumTypeMap: "map", }, wantCode: ` @@ -95,16 +95,16 @@ map func TestSplitCodeByFileN(t *testing.T) { tests := []struct { name string - in *ygen.GeneratedGoCode + in *gogen.GeneratedGoCode inFileN int want map[string]string wantErrSubstring string }{{ name: "simple struct with all only structs populated", - in: &ygen.GeneratedGoCode{ + in: &gogen.GeneratedGoCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", - Structs: []ygen.GoStructCodeSnippet{{ + Structs: []gogen.GoStructCodeSnippet{{ StructName: "name", StructDef: "def\n", ListKeys: "name_key", @@ -122,10 +122,10 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "less than 1 file requested for splitting", - in: &ygen.GeneratedGoCode{ + in: &gogen.GeneratedGoCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", - Structs: []ygen.GoStructCodeSnippet{{ + Structs: []gogen.GoStructCodeSnippet{{ StructName: "name", StructDef: "def\n", ListKeys: "name_key", @@ -137,10 +137,10 @@ func TestSplitCodeByFileN(t *testing.T) { wantErrSubstring: "requested 0 files", }, { name: "more than # of structs files requested for splitting", - in: &ygen.GeneratedGoCode{ + in: &gogen.GeneratedGoCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", - Structs: []ygen.GoStructCodeSnippet{{ + Structs: []gogen.GoStructCodeSnippet{{ StructName: "name", StructDef: "def\n", ListKeys: "name_key", @@ -152,10 +152,10 @@ func TestSplitCodeByFileN(t *testing.T) { wantErrSubstring: "requested 2 files", }, { name: "two structs with enums populated", - in: &ygen.GeneratedGoCode{ + in: &gogen.GeneratedGoCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", - Structs: []ygen.GoStructCodeSnippet{{ + Structs: []gogen.GoStructCodeSnippet{{ StructName: "s1", StructDef: "s1def\n", ListKeys: "s1key", @@ -181,10 +181,10 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "two structs, separated into two files", - in: &ygen.GeneratedGoCode{ + in: &gogen.GeneratedGoCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", - Structs: []ygen.GoStructCodeSnippet{{ + Structs: []gogen.GoStructCodeSnippet{{ StructName: "s1", StructDef: "s1def\n", ListKeys: "s1key", @@ -210,10 +210,10 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "five structs, separated into four files", - in: &ygen.GeneratedGoCode{ + in: &gogen.GeneratedGoCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", - Structs: []ygen.GoStructCodeSnippet{{ + Structs: []gogen.GoStructCodeSnippet{{ StructName: "s1", StructDef: "s1def\n", ListKeys: "s1key", @@ -253,10 +253,10 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "five structs, separated into three files", - in: &ygen.GeneratedGoCode{ + in: &gogen.GeneratedGoCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", - Structs: []ygen.GoStructCodeSnippet{{ + Structs: []gogen.GoStructCodeSnippet{{ StructName: "s1", StructDef: "s1def\n", ListKeys: "s1key", @@ -295,10 +295,10 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "five structs, separated into two files", - in: &ygen.GeneratedGoCode{ + in: &gogen.GeneratedGoCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", - Structs: []ygen.GoStructCodeSnippet{{ + Structs: []gogen.GoStructCodeSnippet{{ StructName: "s1", StructDef: "s1def\n", ListKeys: "s1key", diff --git a/gogen/codegen.go b/gogen/codegen.go new file mode 100644 index 000000000..35a4995d8 --- /dev/null +++ b/gogen/codegen.go @@ -0,0 +1,429 @@ +// Package gogen is a library for generating Go structs from a YANG schema. +package gogen + +import ( + "fmt" + "sort" + "strings" + + "github.com/openconfig/ygot/internal/igenutil" + "github.com/openconfig/ygot/util" + "github.com/openconfig/ygot/ygen" + "github.com/openconfig/ygot/ygot" +) + +// GoCodeGenerator is a structure that is used to pass arguments as to +// how the output Go code should be generated. +type GoCodeGenerator struct { + // Config stores the configuration parameters used for code generation. + Config ygen.GeneratorConfig + // GoOptions stores a struct which stores Go code generation specific + // options for the code generaton. + GoOptions GoOpts +} + +// GoOpts stores Go specific options for the code generation library. +type GoOpts struct { + // SchemaVarName is the name for the variable which stores the compressed + // JSON schema in the generated Go code. JSON schema output is only + // produced if the GenerateJSONSchema YANGCodeGenerator field is set to + // true. + SchemaVarName string + // GoyangImportPath specifies the path that should be used in the generated + // code for importing the goyang/pkg/yang package. + GoyangImportPath string + // YgotImportPath specifies the path to the ygot library that should be used + // in the generated code. + YgotImportPath string + // YtypesImportPath specifies the path to ytypes library that should be used + // in the generated code. + YtypesImportPath string + // GenerateRenameMethod specifies whether methods for renaming list entries + // should be generated in the output Go code. + GenerateRenameMethod bool + // AddAnnotationFields specifies whether annotation fields should be added to + // the generated structs. When set to true, a metadata field is added for each + // struct, and for each field of each struct. Metadata field's names are + // prefixed by the string specified in the AnnotationPrefix argument. + AddAnnotationFields bool + // AnnotationPrefix specifies the string which is prefixed to the name of + // annotation fields. It defaults to Λ. + AnnotationPrefix string + // AddYangPresence specifies whether tags should be added to the generated + // fields of a struct. When set to true, a struct tag will be added to the field + // when a YANG container is a presence container + // https://datatracker.ietf.org/doc/html/rfc6020#section-7.5.1 + // a field tag of `yangPresence="true"` will only be added if the container is + // a YANG presence container, and will be omitted if this is not the case. + AddYangPresence bool + // GenerateGetters specifies whether GetOrCreate* methods should be created + // for struct pointer (YANG container) and map (YANG list) fields of generated + // structs. + GenerateGetters bool + // GenerateDeleteMethod specifies whether Delete* methods should be created for + // map (YANG list) fields of generated structs. + GenerateDeleteMethod bool + // GenerateAppendList specifies whether Append* methods should be created for + // list fields of a struct. These methods take an input list member type, extract + // the key and append the supplied value to the list. + GenerateAppendMethod bool + // GenerateSimpleUnions specifies whether simple typedefs are used to + // represent union subtypes in the generated code instead of using + // wrapper types. + GenerateSimpleUnions bool + // GenerateLeafGetters specifies whether Get* methods should be created for + // leaf fields of a struct. Care should be taken with this option since a Get + // method returns the *Go* zero value for a particular entity if the field is + // unset. This means that it is not possible for a caller of method to know + // whether a field has been explicitly set to the zero value (i.e., an integer + // field is set to 0), or whether the field was actually unset. + GenerateLeafGetters bool + // GeneratePopulateDefault specifies whether a PopulateDefaults method + // should be generated for every GoStruct that recursively populates + // default values within the subtree. + GeneratePopulateDefault bool + // GNMIProtoPath specifies the path to the generated gNMI protobuf, which + // is used to store the catalogue entries for generated modules. + GNMIProtoPath string + // ValidateFunctionName specifies the name of a function that proxies ΛValidate. + ValidateFunctionName string + // IncludeModelData specifies whether gNMI ModelData messages should be generated + // in the output code. + IncludeModelData bool + // AppendEnumSuffixForSimpleUnionEnums appends an "Enum" suffix to the + // enumeration name for simple (i.e. non-typedef) leaves which are + // unions with an enumeration inside. This makes all inlined + // enumerations within unions, whether typedef or not, have this + // suffix, achieving consistency. Since this flag is planned to be a + // v1 compatibility flag along with + // UseDefiningModuleForTypedefEnumNames, and will be removed in v1, it + // only applies when useDefiningModuleForTypedefEnumNames is also set + // to true. + AppendEnumSuffixForSimpleUnionEnums bool +} + +// GeneratedGoCode contains generated code snippets that can be processed by the calling +// application. The generated code is divided into two types of objects - both represented +// as a slice of strings: Structs contains a set of Go structures that have been generated, +// and Enums contains the code for generated enumerated types (corresponding to identities, +// or enumerated values within the YANG models for which code is being generated). Additionally +// the header with package comment of the generated code is returned in Header, along with the +// a slice of strings containing the packages that are required for the generated Go code to +// be compiled is returned. +// +// For schemas that contain enumerated types (identities, or enumerations), a code snippet is +// returned as the EnumMap field that allows the string values from the YANG schema to be resolved. +// The keys of the map are strings corresponding to the name of the generated type, with the +// map values being maps of the int64 identifier for each value of the enumeration to the name of +// the element, as used in the YANG schema. +type GeneratedGoCode struct { + Structs []GoStructCodeSnippet // Structs is the generated set of structs representing containers or lists in the input YANG models. + Enums []string // Enums is the generated set of enum definitions corresponding to identities and enumerations in the input YANG models. + CommonHeader string // CommonHeader is the header that should be used for all output Go files. + OneOffHeader string // OneOffHeader defines the header that should be included in only one output Go file - such as package init statements. + EnumMap string // EnumMap is a Go map that allows the YANG string values of enumerated types to be resolved. + // JSONSchemaCode contains code defining a variable storing a serialised JSON schema for the + // generated Go structs. When deserialised it consists of a map[string]*yang.Entry. The + // entries are the root level yang.Entry definitions along with their corresponding + // hierarchy (i.e., the yang.Entry for /foo contains /foo/... - all of foo's descendents). + // Each yang.Entry which corresponds to a generated Go struct has two extra fields defined: + // - schemapath - the path to this entry within the schema. This is provided since the Path() method of + // the deserialised yang.Entry does not return the path since the Parent pointer is not + // populated. + // - structname - the name of the struct that was generated for the schema element. + JSONSchemaCode string + // RawJSONSchema stores the JSON document which is serialised and stored in JSONSchemaCode. + // It is populated only if the StoreRawSchema GoCodeGenerator boolean is set to true. + RawJSONSchema []byte + // EnumTypeMap is a Go map that allows YANG schemapaths to be mapped to reflect.Type values. + EnumTypeMap string +} + +// NewGoCodeGenerator returns a new instance of the GoCodeGenerator +// struct to the calling function. +func NewGoCodeGenerator(c *ygen.GeneratorConfig, goopts *GoOpts) *GoCodeGenerator { + cg := &GoCodeGenerator{} + + if c != nil { + cg.Config = *c + } + if goopts != nil { + cg.GoOptions = *goopts + } + + return cg +} + +// checkForBinaryKeys returns a non-empty list of errors if the input directory +// has one or more binary types (including union types containing binary types) +// as a list key. +func checkForBinaryKeys(dir *ygen.ParsedDirectory) []error { + var errs []error + for _, k := range dir.ListKeys { + if k.LangType.NativeType == ygot.BinaryTypeName { + errs = append(errs, fmt.Errorf("list %s has a binary key -- this is unsupported", dir.Path)) + continue + } + for typeName := range k.LangType.UnionTypes { + if typeName == ygot.BinaryTypeName { + errs = append(errs, fmt.Errorf("list %s has a union key containing a binary -- this is unsupported", dir.Path)) + } + } + } + return errs +} + +// GenerateGoCode takes a slice of strings containing the path to a set of YANG +// files which contain YANG modules, and a second slice of strings which +// specifies the set of paths that are to be searched for associated models (e.g., +// modules that are included by the specified set of modules, or submodules of those +// modules). It extracts the set of modules that are to be generated, and returns +// a GeneratedGoCode struct which contains: +// 1. A struct definition for each container or list that is within the specified +// set of models. +// 2. Enumerated values which correspond to the set of enumerated entities (leaves +// of type enumeration, identities, typedefs that reference an enumeration) +// within the specified models. +// If errors are encountered during code generation, an error is returned. +func (cg *GoCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*GeneratedGoCode, util.Errors) { + opts := ygen.IROptions{ + ParseOptions: cg.Config.ParseOptions, + TransformationOptions: cg.Config.TransformationOptions, + NestedDirectories: false, + AbsoluteMapPaths: false, + AppendEnumSuffixForSimpleUnionEnums: cg.GoOptions.AppendEnumSuffixForSimpleUnionEnums, + } + + var codegenErr util.Errors + ir, err := ygen.GenerateIR(yangFiles, includePaths, NewGoLangMapper(cg.GoOptions.GenerateSimpleUnions), opts) + if err != nil { + return nil, util.AppendErr(codegenErr, err) + } + + var rootName string + if cg.Config.TransformationOptions.GenerateFakeRoot { + rootName = cg.Config.TransformationOptions.FakeRootName + if rootName == "" { + rootName = igenutil.DefaultRootName + } + if r, ok := ir.Directories[fmt.Sprintf("/%s", rootName)]; ok { + rootName = r.Name + } + } + commonHeader, oneoffHeader, err := writeGoHeader(yangFiles, includePaths, cg, rootName, ir.ModelData) + if err != nil { + return nil, util.AppendErr(codegenErr, err) + } + + usedEnumeratedTypes := map[string]bool{} + // generatedUnions stores a map, keyed by the output name for a union, + // that has already been output in the generated code. This ensures that + // where two entities re-use a union that has already been created (e.g., + // a leafref to a union) then it is output only once in the generated code. + generatedUnions := map[string]bool{} + enumTypeMap := map[string][]string{} + structSnippets := []GoStructCodeSnippet{} + + isBuiltInType := func(fType string) bool { + _, ok := validGoBuiltinTypes[fType] + return ok + } + + // Range through the directories to find the enumerated and union types that we + // need. We have to do this without writing the code out, since we require some + // knowledge of these types to do code generation along with the values. + for _, directoryPath := range ir.OrderedDirectoryPathsByName() { + dir := ir.Directories[directoryPath] + + // Generate structs. + if errs := checkForBinaryKeys(dir); len(errs) != 0 { + codegenErr = util.AppendErrs(codegenErr, errs) + continue + } + structOut, errs := writeGoStruct(dir, ir.Directories, generatedUnions, opts.TransformationOptions.IgnoreShadowSchemaPaths, cg.GoOptions, cg.Config.GenerateJSONSchema) + if errs != nil { + codegenErr = util.AppendErrs(codegenErr, errs) + continue + } + structSnippets = append(structSnippets, structOut) + + // Record down all the enum types we encounter in each field. + + // definedUnionTypes keeps track of which unions we have + // already processed to avoid processing the same one twice. + definedUnionTypes := map[string]bool{} + for _, fn := range dir.OrderedFieldNames() { + field := dir.Fields[fn] + + // Strip the module name from the path. + schemaPath := util.SlicePathToString(append([]string{""}, strings.Split(field.YANGDetails.Path, "/")[2:]...)) + switch { + case field.LangType == nil: + // This is a directory, so we continue. + continue + case field.LangType.IsEnumeratedValue: + usedEnumeratedTypes[field.LangType.NativeType] = true + enumTypeMap[schemaPath] = []string{field.LangType.NativeType} + case len(field.LangType.UnionTypes) > 1: + if definedUnionTypes[field.LangType.NativeType] { + continue + } + definedUnionTypes[field.LangType.NativeType] = true + + for ut := range field.LangType.UnionTypes { + if !isBuiltInType(ut) { + // non-builtin union types are always enumerated types. + usedEnumeratedTypes[ut] = true + if enumTypeMap[schemaPath] == nil { + enumTypeMap[schemaPath] = []string{} + } + enumTypeMap[schemaPath] = append(enumTypeMap[schemaPath], ut) + } + } + // Sort the enumerated types into schema order. + sort.Slice(enumTypeMap[schemaPath], func(i, j int) bool { + return field.LangType.UnionTypes[enumTypeMap[schemaPath][i]] < field.LangType.UnionTypes[enumTypeMap[schemaPath][j]] + }) + } + } + } + + processedEnums, err := genGoEnumeratedTypes(ir.Enums) + if err != nil { + return nil, append(codegenErr, err) + } + + genum, err := writeGoEnumeratedTypes(processedEnums, usedEnumeratedTypes) + if err != nil { + return nil, append(codegenErr, err) + } + + var rawSchema []byte + var jsonSchema string + var enumTypeMapCode string + if cg.Config.GenerateJSONSchema { + var err error + rawSchema, err = ir.SchemaTree(cg.Config.IncludeDescriptions) + if err != nil { + codegenErr = util.AppendErr(codegenErr, fmt.Errorf("error marshalling JSON schema: %v", err)) + } + + if rawSchema != nil { + if jsonSchema, err = writeGoSchema(rawSchema, cg.GoOptions.SchemaVarName); err != nil { + codegenErr = util.AppendErr(codegenErr, err) + } + } + + if enumTypeMapCode, err = generateEnumTypeMap(enumTypeMap); err != nil { + codegenErr = util.AppendErr(codegenErr, err) + } + } + + // Return any errors that were encountered during code generation. + if len(codegenErr) != 0 { + return nil, codegenErr + } + + return &GeneratedGoCode{ + CommonHeader: commonHeader, + OneOffHeader: oneoffHeader, + Structs: structSnippets, + Enums: genum.enums, + EnumMap: genum.valMap, + JSONSchemaCode: jsonSchema, + RawJSONSchema: rawSchema, + EnumTypeMap: enumTypeMapCode, + }, nil +} + +// goEnumeratedType contains the intermediate representation of an enumerated +// type (identityref or enumeration) suitable for Go code generation. +type goEnumeratedType struct { + Name string + CodeValues map[int64]string + YANGValues map[int64]ygot.EnumDefinition +} + +// enumGeneratedCode contains generated Go code for enumerated types. +type enumGeneratedCode struct { + enums []string + valMap string +} + +// genGoEnumeratedTypes converts the input map of EnumeratedYANGType objects to +// another intermediate representation suitable for Go code generation. +func genGoEnumeratedTypes(enums map[string]*ygen.EnumeratedYANGType) (map[string]*goEnumeratedType, error) { + et := map[string]*goEnumeratedType{} + for _, e := range enums { + // initialised to be UNSET, such that it is possible to determine that the enumerated value + // was not modified. + values := map[int64]string{ + 0: "UNSET", + } + + // origValues stores the original set of value names, these are not maintained to be + // Go-safe, and are rather used to map back to the original schema values if required. + // 0 is not populated within this map, such that the values can be used to check whether + // there was a valid entry in the original schema. The value is stored as a ygot + // EnumDefinition, which stores the name, and in the case of identity values, the + // module within which the identity was defined. + origValues := map[int64]ygot.EnumDefinition{} + + switch e.Kind { + case ygen.IdentityType, ygen.SimpleEnumerationType, ygen.DerivedEnumerationType, ygen.UnionEnumerationType, ygen.DerivedUnionEnumerationType: + for i, v := range e.ValToYANGDetails { + values[int64(i)+1] = safeGoEnumeratedValueName(v.Name) + origValues[int64(i)+1] = v + } + default: + return nil, fmt.Errorf("unknown enumerated type %v", e.Kind) + } + + et[e.Name] = &goEnumeratedType{ + Name: e.Name, + CodeValues: values, + YANGValues: origValues, + } + } + return et, nil +} + +// writeGoEnumeratedTypes generates Go code for the input enumerations if they +// are present in the usedEnums map. +func writeGoEnumeratedTypes(enums map[string]*goEnumeratedType, usedEnums map[string]bool) (*enumGeneratedCode, error) { + orderedEnumNames := []string{} + for _, e := range enums { + orderedEnumNames = append(orderedEnumNames, e.Name) + } + sort.Strings(orderedEnumNames) + + enumValMap := map[string]map[int64]ygot.EnumDefinition{} + enumSnippets := []string{} + + for _, en := range orderedEnumNames { + e := enums[en] + if _, ok := usedEnums[fmt.Sprintf("%s%s", goEnumPrefix, e.Name)]; !ok { + // Don't output enumerated types that are not used in the code that we have + // such that we don't create generated code for a large array of types that + // just happen to be in modules that were included by other modules. + continue + } + enumOut, err := writeGoEnum(e) + if err != nil { + return nil, err + } + enumSnippets = append(enumSnippets, enumOut) + enumValMap[e.Name] = e.YANGValues + } + + // Write the map of string -> int -> YANG enum name string out. + vmap, err := writeGoEnumMap(enumValMap) + if err != nil { + return nil, err + } + + return &enumGeneratedCode{ + enums: enumSnippets, + valMap: vmap, + }, nil +} diff --git a/gogen/codegen_test.go b/gogen/codegen_test.go new file mode 100644 index 000000000..4daae74b7 --- /dev/null +++ b/gogen/codegen_test.go @@ -0,0 +1,969 @@ +package gogen + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/openconfig/gnmi/errdiff" + "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/testutil" + "github.com/openconfig/ygot/ygen" +) + +const ( + // TestRoot is the root of the test directory such that this is not + // repeated when referencing files. + TestRoot string = "" + // deflakeRuns specifies the number of runs of code generation that + // should be performed to check for flakes. + deflakeRuns int = 10 +) + +// yangTestCase describs a test case for which code generation is performed +// through Goyang's API, it provides the input set of parameters in a way that +// can be reused across tests. +type yangTestCase struct { + name string // Name is the identifier for the test. + inFiles []string // inFiles is the set of inputFiles for the test. + inIncludePaths []string // inIncludePaths is the set of paths that should be searched for imports. + inExcludeModules []string // inExcludeModules is the set of modules that should be excluded from code generation. + inConfig ygen.GeneratorConfig // inConfig specifies the configuration that should be used for the generator test case. + inGoOptions GoOpts // inGoOpts specifies the go-specific configuration that should be used for the generator test case. + wantStructsCodeFile string // wantsStructsCodeFile is the path of the generated Go code that the output of the test should be compared to. + wantErrSubstring string // wantErrSubstring specifies whether the test should expect an error. + wantSchemaFile string // wantSchemaFile is the path to the schema JSON that the output of the test should be compared to. +} + +// TestSimpleStructs tests the processModules, GenerateGoCode and writeGoCode +// functions. It takes the set of YANG modules described in the slice of +// yangTestCases and generates the struct code for them, comparing the output +// to the wantStructsCodeFile. In order to simplify the files that are used, +// the GenerateGoCode structs are concatenated before comparison with the +// expected output. If the generated code matches the expected output, it is +// run against the Go parser to ensure that the code is valid Go - this is +// expected, but it ensures that the input file does not contain Go which is +// invalid. +func TestSimpleStructs(t *testing.T) { + tests := []yangTestCase{{ + name: "simple openconfig test, with compression, with (useless) enum org name trimming", + inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple.formatted-txt"), + }, { + name: "simple openconfig test, with excluded state, with compression, with enum org name trimming", + inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.ExcludeDerivedState, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-excludestate.formatted-txt"), + }, { + name: "simple openconfig test, with no compression", + inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-no-compress.formatted-txt"), + }, { + name: "simple openconfig test, with compression, without shortened enum leaf names, with enum org name trimming", + inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple.long-enum-names.trimmed-enum.formatted-txt"), + }, { + name: "simple openconfig test, with no compression, with enum org name trimming", + inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-no-compress.trimmed-enum.formatted-txt"), + }, { + name: "OpenConfig leaf-list defaults test, with compression", + inFiles: []string{filepath.Join(datapath, "openconfig-leaflist-default.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-leaflist-default.formatted-txt"), + }, { + name: "OpenConfig schema test - with annotations", + inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + AddAnnotationFields: true, + AnnotationPrefix: "ᗩ", + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-simple-annotations.formatted-txt"), + }, { + name: "OpenConfig schema test - list and associated method (rename, new)", + inFiles: []string{filepath.Join(datapath, "openconfig-withlist.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-withlist.formatted-txt"), + }, { + name: "OpenConfig schema test - list and associated method (rename, new) - using operational state", + inFiles: []string{filepath.Join(datapath, "openconfig-withlist.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferOperationalState, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-withlist-opstate.formatted-txt"), + }, { + name: "OpenConfig schema test - multi-keyed list key struct name conflict and associated method (rename, new)", + inFiles: []string{filepath.Join(datapath, "openconfig-multikey-list-name-conflict.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-multikey-list-name-conflict.formatted-txt"), + }, { + name: "simple openconfig test, with a list that has an enumeration key", + inFiles: []string{filepath.Join(datapath, "openconfig-list-enum-key.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + IgnoreShadowSchemaPaths: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-list-enum-key.formatted-txt"), + }, { + name: "simple openconfig test, with a list that has an enumeration key, with enum org name trimming", + inFiles: []string{filepath.Join(datapath, "openconfig-list-enum-key.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-list-enum-key.trimmed-enum.formatted-txt"), + }, { + name: "openconfig test with a identityref union", + inFiles: []string{filepath.Join(datapath, "openconfig-unione.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-unione.formatted-txt"), + }, { + name: "openconfig test with a identityref union (wrapper unions)", + inFiles: []string{filepath.Join(datapath, "openconfig-unione.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-unione.wrapper-unions.formatted-txt"), + }, { + name: "openconfig tests with fakeroot", + inFiles: []string{filepath.Join(datapath, "openconfig-fakeroot.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-fakeroot.formatted-txt"), + }, { + name: "openconfig noncompressed tests with fakeroot", + inFiles: []string{filepath.Join(datapath, "openconfig-fakeroot.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-fakeroot-nc.formatted-txt"), + }, { + name: "schema test with compression", + inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + GenerateJSONSchema: true, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress.formatted-txt"), + wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress-schema.json"), + }, { + name: "schema test without compression", + inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + EnumerationsUseUnderscores: true, + }, + GenerateJSONSchema: true, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress.formatted-txt"), + wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress-schema.json"), + }, { + name: "schema test with fakeroot", + inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + GenerateJSONSchema: true, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress-fakeroot.formatted-txt"), + wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress-fakeroot-schema.json"), + }, { + name: "schema test with fakeroot and no compression", + inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + GenerateJSONSchema: true, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress-fakeroot.formatted-txt"), + wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress-fakeroot-schema.json"), + }, { + name: "schema test with camelcase annotations", + inFiles: []string{filepath.Join(datapath, "openconfig-camelcase.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-camelcase-compress.formatted-txt"), + }, { + name: "structs test with camelcase annotations", + inFiles: []string{filepath.Join(datapath, "openconfig-enumcamelcase.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-enumcamelcase-compress.formatted-txt"), + }, { + name: "structs test with choices and cases", + inFiles: []string{filepath.Join(datapath, "choice-case-example.yang")}, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/choice-case-example.formatted-txt"), + }, { + name: "module with augments", + inFiles: []string{ + filepath.Join(datapath, "openconfig-simple-target.yang"), + filepath.Join(datapath, "openconfig-simple-augment.yang"), + }, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-augmented.formatted-txt"), + }, { + name: "variable and import explicitly specified", + inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + FakeRootName: "fakeroot", + EnumerationsUseUnderscores: true, + }, + Caller: "testcase", + StoreRawSchema: true, + GenerateJSONSchema: true, + }, + inGoOptions: GoOpts{ + SchemaVarName: "YANGSchema", + GoyangImportPath: "foo/goyang", + YgotImportPath: "bar/ygot", + YtypesImportPath: "baz/ytypes", + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-explicit.formatted-txt"), + wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-explicit-schema.json"), + }, { + name: "module with entities at the root", + inFiles: []string{filepath.Join(datapath, "root-entities.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + FakeRootName: "fakeroot", + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + Caller: "testcase", + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/root-entities.formatted-txt"), + }, { + name: "module with empty leaf", + inFiles: []string{filepath.Join(datapath, "empty.yang")}, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/empty.formatted-txt"), + }, { + name: "module with excluded modules", + inFiles: []string{filepath.Join(datapath, "excluded-module.yang")}, + inExcludeModules: []string{"excluded-module-two"}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + FakeRootName: "office", + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/excluded-module.formatted-txt"), + }, { + name: "module with excluded config false", + inFiles: []string{filepath.Join(datapath, "", "openconfig-config-false.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.UncompressedExcludeDerivedState, + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-config-false-uncompressed.formatted-txt"), + }, { + name: "module with excluded config false - with compression", + inFiles: []string{filepath.Join(datapath, "", "openconfig-config-false.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + CompressBehaviour: genutil.ExcludeDerivedState, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-config-false-compressed.formatted-txt"), + }, { + name: "module with getters, delete and append methods", + inFiles: []string{filepath.Join(datapath, "", "openconfig-list-enum-key.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateAppendMethod: true, + GenerateGetters: true, + GenerateDeleteMethod: true, + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-list-enum-key.getters-append.formatted-txt"), + }, { + name: "module with excluded state, with RO list, path compression on", + inFiles: []string{filepath.Join(datapath, "", "exclude-state-ro-list.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + CompressBehaviour: genutil.ExcludeDerivedState, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "exclude-state-ro-list.formatted-txt"), + }, { + name: "different union enumeration types", + inFiles: []string{filepath.Join(datapath, "", "enum-union.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-union.formatted-txt"), + }, { + name: "different union enumeration types with consistent naming for union-inlined enums", + inFiles: []string{filepath.Join(datapath, "", "enum-union.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + AppendEnumSuffixForSimpleUnionEnums: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-union.consistent.formatted-txt"), + }, { + name: "different union enumeration types with default enum values", + inFiles: []string{filepath.Join(datapath, "", "enum-union-with-enum-defaults.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + AppendEnumSuffixForSimpleUnionEnums: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-union-with-enum-defaults.formatted-txt"), + }, { + name: "different union enumeration types with default enum values (wrapper union)", + inFiles: []string{filepath.Join(datapath, "", "enum-union-with-enum-defaults.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + AppendEnumSuffixForSimpleUnionEnums: true, + }, + wantErrSubstring: "default value not supported for wrapper union values, please generate using simplified union leaves", + }, { + name: "enumeration behaviour - resolution across submodules and grouping re-use within union", + inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.formatted-txt"), + }, { + name: "enumeration behaviour (wrapper unions) - resolution across submodules and grouping re-use within union", + inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateLeafGetters: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.wrapper-unions.formatted-txt"), + }, { + name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with enumeration leaf names not shortened", + inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.long-enum-names.formatted-txt"), + }, { + name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with typedef enum names being prefixed by the module of their use/residence rather than of their definition", + inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.residing-module-typedef-enum-name.formatted-txt"), + }, { + name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with typedef enum names being prefixed by the module of their use/residence rather than of their definition, and enumeration leaf names not shortened", + inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.long-enum-names.residing-module-typedef-enum-name.formatted-txt"), + }, { + name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with typedef enum names being prefixed by the module of their use/residence rather than of their definition, and enumeration leaf names not shortened", + inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + EnumerationsUseUnderscores: true, + }, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.long-enum-names.residing-module-typedef-enum-name.wrapper-unions.formatted-txt"), + }, { + name: "enumeration behaviour - multiple enumerations within a union", + inFiles: []string{filepath.Join(datapath, "", "enum-multi-module.yang")}, + inIncludePaths: []string{filepath.Join(datapath, "modules")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + GenerateJSONSchema: true, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-multi-module.formatted-txt"), + }, { + name: "module with leaf getters", + inFiles: []string{filepath.Join(datapath, "", "openconfig-list-enum-key.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + CompressBehaviour: genutil.PreferIntendedConfig, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-list-enum-key.leaf-getters.formatted-txt"), + }, { + name: "uncompressed module with two different enums", + inFiles: []string{filepath.Join(datapath, "", "enum-list-uncompressed.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-list-uncompressed.formatted-txt"), + }, { + name: "uncompressed module with two different enums (wrapper unions)", + inFiles: []string{filepath.Join(datapath, "", "enum-list-uncompressed.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-list-uncompressed.wrapper-unions.formatted-txt"), + }, { + name: "with model data", + inFiles: []string{filepath.Join(datapath, "", "openconfig-versioned-mod.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + CompressBehaviour: genutil.PreferIntendedConfig, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + IncludeModelData: true, + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-versioned-mod.formatted-txt"), + }, { + name: "model with deduplicated enums", + inFiles: []string{filepath.Join(datapath, "enum-duplication.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-duplication-dedup.formatted-txt"), + }, { + name: "model with enums that are in the same grouping duplicated", + inFiles: []string{filepath.Join(datapath, "enum-duplication.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + ParseOptions: ygen.ParseOpts{ + SkipEnumDeduplication: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-duplication-dup.formatted-txt"), + }, { + name: "OpenConfig schema test - list with binary key", + inFiles: []string{filepath.Join(datapath, "openconfig-binary-list.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, + }, + wantErrSubstring: "has a binary key", + }, { + name: "OpenConfig schema test - multi-keyed list with binary key", + inFiles: []string{filepath.Join(datapath, "openconfig-binary-multi-list.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, + }, + wantErrSubstring: "has a binary key", + }, { + name: "OpenConfig schema test - list with union key containing binary", + inFiles: []string{filepath.Join(datapath, "openconfig-union-binary-list.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, + }, + wantErrSubstring: "has a union key containing a binary", + }, { + name: "module with presence containers", + inFiles: []string{filepath.Join(datapath, "presence-container-example.yang")}, + inConfig: ygen.GeneratorConfig{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + FakeRootName: "device", + EnumerationsUseUnderscores: true, + }, + }, + inGoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + AddYangPresence: true, + }, + wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/presence-container-example.formatted-txt"), + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + genCode := func() (*GeneratedGoCode, string, map[string]interface{}, error) { + // Set defaults within the supplied configuration for these tests. + if tt.inConfig.Caller == "" { + // Set the name of the caller explicitly to avoid issues when + // the unit tests are called by external test entities. + tt.inConfig.Caller = "codegen-tests" + } + tt.inConfig.StoreRawSchema = true + tt.inConfig.ParseOptions.ExcludeModules = tt.inExcludeModules + + cg := NewGoCodeGenerator(&tt.inConfig, &tt.inGoOptions) + + gotGeneratedCode, errs := cg.GenerateGoCode(tt.inFiles, tt.inIncludePaths) + var err error + if len(errs) > 0 { + err = fmt.Errorf("%w", errs) + } + if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" { + t.Fatalf("%s: cg.GenerateCode(%v, %v): Config: %+v, Did not get expected error: %s", tt.name, tt.inFiles, tt.inIncludePaths, tt.inConfig, diff) + } + if err != nil { + return nil, "", nil, err + } + + // Write all the received structs into a single file such that + // it can be compared to the received file. + var gotCode bytes.Buffer + fmt.Fprint(&gotCode, gotGeneratedCode.CommonHeader) + fmt.Fprint(&gotCode, gotGeneratedCode.OneOffHeader) + for _, gotStruct := range gotGeneratedCode.Structs { + fmt.Fprint(&gotCode, gotStruct.String()) + } + + for _, gotEnum := range gotGeneratedCode.Enums { + fmt.Fprint(&gotCode, gotEnum) + } + + // Write generated enumeration map out. + fmt.Fprint(&gotCode, gotGeneratedCode.EnumMap) + + var gotJSON map[string]interface{} + if tt.inConfig.GenerateJSONSchema { + // Write the schema byte array out. + fmt.Fprint(&gotCode, gotGeneratedCode.JSONSchemaCode) + fmt.Fprint(&gotCode, gotGeneratedCode.EnumTypeMap) + + if err := json.Unmarshal(gotGeneratedCode.RawJSONSchema, &gotJSON); err != nil { + t.Fatalf("%s: json.Unmarshal(..., %v), could not unmarshal received JSON: %v", tt.name, gotGeneratedCode.RawJSONSchema, err) + } + } + return gotGeneratedCode, gotCode.String(), gotJSON, nil + } + + gotGeneratedCode, gotCode, gotJSON, err := genCode() + if err != nil { + return + } + + if tt.wantSchemaFile != "" { + wantSchema, rferr := ioutil.ReadFile(tt.wantSchemaFile) + if rferr != nil { + t.Fatalf("%s: ioutil.ReadFile(%q) error: %v", tt.name, tt.wantSchemaFile, rferr) + } + + var wantJSON map[string]interface{} + if err := json.Unmarshal(wantSchema, &wantJSON); err != nil { + t.Fatalf("%s: json.Unmarshal(..., [contents of %s]), could not unmarshal golden JSON file: %v", tt.name, tt.wantSchemaFile, err) + } + + if !cmp.Equal(gotJSON, wantJSON) { + diff, _ := testutil.GenerateUnifiedDiff(string(wantSchema), string(gotGeneratedCode.RawJSONSchema)) + t.Fatalf("%s: GenerateGoCode(%v, %v), Config: %+v, did not return correct JSON (file: %v), diff: \n%s", tt.name, tt.inFiles, tt.inIncludePaths, tt.inConfig, tt.wantSchemaFile, diff) + } + } + + wantCodeBytes, rferr := ioutil.ReadFile(tt.wantStructsCodeFile) + if rferr != nil { + t.Fatalf("%s: ioutil.ReadFile(%q) error: %v", tt.name, tt.wantStructsCodeFile, rferr) + } + + wantCode := string(wantCodeBytes) + + if gotCode != wantCode { + // Use difflib to generate a unified diff between the + // two code snippets such that this is simpler to debug + // in the test output. + diff, _ := testutil.GenerateUnifiedDiff(wantCode, gotCode) + t.Errorf("%s: GenerateGoCode(%v, %v), Config: %+v, did not return correct code (file: %v), diff:\n%s", + tt.name, tt.inFiles, tt.inIncludePaths, tt.inConfig, tt.wantStructsCodeFile, diff) + } + + for i := 0; i < deflakeRuns; i++ { + _, gotAttempt, _, _ := genCode() + if gotAttempt != gotCode { + diff, _ := testutil.GenerateUnifiedDiff(gotAttempt, gotCode) + t.Fatalf("flaky code generation, diff:\n%s", diff) + } + } + }) + } +} diff --git a/gogen/genir_test.go b/gogen/genir_test.go new file mode 100644 index 000000000..23089c172 --- /dev/null +++ b/gogen/genir_test.go @@ -0,0 +1,1863 @@ +package gogen + +import ( + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/openconfig/gnmi/errdiff" + gpb "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/ygen" + "github.com/openconfig/ygot/ygot" + "google.golang.org/protobuf/testing/protocmp" +) + +// datapath is the path to common YANG test modules. +const datapath = "../testdata/modules" + +type goLangMapper struct { + *GoLangMapper +} + +// PopulateFieldFlags populates extra information given a particular +// field of a ParsedDirectory and the corresponding AST node. +func (goLangMapper) PopulateFieldFlags(nd ygen.NodeDetails, field *yang.Entry) map[string]string { + if field.Path() == "/openconfig-simple/parent" { + return map[string]string{"foo": "bar"} + } else { + return nil + } +} + +// PopulateEnumFlags populates extra information given a particular +// enumerated type its corresponding AST representation. +func (goLangMapper) PopulateEnumFlags(et ygen.EnumeratedYANGType, yangtype *yang.YangType) map[string]string { + return map[string]string{"typename": yangtype.Name} +} + +func TestGenerateIR(t *testing.T) { + tests := []struct { + desc string + inYANGFiles []string + inIncludePaths []string + inExcludeModules []string + inLangMapper ygen.LangMapper + inOpts ygen.IROptions + wantIR *ygen.IR + wantErrSubstring string + }{{ + desc: "simple openconfig test with compression", + inYANGFiles: []string{ + filepath.Join(datapath, "openconfig-simple.yang"), + filepath.Join(datapath, "openconfig-simple-augment2.yang"), + }, + inLangMapper: goLangMapper{GoLangMapper: NewGoLangMapper(true)}, + inOpts: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + GenerateFakeRoot: true, + }, + AppendEnumSuffixForSimpleUnionEnums: true, + }, + wantIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ + "/device": { + Name: "Device", + Type: ygen.Container, + Path: "/device", + Fields: map[string]*ygen.NodeDetails{ + "parent": { + Name: "Parent", + YANGDetails: ygen.YANGNodeDetails{ + Name: "parent", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent", + SchemaPath: "/parent", + LeafrefTargetPath: "", + Description: "I am a parent container\nthat has 4 children.", + }, + Type: ygen.ContainerNode, + MappedPaths: [][]string{{"parent"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + Flags: map[string]string{"foo": "bar"}, + }, + "remote-container": { + Name: "RemoteContainer", + YANGDetails: ygen.YANGNodeDetails{ + Name: "remote-container", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + Path: "/openconfig-simple/remote-container", + SchemaPath: "/remote-container", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.ContainerNode, + MappedPaths: [][]string{{"remote-container"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + IsFakeRoot: true, + }, + "/openconfig-simple/parent": { + Name: "Parent", + Type: ygen.Container, + Path: "/openconfig-simple/parent", + Fields: map[string]*ygen.NodeDetails{ + "child": { + Name: "Child", + YANGDetails: ygen.YANGNodeDetails{ + Name: "child", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child", + SchemaPath: "/parent/child", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.ContainerNode, + LangType: nil, + MappedPaths: [][]string{{"child"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + }, + "/openconfig-simple/parent/child": { + Name: "Parent_Child", + Type: ygen.Container, + Path: "/openconfig-simple/parent/child", + Fields: map[string]*ygen.NodeDetails{ + "zero": { + Name: "Zero", + YANGDetails: ygen.YANGNodeDetails{ + Name: "zero", + BelongingModule: "openconfig-simple-augment2", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple-grouping", + Path: "/openconfig-simple/parent/child/state/zero", + SchemaPath: "/parent/child/state/zero", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "string", + ZeroValue: `""`, + }, + MappedPaths: [][]string{{"state", "zero"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple-augment2"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "one": { + Name: "One", + YANGDetails: ygen.YANGNodeDetails{ + Name: "one", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/config/one", + SchemaPath: "/parent/child/config/one", + ShadowSchemaPath: "/parent/child/state/one", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"config", "one"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: [][]string{{"state", "one"}}, + ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + }, + "two": { + Name: "Two", + YANGDetails: ygen.YANGNodeDetails{ + Name: "two", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state/two", + SchemaPath: "/parent/child/state/two", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"state", "two"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "three": { + Name: "Three", + YANGDetails: ygen.YANGNodeDetails{ + Name: "three", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/config/three", + SchemaPath: "/parent/child/config/three", + ShadowSchemaPath: "/parent/child/state/three", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "E_Child_Three", + UnionTypes: nil, + IsEnumeratedValue: true, + ZeroValue: "0", + DefaultValue: nil, + }, + MappedPaths: [][]string{{"config", "three"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: [][]string{{"state", "three"}}, + ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + }, + "four": { + Name: "Four", + YANGDetails: ygen.YANGNodeDetails{ + Name: "four", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/config/four", + SchemaPath: "/parent/child/config/four", + ShadowSchemaPath: "/parent/child/state/four", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "Binary", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: "nil", + DefaultValue: nil, + }, + MappedPaths: [][]string{{"config", "four"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: [][]string{{"state", "four"}}, + ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + }, + }, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + }, + "/openconfig-simple/remote-container": { + Name: "RemoteContainer", + Type: ygen.Container, + Path: "/openconfig-simple/remote-container", + Fields: map[string]*ygen.NodeDetails{ + "a-leaf": { + Name: "ALeaf", + YANGDetails: ygen.YANGNodeDetails{ + Name: "a-leaf", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + Path: "/openconfig-simple/remote-container/config/a-leaf", + SchemaPath: "/remote-container/config/a-leaf", + ShadowSchemaPath: "/remote-container/state/a-leaf", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"config", "a-leaf"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: [][]string{{"state", "a-leaf"}}, + ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + }, + }, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + }, + }, + Enums: map[string]*ygen.EnumeratedYANGType{ + "/openconfig-simple/parent-config/three": { + Name: "Child_Three", + Kind: ygen.SimpleEnumerationType, + TypeName: "enumeration", + ValToYANGDetails: []ygot.EnumDefinition{{ + Name: "ONE", + Value: 0, + }, { + Name: "TWO", + Value: 1, + }}, + Flags: map[string]string{"typename": "enumeration"}, + }, + }, + ModelData: []*gpb.ModelData{{Name: "openconfig-remote"}, {Name: "openconfig-simple"}, {Name: "openconfig-simple-augment2"}, {Name: "openconfig-simple-grouping"}}, + }, + }, { + desc: "simple openconfig test compression prefer state no underscores", + inYANGFiles: []string{ + filepath.Join(datapath, "openconfig-simple.yang"), + filepath.Join(datapath, "openconfig-simple-augment2.yang"), + }, + inLangMapper: NewGoLangMapper(true), + inOpts: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferOperationalState, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: false, + GenerateFakeRoot: true, + }, + AppendEnumSuffixForSimpleUnionEnums: true, + }, + wantIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ + "/device": { + Name: "Device", + Type: ygen.Container, + Path: "/device", + Fields: map[string]*ygen.NodeDetails{ + "parent": { + Name: "Parent", + YANGDetails: ygen.YANGNodeDetails{ + Name: "parent", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent", + SchemaPath: "/parent", + LeafrefTargetPath: "", + Description: "I am a parent container\nthat has 4 children.", + }, + Type: ygen.ContainerNode, + MappedPaths: [][]string{{"parent"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "remote-container": { + Name: "RemoteContainer", + YANGDetails: ygen.YANGNodeDetails{ + Name: "remote-container", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + Path: "/openconfig-simple/remote-container", + SchemaPath: "/remote-container", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.ContainerNode, + MappedPaths: [][]string{{"remote-container"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + IsFakeRoot: true, + }, + "/openconfig-simple/parent": { + Name: "Parent", + Type: ygen.Container, + Path: "/openconfig-simple/parent", + Fields: map[string]*ygen.NodeDetails{ + "child": { + Name: "Child", + YANGDetails: ygen.YANGNodeDetails{ + Name: "child", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child", + SchemaPath: "/parent/child", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.ContainerNode, + LangType: nil, + MappedPaths: [][]string{{"child"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + }, + "/openconfig-simple/parent/child": { + Name: "Parent_Child", + Type: ygen.Container, + Path: "/openconfig-simple/parent/child", + Fields: map[string]*ygen.NodeDetails{ + "zero": { + Name: "Zero", + YANGDetails: ygen.YANGNodeDetails{ + Name: "zero", + BelongingModule: "openconfig-simple-augment2", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple-grouping", + Path: "/openconfig-simple/parent/child/state/zero", + SchemaPath: "/parent/child/state/zero", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{NativeType: "string", ZeroValue: `""`}, + MappedPaths: [][]string{{"state", "zero"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple-augment2"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "one": { + Name: "One", + YANGDetails: ygen.YANGNodeDetails{ + Name: "one", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state/one", + SchemaPath: "/parent/child/state/one", + ShadowSchemaPath: "/parent/child/config/one", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"state", "one"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: [][]string{{"config", "one"}}, + ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + }, + "two": { + Name: "Two", + YANGDetails: ygen.YANGNodeDetails{ + Name: "two", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state/two", + SchemaPath: "/parent/child/state/two", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"state", "two"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "three": { + Name: "Three", + YANGDetails: ygen.YANGNodeDetails{ + Name: "three", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state/three", + SchemaPath: "/parent/child/state/three", + ShadowSchemaPath: "/parent/child/config/three", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "E_ChildThree", + UnionTypes: nil, + IsEnumeratedValue: true, + ZeroValue: "0", + DefaultValue: nil, + }, + MappedPaths: [][]string{{"state", "three"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: [][]string{{"config", "three"}}, + ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + }, + "four": { + Name: "Four", + YANGDetails: ygen.YANGNodeDetails{ + Name: "four", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state/four", + SchemaPath: "/parent/child/state/four", + ShadowSchemaPath: "/parent/child/config/four", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "Binary", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: "nil", + DefaultValue: nil, + }, + MappedPaths: [][]string{{"state", "four"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: [][]string{{"config", "four"}}, + ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + }, + }, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + }, + "/openconfig-simple/remote-container": { + Name: "RemoteContainer", + Type: ygen.Container, + Path: "/openconfig-simple/remote-container", + Fields: map[string]*ygen.NodeDetails{ + "a-leaf": { + Name: "ALeaf", + YANGDetails: ygen.YANGNodeDetails{ + Name: "a-leaf", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + Path: "/openconfig-simple/remote-container/state/a-leaf", + SchemaPath: "/remote-container/state/a-leaf", + ShadowSchemaPath: "/remote-container/config/a-leaf", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"state", "a-leaf"}}, + MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + ShadowMappedPaths: [][]string{{"config", "a-leaf"}}, + ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, + }, + }, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + }, + }, + Enums: map[string]*ygen.EnumeratedYANGType{ + "/openconfig-simple/parent-config/three": { + Name: "ChildThree", + Kind: ygen.SimpleEnumerationType, + TypeName: "enumeration", + ValToYANGDetails: []ygot.EnumDefinition{{ + Name: "ONE", + Value: 0, + }, { + Name: "TWO", + Value: 1, + }}, + }, + }, + ModelData: []*gpb.ModelData{{Name: "openconfig-remote"}, {Name: "openconfig-simple"}, {Name: "openconfig-simple-augment2"}, {Name: "openconfig-simple-grouping"}}, + }, + }, { + desc: "simple openconfig test without compression", + inYANGFiles: []string{ + filepath.Join(datapath, "openconfig-simple.yang"), + filepath.Join(datapath, "openconfig-simple-augment2.yang"), + }, + inLangMapper: NewGoLangMapper(true), + inOpts: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.Uncompressed, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + GenerateFakeRoot: true, + }, + AppendEnumSuffixForSimpleUnionEnums: true, + }, + wantIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ + "/device": { + Name: "Device", + Type: ygen.Container, + Path: "/device", + Fields: map[string]*ygen.NodeDetails{ + "parent": { + Name: "Parent", + YANGDetails: ygen.YANGNodeDetails{ + Name: "parent", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent", + SchemaPath: "/parent", + LeafrefTargetPath: "", + Description: "I am a parent container\nthat has 4 children.", + }, + Type: ygen.ContainerNode, + MappedPaths: [][]string{{"parent"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "remote-container": { + Name: "RemoteContainer", + YANGDetails: ygen.YANGNodeDetails{ + Name: "remote-container", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + Path: "/openconfig-simple/remote-container", + SchemaPath: "/remote-container", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.ContainerNode, + MappedPaths: [][]string{{"remote-container"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + IsFakeRoot: true, + }, + "/openconfig-simple/parent": { + Name: "OpenconfigSimple_Parent", + Type: ygen.Container, + Path: "/openconfig-simple/parent", + Fields: map[string]*ygen.NodeDetails{ + "child": { + Name: "Child", + YANGDetails: ygen.YANGNodeDetails{ + Name: "child", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child", + SchemaPath: "/parent/child", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.ContainerNode, + LangType: nil, + MappedPaths: [][]string{{"child"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + }, + "/openconfig-simple/parent/child": { + Name: "OpenconfigSimple_Parent_Child", + Type: ygen.Container, + Path: "/openconfig-simple/parent/child", + Fields: map[string]*ygen.NodeDetails{ + "config": { + Name: "Config", + YANGDetails: ygen.YANGNodeDetails{ + Name: "config", + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/config", + SchemaPath: "/parent/child/config", + LeafrefTargetPath: "", + Description: "", + }, + Type: 1, + MappedPaths: [][]string{{"config"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + }, + "state": { + Name: "State", + YANGDetails: ygen.YANGNodeDetails{ + Name: "state", + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state", + SchemaPath: "/parent/child/state", + LeafrefTargetPath: "", + Description: "", + }, + Type: 1, + MappedPaths: [][]string{{"state"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + }, + }, + ListKeys: nil, + PackageName: "", + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + }, + "/openconfig-simple/parent/child/config": { + Name: "OpenconfigSimple_Parent_Child_Config", + Type: ygen.Container, + Path: "/openconfig-simple/parent/child/config", + Fields: map[string]*ygen.NodeDetails{ + "four": { + Name: "Four", + YANGDetails: ygen.YANGNodeDetails{ + Name: "four", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/config/four", + SchemaPath: "/parent/child/config/four", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "Binary", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: "nil", + DefaultValue: nil, + }, + MappedPaths: [][]string{{"four"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "one": { + Name: "One", + YANGDetails: ygen.YANGNodeDetails{ + Name: "one", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/config/one", + SchemaPath: "/parent/child/config/one", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"one"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "three": { + Name: "Three", + YANGDetails: ygen.YANGNodeDetails{ + Name: "three", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/config/three", + SchemaPath: "/parent/child/config/three", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "E_Simple_Parent_Child_Config_Three", + UnionTypes: nil, + IsEnumeratedValue: true, + ZeroValue: "0", + DefaultValue: nil, + }, + MappedPaths: [][]string{{"three"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + ListKeys: nil, + PackageName: "", + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + }, + "/openconfig-simple/parent/child/state": { + Name: "OpenconfigSimple_Parent_Child_State", + Type: ygen.Container, + Path: "/openconfig-simple/parent/child/state", + Fields: map[string]*ygen.NodeDetails{ + "four": { + Name: "Four", + YANGDetails: ygen.YANGNodeDetails{ + Name: "four", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state/four", + SchemaPath: "/parent/child/state/four", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "Binary", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: "nil", + DefaultValue: nil, + }, + MappedPaths: [][]string{{"four"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "one": { + Name: "One", + YANGDetails: ygen.YANGNodeDetails{ + Name: "one", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state/one", + SchemaPath: "/parent/child/state/one", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"one"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "three": { + Name: "Three", + YANGDetails: ygen.YANGNodeDetails{ + Name: "three", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state/three", + SchemaPath: "/parent/child/state/three", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "E_Simple_Parent_Child_Config_Three", + UnionTypes: nil, + IsEnumeratedValue: true, + ZeroValue: "0", + DefaultValue: nil, + }, + MappedPaths: [][]string{{"three"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "two": { + Name: "Two", + YANGDetails: ygen.YANGNodeDetails{ + Name: "two", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + Path: "/openconfig-simple/parent/child/state/two", + SchemaPath: "/parent/child/state/two", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"two"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "zero": { + Name: "Zero", + YANGDetails: ygen.YANGNodeDetails{ + Name: "zero", + Defaults: nil, + BelongingModule: "openconfig-simple-augment2", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple-grouping", + Path: "/openconfig-simple/parent/child/state/zero", + SchemaPath: "/parent/child/state/zero", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"zero"}}, + MappedPathModules: [][]string{{"openconfig-simple-augment2"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + ListKeys: nil, + PackageName: "", + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-simple", + ConfigFalse: true, + }, + "/openconfig-simple/remote-container": { + Name: "OpenconfigSimple_RemoteContainer", + Type: ygen.Container, + Path: "/openconfig-simple/remote-container", + Fields: map[string]*ygen.NodeDetails{ + "config": { + Name: "Config", + YANGDetails: ygen.YANGNodeDetails{ + Name: "config", + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + Path: "/openconfig-simple/remote-container/config", + SchemaPath: "/remote-container/config", + LeafrefTargetPath: "", + Description: "", + }, + Type: 1, + MappedPaths: [][]string{{"config"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + }, + "state": { + Name: "State", + YANGDetails: ygen.YANGNodeDetails{ + Name: "state", + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + Path: "/openconfig-simple/remote-container/state", + SchemaPath: "/remote-container/state", + LeafrefTargetPath: "", + Description: "", + }, + Type: 1, + MappedPaths: [][]string{{"state"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + }, + }, + ListKeys: nil, + PackageName: "", + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + }, + "/openconfig-simple/remote-container/config": { + Name: "OpenconfigSimple_RemoteContainer_Config", + Type: ygen.Container, + Path: "/openconfig-simple/remote-container/config", + Fields: map[string]*ygen.NodeDetails{ + "a-leaf": { + Name: "ALeaf", + YANGDetails: ygen.YANGNodeDetails{ + Name: "a-leaf", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + Path: "/openconfig-simple/remote-container/config/a-leaf", + SchemaPath: "/remote-container/config/a-leaf", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"a-leaf"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + }, + "/openconfig-simple/remote-container/state": { + Name: "OpenconfigSimple_RemoteContainer_State", + Type: ygen.Container, + Path: "/openconfig-simple/remote-container/state", + Fields: map[string]*ygen.NodeDetails{ + "a-leaf": { + Name: "ALeaf", + YANGDetails: ygen.YANGNodeDetails{ + Name: "a-leaf", + Defaults: nil, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + Path: "/openconfig-simple/remote-container/state/a-leaf", + SchemaPath: "/remote-container/state/a-leaf", + LeafrefTargetPath: "", + Description: "", + }, + Type: 3, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"a-leaf"}}, + MappedPathModules: [][]string{{"openconfig-simple"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + BelongingModule: "openconfig-simple", + RootElementModule: "openconfig-simple", + DefiningModule: "openconfig-remote", + ConfigFalse: true, + }, + }, + Enums: map[string]*ygen.EnumeratedYANGType{ + "/openconfig-simple/parent-config/three": { + Name: "Simple_Parent_Child_Config_Three", + Kind: 1, + TypeName: "enumeration", + ValToYANGDetails: []ygot.EnumDefinition{{ + Name: "ONE", + Value: 0, + }, { + Name: "TWO", + Value: 1, + }}, + }, + }, + ModelData: []*gpb.ModelData{{Name: "openconfig-remote"}, {Name: "openconfig-simple"}, {Name: "openconfig-simple-augment2"}, {Name: "openconfig-simple-grouping"}}, + }, + }, { + desc: "exclude module test with compression", + inYANGFiles: []string{filepath.Join(datapath, "excluded-module-noimport.yang")}, + inExcludeModules: []string{"excluded-module-two"}, + inLangMapper: NewGoLangMapper(true), + inOpts: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + GenerateFakeRoot: true, + }, + AppendEnumSuffixForSimpleUnionEnums: true, + }, + wantIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ + "/device": { + Name: "Device", + Type: ygen.Container, + Path: "/device", + Fields: map[string]*ygen.NodeDetails{ + "e1": { + Name: "E1", + YANGDetails: ygen.YANGNodeDetails{ + Name: "e1", + Defaults: nil, + BelongingModule: "excluded-module-noimport", + RootElementModule: "excluded-module-noimport", + DefiningModule: "excluded-module-noimport", + Path: "/excluded-module-noimport/e1", + SchemaPath: "/e1", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "string", + UnionTypes: nil, + IsEnumeratedValue: false, + ZeroValue: `""`, + DefaultValue: nil, + }, + MappedPaths: [][]string{{"e1"}}, + MappedPathModules: [][]string{{"excluded-module-noimport"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + IsFakeRoot: true, + }, + }, + // TODO(wenbli): Determine whether "excluded-module-two" should be here. + ModelData: []*gpb.ModelData{{Name: "excluded-module-noimport"}, {Name: "excluded-module-two"}}, + }, + }, { + desc: "complex openconfig test with compression", + inYANGFiles: []string{filepath.Join(datapath, "openconfig-complex.yang")}, + inLangMapper: NewGoLangMapper(true), + inOpts: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + GenerateFakeRoot: true, + }, + AppendEnumSuffixForSimpleUnionEnums: true, + }, + wantIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ + "/device": { + Name: "Device", + Type: ygen.Container, + Path: "/device", + Fields: map[string]*ygen.NodeDetails{ + "model": { + Name: "Model", + YANGDetails: ygen.YANGNodeDetails{ + Name: "model", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model", + SchemaPath: "/model", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.ContainerNode, + LangType: nil, + MappedPaths: [][]string{{"model"}}, + MappedPathModules: [][]string{{"openconfig-complex"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "example-presence": { + Name: "ExamplePresence", + YANGDetails: ygen.YANGNodeDetails{ + Name: "example-presence", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/example-presence", + SchemaPath: "/example-presence", + LeafrefTargetPath: "", + PresenceStatement: ygot.String("This is an example presence container"), + Description: "", + }, + Type: ygen.ContainerNode, + LangType: nil, + MappedPaths: [][]string{{"example-presence"}}, + MappedPathModules: [][]string{{"openconfig-complex"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + }, + IsFakeRoot: true, + }, + "/openconfig-complex/example-presence": { + Name: "ExamplePresence", + Type: ygen.Container, + Path: "/openconfig-complex/example-presence", + Fields: map[string]*ygen.NodeDetails{}, + PackageName: "", + ListKeys: nil, + IsFakeRoot: false, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + }, + "/openconfig-complex/model": { + Name: "Model", + Type: ygen.Container, + Path: "/openconfig-complex/model", + Fields: map[string]*ygen.NodeDetails{ + "anydata-leaf": { + Name: "AnydataLeaf", + YANGDetails: ygen.YANGNodeDetails{ + Name: "anydata-leaf", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/anydata-leaf", + SchemaPath: "/model/anydata-leaf", + LeafrefTargetPath: "", + Description: "some anydata leaf", + }, + Type: ygen.AnyDataNode, + LangType: nil, + MappedPaths: [][]string{{"anydata-leaf"}}, + MappedPathModules: [][]string{{"openconfig-complex"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "dateref": { + Name: "Dateref", + YANGDetails: ygen.YANGNodeDetails{ + Name: "dateref", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/dateref", + SchemaPath: "/model/dateref", + LeafrefTargetPath: "/openconfig-complex/model/a/single-key/config/dates", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "uint8", + ZeroValue: "0", + DefaultValue: ygot.String("5"), + }, + MappedPaths: [][]string{{"dateref"}}, + MappedPathModules: [][]string{{"openconfig-complex"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "multi-key": { + Name: "MultiKey", + YANGDetails: ygen.YANGNodeDetails{ + Name: "multi-key", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/b/multi-key", + SchemaPath: "/model/b/multi-key", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.ListNode, + LangType: nil, + MappedPaths: [][]string{{"b", "multi-key"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "single-key": { + Name: "SingleKey", + YANGDetails: ygen.YANGNodeDetails{ + Name: "single-key", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key", + SchemaPath: "/model/a/single-key", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.ListNode, + LangType: nil, + MappedPaths: [][]string{{"a", "single-key"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: nil, + ShadowMappedPathModules: nil, + }, + "unkeyed-list": { + Name: "UnkeyedList", + YANGDetails: ygen.YANGNodeDetails{ + Name: "unkeyed-list", + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/c/unkeyed-list", + SchemaPath: "/model/c/unkeyed-list", + }, + Type: ygen.ListNode, + MappedPaths: [][]string{{"c", "unkeyed-list"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, + }, + ListKeys: nil, + PackageName: "", + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + }, + "/openconfig-complex/model/a/single-key": { + Name: "Model_SingleKey", + Type: ygen.List, + Path: "/openconfig-complex/model/a/single-key", + Fields: map[string]*ygen.NodeDetails{ + "dates": { + Name: "Dates", + YANGDetails: ygen.YANGNodeDetails{ + Name: "dates", + Defaults: []string{"5"}, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/dates", + SchemaPath: "/model/a/single-key/config/dates", + ShadowSchemaPath: "/model/a/single-key/state/dates", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafListNode, + LangType: &ygen.MappedType{NativeType: "uint8", ZeroValue: "0", DefaultValue: ygot.String("[]uint8{5}")}, + MappedPaths: [][]string{{"config", "dates"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "dates"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, + "dates-with-defaults": { + Name: "DatesWithDefaults", + YANGDetails: ygen.YANGNodeDetails{ + Name: "dates-with-defaults", + Defaults: []string{"1", "2"}, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/dates-with-defaults", + SchemaPath: "/model/a/single-key/config/dates-with-defaults", + ShadowSchemaPath: "/model/a/single-key/state/dates-with-defaults", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafListNode, + LangType: &ygen.MappedType{NativeType: "uint8", ZeroValue: "0", DefaultValue: ygot.String("[]uint8{1, 2}")}, + MappedPaths: [][]string{{"config", "dates-with-defaults"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "dates-with-defaults"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, + "iref": { + Name: "Iref", + YANGDetails: ygen.YANGNodeDetails{ + Name: "iref", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/iref", + SchemaPath: "/model/a/single-key/config/iref", + ShadowSchemaPath: "/model/a/single-key/state/iref", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "E_Complex_SOFTWARE", + IsEnumeratedValue: true, + ZeroValue: "0", + }, + MappedPaths: [][]string{{"config", "iref"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "iref"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, + "key": { + Name: "Key", + YANGDetails: ygen.YANGNodeDetails{ + Name: "key", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/key", + SchemaPath: "/model/a/single-key/config/key", + ShadowSchemaPath: "/model/a/single-key/state/key", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "Model_SingleKey_Key_Union", + UnionTypes: map[string]int{ + "E_Complex_WeekendDays": 1, + "uint8": 0, + }, + ZeroValue: "nil", + }, + MappedPaths: [][]string{{"config", "key"}, {"key"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "key"}, {"key"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, + }, + "leaf-default-override": { + Name: "LeafDefaultOverride", + YANGDetails: ygen.YANGNodeDetails{ + Name: "leaf-default-override", + Defaults: []string{"3"}, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/leaf-default-override", + SchemaPath: "/model/a/single-key/config/leaf-default-override", + ShadowSchemaPath: "/model/a/single-key/state/leaf-default-override", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "Model_SingleKey_LeafDefaultOverride_Union", + UnionTypes: map[string]int{ + "E_Complex_CycloneScales_Enum": 1, + "uint8": 0, + }, + ZeroValue: "nil", + DefaultValue: ygot.String("UnionUint8(3)"), + }, + MappedPaths: [][]string{{"config", "leaf-default-override"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "leaf-default-override"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, + "simple-union-enum": { + Name: "SimpleUnionEnum", + YANGDetails: ygen.YANGNodeDetails{ + Name: "simple-union-enum", + Defaults: []string{"TWO"}, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/simple-union-enum", + SchemaPath: "/model/a/single-key/config/simple-union-enum", + ShadowSchemaPath: "/model/a/single-key/state/simple-union-enum", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "Model_SingleKey_SimpleUnionEnum_Union", + UnionTypes: map[string]int{ + "E_SingleKey_SimpleUnionEnum_Enum": 1, + "uint64": 0, + }, + ZeroValue: "nil", + DefaultValue: ygot.String("SingleKey_SimpleUnionEnum_Enum_TWO"), + }, + MappedPaths: [][]string{{"config", "simple-union-enum"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "simple-union-enum"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, + "singleton-union-enum": { + Name: "SingletonUnionEnum", + YANGDetails: ygen.YANGNodeDetails{ + Name: "singleton-union-enum", + Defaults: []string{"DEUX"}, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/singleton-union-enum", + SchemaPath: "/model/a/single-key/config/singleton-union-enum", + ShadowSchemaPath: "/model/a/single-key/state/singleton-union-enum", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "E_SingleKey_SingletonUnionEnum_Enum", + UnionTypes: map[string]int{ + "E_SingleKey_SingletonUnionEnum_Enum": 0, + }, + IsEnumeratedValue: true, + ZeroValue: "0", + DefaultValue: ygot.String("SingleKey_SingletonUnionEnum_Enum_DEUX"), + }, + MappedPaths: [][]string{{"config", "singleton-union-enum"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "singleton-union-enum"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, + "typedef-enum": { + Name: "TypedefEnum", + YANGDetails: ygen.YANGNodeDetails{ + Name: "typedef-enum", + Defaults: []string{"SATURDAY"}, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/typedef-enum", + SchemaPath: "/model/a/single-key/config/typedef-enum", + ShadowSchemaPath: "/model/a/single-key/state/typedef-enum", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "E_Complex_WeekendDays", + IsEnumeratedValue: true, + ZeroValue: "0", + DefaultValue: ygot.String("Complex_WeekendDays_SATURDAY"), + }, + MappedPaths: [][]string{{"config", "typedef-enum"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "typedef-enum"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, + "typedef-union-enum": { + Name: "TypedefUnionEnum", + YANGDetails: ygen.YANGNodeDetails{ + Name: "typedef-union-enum", + Defaults: []string{"SUPER"}, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/typedef-union-enum", + SchemaPath: "/model/a/single-key/config/typedef-union-enum", + ShadowSchemaPath: "/model/a/single-key/state/typedef-union-enum", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "Model_SingleKey_TypedefUnionEnum_Union", + UnionTypes: map[string]int{ + "E_Complex_CycloneScales_Enum": 1, + "uint8": 0, + }, + ZeroValue: "nil", + DefaultValue: ygot.String("Complex_CycloneScales_Enum_SUPER"), + }, + MappedPaths: [][]string{{"config", "typedef-union-enum"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "typedef-union-enum"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, + }, + ListKeys: map[string]*ygen.ListKey{ + "key": { + Name: "Key", + LangType: &ygen.MappedType{ + NativeType: "Model_SingleKey_Key_Union", + UnionTypes: map[string]int{ + "E_Complex_WeekendDays": 1, + "uint8": 0, + }, + ZeroValue: "nil", + }, + }, + }, + ListKeyYANGNames: []string{"key"}, + PackageName: "", + IsFakeRoot: false, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + }, + "/openconfig-complex/model/b/multi-key": { + Name: "Model_MultiKey", + Type: ygen.List, + Path: "/openconfig-complex/model/b/multi-key", + Fields: map[string]*ygen.NodeDetails{ + "key1": { + Name: "Key1", + YANGDetails: ygen.YANGNodeDetails{ + Name: "key1", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/b/multi-key/config/key1", + SchemaPath: "/model/b/multi-key/config/key1", + ShadowSchemaPath: "/model/b/multi-key/state/key1", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "uint32", + UnionTypes: map[string]int{"uint32": 0}, + ZeroValue: "0", + }, + MappedPaths: [][]string{{"config", "key1"}, {"key1"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "key1"}, {"key1"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, + }, + "key2": { + Name: "Key2", + YANGDetails: ygen.YANGNodeDetails{ + Name: "key2", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/b/multi-key/config/key2", + SchemaPath: "/model/b/multi-key/config/key2", + ShadowSchemaPath: "/model/b/multi-key/state/key2", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{NativeType: "E_MultiKey_Key2", IsEnumeratedValue: true, ZeroValue: "0"}, + MappedPaths: [][]string{{"config", "key2"}, {"key2"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "key2"}, {"key2"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, + }, + }, + ListKeys: map[string]*ygen.ListKey{ + "key1": { + Name: "Key1", + LangType: &ygen.MappedType{ + NativeType: "uint32", + UnionTypes: map[string]int{"uint32": 0}, + ZeroValue: "0", + }, + }, + "key2": { + Name: "Key2", + LangType: &ygen.MappedType{NativeType: "E_MultiKey_Key2", IsEnumeratedValue: true, ZeroValue: "0"}, + }, + }, + ListKeyYANGNames: []string{"key1", "key2"}, + PackageName: "", + IsFakeRoot: false, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + }, + "/openconfig-complex/model/c/unkeyed-list": { + Name: "Model_UnkeyedList", + Type: ygen.List, + Path: "/openconfig-complex/model/c/unkeyed-list", + Fields: map[string]*ygen.NodeDetails{ + "field": { + Name: "Field", + YANGDetails: ygen.YANGNodeDetails{ + Name: "field", + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/c/unkeyed-list/field", + SchemaPath: "/model/c/unkeyed-list/field", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{NativeType: "Binary", ZeroValue: "nil"}, + MappedPaths: [][]string{{"field"}}, + MappedPathModules: [][]string{{"openconfig-complex"}}, + }, + }, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + ConfigFalse: true, + }, + }, + Enums: map[string]*ygen.EnumeratedYANGType{ + "/openconfig-complex/cyclone-scales": { + Name: "Complex_CycloneScales_Enum", + Kind: ygen.DerivedUnionEnumerationType, + TypeName: "cyclone-scales", + ValToYANGDetails: []ygot.EnumDefinition{ + { + Name: "NORMAL", + DefiningModule: "", + Value: 0, + }, + { + Name: "SUPER", + DefiningModule: "", + Value: 1, + }, + }, + }, + "/openconfig-complex/SOFTWARE": { + Name: "Complex_SOFTWARE", + Kind: ygen.IdentityType, + IdentityBaseName: "SOFTWARE", + TypeName: "identityref", + ValToYANGDetails: []ygot.EnumDefinition{ + {Name: "OS", DefiningModule: "openconfig-complex"}, + }, + }, + "/openconfig-complex/multi-key-config/key2": { + Name: "MultiKey_Key2", + Kind: ygen.SimpleEnumerationType, + TypeName: "enumeration", + ValToYANGDetails: []ygot.EnumDefinition{ + { + Name: "RED", + DefiningModule: "", + Value: 0, + }, + { + Name: "BLUE", + DefiningModule: "", + Value: 1, + }, + }, + }, + "/openconfig-complex/weekend-days": { + Name: "Complex_WeekendDays", + Kind: ygen.DerivedEnumerationType, + TypeName: "weekend-days", + ValToYANGDetails: []ygot.EnumDefinition{ + { + Name: "SATURDAY", + DefiningModule: "", + Value: 0, + }, + { + Name: "SUNDAY", + DefiningModule: "", + Value: 1, + }, + }, + }, + "/openconfig-complex/single-key-config/simple-union-enum": { + Name: "SingleKey_SimpleUnionEnum_Enum", + Kind: ygen.UnionEnumerationType, + TypeName: "union", + ValToYANGDetails: []ygot.EnumDefinition{ + { + Name: "ONE", + DefiningModule: "", + Value: 0, + }, + { + Name: "TWO", + DefiningModule: "", + Value: 1, + }, + { + Name: "THREE", + DefiningModule: "", + Value: 2, + }, + }, + }, + "/openconfig-complex/single-key-config/singleton-union-enum": { + Name: "SingleKey_SingletonUnionEnum_Enum", + Kind: ygen.UnionEnumerationType, + TypeName: "union", + ValToYANGDetails: []ygot.EnumDefinition{ + { + Name: "UN", + DefiningModule: "", + Value: 0, + }, + { + Name: "DEUX", + DefiningModule: "", + Value: 1, + }, + { + Name: "TROIS", + DefiningModule: "", + Value: 2, + }, + }, + }, + }, + ModelData: []*gpb.ModelData{{Name: "openconfig-complex"}}, + }, + }} + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + tt.inOpts.ParseOptions.ExcludeModules = tt.inExcludeModules + got, err := ygen.GenerateIR(tt.inYANGFiles, tt.inIncludePaths, tt.inLangMapper, tt.inOpts) + if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" { + t.Fatalf("did not get expected error, %s", diff) + } + if diff := cmp.Diff(got, tt.wantIR, cmpopts.IgnoreUnexported(ygen.IR{}, ygen.ParsedDirectory{}, ygen.EnumeratedYANGType{}), protocmp.Transform()); diff != "" { + t.Fatalf("did not get expected IR, diff(-got,+want):\n%s", diff) + } + }) + } +} diff --git a/ygen/goelements.go b/gogen/goelements.go similarity index 91% rename from ygen/goelements.go rename to gogen/goelements.go index 0e374d0fd..5aef3390b 100644 --- a/ygen/goelements.go +++ b/gogen/goelements.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ygen +package gogen import ( "bytes" @@ -23,14 +23,13 @@ import ( "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/internal/igenutil" "github.com/openconfig/ygot/util" + "github.com/openconfig/ygot/ygen" "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" ) -// TODO(wenbli): Split the contents of this file out into a separate package -// once refactoring of Go generation has been completed. - const ( // goEnumPrefix is the prefix that is used for type names in the output // Go code, such that an enumeration's name is of the form @@ -128,7 +127,7 @@ var ( ) // Ensure at compile time that the GoLangMapper implements the LangMapper interface. -var _ LangMapper = &GoLangMapper{} +var _ ygen.LangMapper = &GoLangMapper{} // GoLangMapper contains the functionality and state for generating Go names for // the generated code. @@ -136,7 +135,7 @@ type GoLangMapper struct { // LangMapperBase being embedded is a requirement for GoLangMapper to // implement the LangMapper interface, and also gives it access to // built-in methods. - LangMapperBase + ygen.LangMapperBase // definedGlobals specifies the global Go names used during code // generation to avoid conflicts. @@ -155,7 +154,7 @@ type GoLangMapper struct { // UnimplementedLangMapperExt ensures GoLangMapper implements the // LangMapperExt interface for forwards compatibility. - UnimplementedLangMapperExt + ygen.UnimplementedLangMapperExt } // NewGoLangMapper creates a new GoLangMapper instance, initialised with the @@ -195,7 +194,7 @@ type resolveTypeArgs struct { func pathToCamelCaseName(e *yang.Entry, compressOCPaths bool) string { var pathElements []*yang.Entry - if IsFakeRoot(e) { + if igenutil.IsFakeRoot(e) { // Handle the special case of the root element if it exists. pathElements = []*yang.Entry{e} } else { @@ -255,7 +254,7 @@ func (s *GoLangMapper) FieldName(e *yang.Entry) (string, error) { // LeafType maps the input leaf entry to a MappedType object containing the // type information about the field. -func (s *GoLangMapper) LeafType(e *yang.Entry, opts IROptions) (*MappedType, error) { +func (s *GoLangMapper) LeafType(e *yang.Entry, opts ygen.IROptions) (*ygen.MappedType, error) { mtype, err := s.yangTypeToGoType(resolveTypeArgs{yangType: e.Type, contextEntry: e}, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.TransformationOptions.EnumOrgPrefixesToTrim) if err != nil { return nil, err @@ -272,7 +271,7 @@ func (s *GoLangMapper) LeafType(e *yang.Entry, opts IROptions) (*MappedType, err // LeafType maps the input list key entry to a MappedType object containing the // type information about the key field. -func (s *GoLangMapper) KeyLeafType(e *yang.Entry, opts IROptions) (*MappedType, error) { +func (s *GoLangMapper) KeyLeafType(e *yang.Entry, opts ygen.IROptions) (*ygen.MappedType, error) { return s.yangTypeToGoType(resolveTypeArgs{yangType: e.Type, contextEntry: e}, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.TransformationOptions.EnumOrgPrefixesToTrim) } @@ -292,10 +291,10 @@ func (s *GoLangMapper) PackageName(*yang.Entry, genutil.CompressBehaviour, bool) // The skipEnumDedup argument specifies whether leaves of type enumeration that are // used more than once in the schema should share a common type. By default, a single // type for each leaf is created. -func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (*MappedType, error) { +func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (*ygen.MappedType, error) { defVal := genutil.TypeDefaultValue(args.yangType) // Handle the case of a typedef which is actually an enumeration. - typedefName, _, err := s.EnumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) + typedefName, _, err := s.EnumeratedTypedefTypeName(args.yangType, args.contextEntry, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) if err != nil { // err is non nil when this was a typedef which included // an invalid enumerated type. @@ -303,7 +302,7 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s } if typedefName != "" { - return &MappedType{ + return &ygen.MappedType{ NativeType: typedefName, IsEnumeratedValue: true, // mtype is set to non-nil when this was a valid enumeration @@ -317,31 +316,31 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s // Perform the actual mapping of the type to the Go type. switch args.yangType.Kind { case yang.Yint8: - return &MappedType{NativeType: "int8", ZeroValue: goZeroValues["int8"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "int8", ZeroValue: goZeroValues["int8"], DefaultValue: defVal}, nil case yang.Yint16: - return &MappedType{NativeType: "int16", ZeroValue: goZeroValues["int16"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "int16", ZeroValue: goZeroValues["int16"], DefaultValue: defVal}, nil case yang.Yint32: - return &MappedType{NativeType: "int32", ZeroValue: goZeroValues["int32"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "int32", ZeroValue: goZeroValues["int32"], DefaultValue: defVal}, nil case yang.Yint64: - return &MappedType{NativeType: "int64", ZeroValue: goZeroValues["int64"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "int64", ZeroValue: goZeroValues["int64"], DefaultValue: defVal}, nil case yang.Yuint8: - return &MappedType{NativeType: "uint8", ZeroValue: goZeroValues["uint8"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "uint8", ZeroValue: goZeroValues["uint8"], DefaultValue: defVal}, nil case yang.Yuint16: - return &MappedType{NativeType: "uint16", ZeroValue: goZeroValues["uint16"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "uint16", ZeroValue: goZeroValues["uint16"], DefaultValue: defVal}, nil case yang.Yuint32: - return &MappedType{NativeType: "uint32", ZeroValue: goZeroValues["uint32"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "uint32", ZeroValue: goZeroValues["uint32"], DefaultValue: defVal}, nil case yang.Yuint64: - return &MappedType{NativeType: "uint64", ZeroValue: goZeroValues["uint64"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "uint64", ZeroValue: goZeroValues["uint64"], DefaultValue: defVal}, nil case yang.Ybool: - return &MappedType{NativeType: "bool", ZeroValue: goZeroValues["bool"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "bool", ZeroValue: goZeroValues["bool"], DefaultValue: defVal}, nil case yang.Yempty: // Empty is a YANG type that either exists or doesn't, therefore // map it to a boolean to indicate its presence or not. The empty // type name uses a specific name in the generated code, such that // it can be identified for marshalling. - return &MappedType{NativeType: ygot.EmptyTypeName, ZeroValue: goZeroValues[ygot.EmptyTypeName]}, nil + return &ygen.MappedType{NativeType: ygot.EmptyTypeName, ZeroValue: goZeroValues[ygot.EmptyTypeName]}, nil case yang.Ystring: - return &MappedType{NativeType: "string", ZeroValue: goZeroValues["string"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "string", ZeroValue: goZeroValues["string"], DefaultValue: defVal}, nil case yang.Yunion: // A YANG Union is a leaf that can take multiple values - its subtypes need // to be extracted. @@ -357,7 +356,7 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s if err != nil { return nil, err } - return &MappedType{ + return &ygen.MappedType{ NativeType: fmt.Sprintf("%s%s", goEnumPrefix, n), IsEnumeratedValue: true, ZeroValue: "0", @@ -374,14 +373,14 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s if err != nil { return nil, err } - return &MappedType{ + return &ygen.MappedType{ NativeType: fmt.Sprintf("%s%s", goEnumPrefix, n), IsEnumeratedValue: true, ZeroValue: "0", DefaultValue: defVal, }, nil case yang.Ydecimal64: - return &MappedType{NativeType: "float64", ZeroValue: goZeroValues["float64"], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: "float64", ZeroValue: goZeroValues["float64"], DefaultValue: defVal}, nil case yang.Yleafref: // This is a leafref, so we check what the type of the leaf that it // references is by looking it up. @@ -398,13 +397,13 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s // Map binary fields to the Binary type defined in the output code, // this is used to ensure that we can distinguish a binary field from // a leaf-list of uint8s which is not possible if mapping to []byte. - return &MappedType{NativeType: ygot.BinaryTypeName, ZeroValue: goZeroValues[ygot.BinaryTypeName], DefaultValue: defVal}, nil + return &ygen.MappedType{NativeType: ygot.BinaryTypeName, ZeroValue: goZeroValues[ygot.BinaryTypeName], DefaultValue: defVal}, nil default: // Return an empty interface for the types that we do not currently // support. Back-end validation is required for these types. // TODO(robjs): Missing types currently bits. These // should be added. - return &MappedType{NativeType: "interface{}", ZeroValue: goZeroValues["interface{}"]}, nil + return &ygen.MappedType{NativeType: "interface{}", ZeroValue: goZeroValues["interface{}"]}, nil } } @@ -442,9 +441,9 @@ func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, s // but used in multiple places. // // goUnionType returns an error if mapping is not possible. -func (s *GoLangMapper) goUnionType(args resolveTypeArgs, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (*MappedType, error) { +func (s *GoLangMapper) goUnionType(args resolveTypeArgs, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (*ygen.MappedType, error) { var errs []error - unionMappedTypes := make(map[int]*MappedType) + unionMappedTypes := make(map[int]*ygen.MappedType) // Extract the subtypes that are defined into a map which is keyed on the // mapped type. A map is used such that other functions that rely checking @@ -459,7 +458,7 @@ func (s *GoLangMapper) goUnionType(args resolveTypeArgs, compressOCPaths, skipEn return nil, fmt.Errorf("errors mapping element: %v", errs) } - resolvedType := &MappedType{ + resolvedType := &ygen.MappedType{ NativeType: fmt.Sprintf("%s_Union", pathToCamelCaseName(args.contextEntry, compressOCPaths)), // Zero value is set to nil, other than in cases where there is // a single type in the union. @@ -486,7 +485,7 @@ func (s *GoLangMapper) goUnionType(args resolveTypeArgs, compressOCPaths, skipEn // The skipEnumDedup argument specifies whether the current code generation is // de-duplicating enumerations where they are used in more than one place in // the schema. -func (s *GoLangMapper) goUnionSubTypes(subtype *yang.YangType, ctx *yang.Entry, currentTypes map[string]int, unionMappedTypes map[int]*MappedType, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) []error { +func (s *GoLangMapper) goUnionSubTypes(subtype *yang.YangType, ctx *yang.Entry, currentTypes map[string]int, unionMappedTypes map[int]*ygen.MappedType, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) []error { var errs []error // If subtype.Type is not empty then this means that this type is defined to // be a union itself. @@ -499,7 +498,7 @@ func (s *GoLangMapper) goUnionSubTypes(subtype *yang.YangType, ctx *yang.Entry, contextType := subtype - var mtype *MappedType + var mtype *ygen.MappedType switch subtype.Kind { case yang.Yidentityref: // Handle the specific case that the context entry is now not the correct entry @@ -511,7 +510,7 @@ func (s *GoLangMapper) goUnionSubTypes(subtype *yang.YangType, ctx *yang.Entry, return append(errs, err) } defVal := genutil.TypeDefaultValue(subtype) - mtype = &MappedType{ + mtype = &ygen.MappedType{ NativeType: fmt.Sprintf("%s%s", goEnumPrefix, baseType), IsEnumeratedValue: true, ZeroValue: "0", @@ -542,7 +541,7 @@ func (s *GoLangMapper) goUnionSubTypes(subtype *yang.YangType, ctx *yang.Entry, // generateGoDefaultValue returns a pointer to a Go literal that represents the // default value for the entry. If there is no default value for the field, nil // is returned. -func generateGoDefaultValue(field *yang.Entry, mtype *MappedType, gogen *GoLangMapper, compressPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string, simpleUnions bool) (*string, error) { +func generateGoDefaultValue(field *yang.Entry, mtype *ygen.MappedType, gogen *GoLangMapper, compressPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string, simpleUnions bool) (*string, error) { // Set the default type to the mapped Go type. defaultValues := field.DefaultValues() if len(defaultValues) == 0 && mtype.DefaultValue != nil { @@ -606,7 +605,7 @@ func generateGoDefaultValue(field *yang.Entry, mtype *MappedType, gogen *GoLangM // type for each leaf is created. func (s *GoLangMapper) yangDefaultValueToGo(value string, args resolveTypeArgs, isSingletonUnion, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (string, yang.TypeKind, error) { // Handle the case of a typedef which is actually an enumeration. - typedefName, _, err := s.EnumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) + typedefName, _, err := s.EnumeratedTypedefTypeName(args.yangType, args.contextEntry, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) if err != nil { // err is non nil when this was a typedef which included // an invalid enumerated type. diff --git a/ygen/goelements_test.go b/gogen/goelements_test.go similarity index 94% rename from ygen/goelements_test.go rename to gogen/goelements_test.go index b841c0426..611f1028e 100644 --- a/ygen/goelements_test.go +++ b/gogen/goelements_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ygen +package gogen import ( "encoding/base64" @@ -23,6 +23,8 @@ import ( "github.com/kylelemons/godebug/pretty" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/internal/igenutil" + "github.com/openconfig/ygot/ygen" "github.com/openconfig/ygot/ygot" ) @@ -41,7 +43,7 @@ func TestUnionSubTypes(t *testing.T) { // entry as a parameter to goUnionSubTypes without the context entry. inNoContext bool want []string - wantMtypes map[int]*MappedType + wantMtypes map[int]*ygen.MappedType wantErr bool }{{ name: "union of strings", @@ -57,7 +59,7 @@ func TestUnionSubTypes(t *testing.T) { }, }, want: []string{"string"}, - wantMtypes: map[int]*MappedType{ + wantMtypes: map[int]*ygen.MappedType{ 0: { NativeType: "string", UnionTypes: nil, @@ -80,7 +82,7 @@ func TestUnionSubTypes(t *testing.T) { }, }, want: []string{"int8", "string"}, - wantMtypes: map[int]*MappedType{ + wantMtypes: map[int]*ygen.MappedType{ 0: { NativeType: "int8", UnionTypes: nil, @@ -122,7 +124,7 @@ func TestUnionSubTypes(t *testing.T) { }, }, want: []string{"string", "int32", "uint64", "int16"}, - wantMtypes: map[int]*MappedType{ + wantMtypes: map[int]*ygen.MappedType{ 0: { NativeType: "string", UnionTypes: nil, @@ -218,7 +220,7 @@ func TestUnionSubTypes(t *testing.T) { }, }, want: []string{"E_TypedefModule_DerivedEnum", "int16"}, - wantMtypes: map[int]*MappedType{ + wantMtypes: map[int]*ygen.MappedType{ 0: { NativeType: "E_TypedefModule_DerivedEnum", IsEnumeratedValue: true, @@ -263,7 +265,7 @@ func TestUnionSubTypes(t *testing.T) { }, }, want: []string{"E_UnionModule_DerivedUnion_Enum", "int16"}, - wantMtypes: map[int]*MappedType{ + wantMtypes: map[int]*ygen.MappedType{ 0: { NativeType: "E_UnionModule_DerivedUnion_Enum", IsEnumeratedValue: true, @@ -317,7 +319,7 @@ func TestUnionSubTypes(t *testing.T) { }, }, want: []string{"E_TypedefModule_DerivedEnum", "int16"}, - wantMtypes: map[int]*MappedType{ + wantMtypes: map[int]*ygen.MappedType{ 0: { NativeType: "E_TypedefModule_DerivedEnum", IsEnumeratedValue: true, @@ -350,7 +352,7 @@ func TestUnionSubTypes(t *testing.T) { }, }, want: []string{"E_BaseModule_UnionLeaf_Enum"}, - wantMtypes: map[int]*MappedType{ + wantMtypes: map[int]*ygen.MappedType{ 0: { NativeType: "E_BaseModule_UnionLeaf_Enum", IsEnumeratedValue: true, @@ -383,7 +385,7 @@ func TestUnionSubTypes(t *testing.T) { }, }, want: []string{"E_Basemod_Id", "E_Basemod2_Id2"}, - wantMtypes: map[int]*MappedType{ + wantMtypes: map[int]*ygen.MappedType{ 0: { NativeType: "E_Basemod_Id", UnionTypes: nil, @@ -427,7 +429,7 @@ func TestUnionSubTypes(t *testing.T) { }, }, want: []string{"E_BaseModule_BaseIdentity"}, - wantMtypes: map[int]*MappedType{ + wantMtypes: map[int]*ygen.MappedType{ 0: { NativeType: "E_BaseModule_BaseIdentity", UnionTypes: nil, @@ -445,7 +447,7 @@ func TestUnionSubTypes(t *testing.T) { t.Fatal(err) } - mtypes := make(map[int]*MappedType) + mtypes := make(map[int]*ygen.MappedType) ctypes := make(map[string]int) ctxEntry := tt.inCtxEntry if tt.inNoContext { @@ -477,7 +479,7 @@ func TestUnionSubTypes(t *testing.T) { } } - if diff := cmp.Diff(tt.wantMtypes, mtypes, cmp.AllowUnexported(MappedType{}), cmpopts.EquateEmpty()); diff != "" { + if diff := cmp.Diff(tt.wantMtypes, mtypes, cmp.AllowUnexported(ygen.MappedType{}), cmpopts.EquateEmpty()); diff != "" { t.Errorf("mtypes not as expected (-want, +got):\n%s", diff) } }) @@ -495,48 +497,48 @@ func TestYangTypeToGoType(t *testing.T) { inEnumEntries []*yang.Entry // inEnumEntries is used to add more state for findEnumSet to test enum name generation. inSkipEnumDedup bool inCompressPath bool - want *MappedType + want *ygen.MappedType wantErr bool }{{ name: "simple lookup resolution", in: &yang.YangType{Kind: yang.Yint32, Name: "int32"}, - want: &MappedType{NativeType: "int32", ZeroValue: "0"}, + want: &ygen.MappedType{NativeType: "int32", ZeroValue: "0"}, }, { name: "int32 with default", in: &yang.YangType{Kind: yang.Yint32, Name: "int32", Default: "42"}, - want: &MappedType{NativeType: "int32", ZeroValue: "0", DefaultValue: ygot.String("42")}, + want: &ygen.MappedType{NativeType: "int32", ZeroValue: "0", DefaultValue: ygot.String("42")}, }, { name: "decimal64", in: &yang.YangType{Kind: yang.Ydecimal64, Name: "decimal64", Default: "4.2"}, - want: &MappedType{NativeType: "float64", ZeroValue: "0.0", DefaultValue: ygot.String("4.2")}, + want: &ygen.MappedType{NativeType: "float64", ZeroValue: "0.0", DefaultValue: ygot.String("4.2")}, }, { name: "binary lookup resolution", in: &yang.YangType{Kind: yang.Ybinary, Name: "binary"}, - want: &MappedType{NativeType: "Binary", ZeroValue: "nil"}, + want: &ygen.MappedType{NativeType: "Binary", ZeroValue: "nil"}, }, { name: "unknown lookup resolution", in: &yang.YangType{Kind: yang.YinstanceIdentifier, Name: "instanceIdentifier"}, - want: &MappedType{NativeType: "interface{}", ZeroValue: "nil"}, + want: &ygen.MappedType{NativeType: "interface{}", ZeroValue: "nil"}, }, { name: "simple empty resolution", in: &yang.YangType{Kind: yang.Yempty, Name: "empty"}, - want: &MappedType{NativeType: "YANGEmpty", ZeroValue: "false"}, + want: &ygen.MappedType{NativeType: "YANGEmpty", ZeroValue: "false"}, }, { name: "simple boolean resolution", in: &yang.YangType{Kind: yang.Ybool, Name: "bool"}, - want: &MappedType{NativeType: "bool", ZeroValue: "false"}, + want: &ygen.MappedType{NativeType: "bool", ZeroValue: "false"}, }, { name: "simple int64 resolution", in: &yang.YangType{Kind: yang.Yint64, Name: "int64"}, - want: &MappedType{NativeType: "int64", ZeroValue: "0"}, + want: &ygen.MappedType{NativeType: "int64", ZeroValue: "0"}, }, { name: "simple uint8 resolution", in: &yang.YangType{Kind: yang.Yuint8, Name: "uint8"}, - want: &MappedType{NativeType: "uint8", ZeroValue: "0"}, + want: &ygen.MappedType{NativeType: "uint8", ZeroValue: "0"}, }, { name: "simple uint16 resolution", in: &yang.YangType{Kind: yang.Yuint16, Name: "uint16"}, - want: &MappedType{NativeType: "uint16", ZeroValue: "0"}, + want: &ygen.MappedType{NativeType: "uint16", ZeroValue: "0"}, }, { name: "leafref without valid path", in: &yang.YangType{Kind: yang.Yleafref, Name: "leafref"}, @@ -582,7 +584,7 @@ func TestYangTypeToGoType(t *testing.T) { Default: "42", }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "Module_Container_Leaf_Union", UnionTypes: map[string]int{"string": 0, "int8": 1}, ZeroValue: "nil", @@ -597,7 +599,7 @@ func TestYangTypeToGoType(t *testing.T) { {Kind: yang.Ystring, Name: "string"}, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "string", UnionTypes: map[string]int{"string": 0}, ZeroValue: `""`, @@ -623,7 +625,7 @@ func TestYangTypeToGoType(t *testing.T) { Parent: &yang.Module{Name: "base-module"}, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_BaseModule_DerivedIdentityref", IsEnumeratedValue: true, ZeroValue: "0", @@ -650,7 +652,7 @@ func TestYangTypeToGoType(t *testing.T) { Parent: &yang.Module{Name: "base-module"}, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_BaseModule_DerivedIdentityref", IsEnumeratedValue: true, ZeroValue: "0", @@ -670,7 +672,7 @@ func TestYangTypeToGoType(t *testing.T) { Parent: &yang.Module{Name: "base-module"}, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_BaseModule_EnumerationLeaf", IsEnumeratedValue: true, ZeroValue: "0", @@ -690,7 +692,7 @@ func TestYangTypeToGoType(t *testing.T) { Parent: &yang.Module{Name: "base-module"}, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_BaseModule_EnumerationLeaf", IsEnumeratedValue: true, ZeroValue: "0", @@ -714,7 +716,7 @@ func TestYangTypeToGoType(t *testing.T) { Parent: &yang.Module{Name: "base-module"}, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_BaseModule_UnionLeaf_Enum", UnionTypes: map[string]int{"E_BaseModule_UnionLeaf_Enum": 0}, IsEnumeratedValue: true, @@ -740,7 +742,7 @@ func TestYangTypeToGoType(t *testing.T) { }, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_BaseModule_DerivedEnumeration", IsEnumeratedValue: true, ZeroValue: "0", @@ -764,7 +766,7 @@ func TestYangTypeToGoType(t *testing.T) { }, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_BaseModule_UnionLeaf_Enum", UnionTypes: map[string]int{"E_BaseModule_UnionLeaf_Enum": 0}, IsEnumeratedValue: true, @@ -790,7 +792,7 @@ func TestYangTypeToGoType(t *testing.T) { }, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_BaseModule_DerivedEnumeration", IsEnumeratedValue: true, ZeroValue: "0", @@ -816,7 +818,7 @@ func TestYangTypeToGoType(t *testing.T) { }, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_TestModule_BaseIdentity", IsEnumeratedValue: true, ZeroValue: "0", @@ -842,7 +844,7 @@ func TestYangTypeToGoType(t *testing.T) { }, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_TestModule_BaseIdentity", IsEnumeratedValue: true, ZeroValue: "0", @@ -875,7 +877,7 @@ func TestYangTypeToGoType(t *testing.T) { }, }, }, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_BaseModule_BaseIdentity", UnionTypes: map[string]int{"E_BaseModule_BaseIdentity": 0}, IsEnumeratedValue: true, @@ -898,7 +900,7 @@ func TestYangTypeToGoType(t *testing.T) { }, }, inCompressPath: true, - want: &MappedType{ + want: &ygen.MappedType{ NativeType: "E_Container_Eleaf", IsEnumeratedValue: true, ZeroValue: "0", @@ -914,7 +916,7 @@ func TestYangTypeToGoType(t *testing.T) { }, }, inCompressPath: true, - want: &MappedType{NativeType: "E_Container_Eleaf", IsEnumeratedValue: true, ZeroValue: "0"}, + want: &ygen.MappedType{NativeType: "E_Container_Eleaf", IsEnumeratedValue: true, ZeroValue: "0"}, }, { name: "leafref", ctx: &yang.Entry{ @@ -956,7 +958,7 @@ func TestYangTypeToGoType(t *testing.T) { Parent: &yang.Entry{Name: "module"}, }, }, - want: &MappedType{NativeType: "uint32", ZeroValue: "0"}, + want: &ygen.MappedType{NativeType: "uint32", ZeroValue: "0"}, }, { name: "enumeration from grouping used in multiple places - skip deduplication", ctx: &yang.Entry{ @@ -998,14 +1000,14 @@ func TestYangTypeToGoType(t *testing.T) { Parent: &yang.Entry{ Name: "config", Parent: &yang.Entry{ - Name: "container", + Name: "a-container-lexicographically-earlier", Parent: &yang.Entry{Name: "base-module"}, }, }, }}, inCompressPath: true, inSkipEnumDedup: true, - want: &MappedType{NativeType: "E_Bar_Leaf", IsEnumeratedValue: true, ZeroValue: "0"}, + want: &ygen.MappedType{NativeType: "E_Bar_Leaf", IsEnumeratedValue: true, ZeroValue: "0"}, }, { name: "enumeration from grouping used in multiple places - with deduplication", ctx: &yang.Entry{ @@ -1051,7 +1053,7 @@ func TestYangTypeToGoType(t *testing.T) { }, }}, inCompressPath: true, - want: &MappedType{NativeType: "E_AContainerLexicographicallyEarlier_EnumLeaf", IsEnumeratedValue: true, ZeroValue: "0"}, + want: &ygen.MappedType{NativeType: "E_AContainerLexicographicallyEarlier_EnumLeaf", IsEnumeratedValue: true, ZeroValue: "0"}, }} for _, tt := range tests { @@ -1076,11 +1078,9 @@ func TestYangTypeToGoType(t *testing.T) { } if tt.inEntries != nil { - st, err := buildSchemaTree(tt.inEntries) - if err != nil { - t.Fatalf("buildSchemaTree(%v): could not build schema tree: %v", tt.inEntries, err) + if err := s.SetupSchemaTree(tt.inEntries); err != nil { + t.Fatalf("SetupSchemaTree(%v): could not build schema tree: %v", tt.inEntries, err) } - s.schematree = st } args := resolveTypeArgs{ @@ -1254,6 +1254,52 @@ func TestStructName(t *testing.T) { } } +// enumMapFromEntries recursively finds enumerated values from a slice of +// entries and returns an enumMap. The input enumMap is intended for +// findEnumSet. +func enumMapFromEntries(entries []*yang.Entry) map[string]*yang.Entry { + enumMap := map[string]*yang.Entry{} + for _, e := range entries { + addEnumsToEnumMap(e, enumMap) + } + return enumMap +} + +// enumMapFromEntries recursively finds enumerated values from a slice of +// resolveTypeArgs and returns an enumMap. The input enumMap is intended for +// findEnumSet. +func enumMapFromArgs(args []resolveTypeArgs) map[string]*yang.Entry { + enumMap := map[string]*yang.Entry{} + for _, a := range args { + addEnumsToEnumMap(a.contextEntry, enumMap) + } + return enumMap +} + +// enumMapFromEntries recursively finds enumerated values from an entry and +// returns an enumMap. The input enumMap is intended for findEnumSet. +func enumMapFromEntry(entry *yang.Entry) map[string]*yang.Entry { + enumMap := map[string]*yang.Entry{} + addEnumsToEnumMap(entry, enumMap) + return enumMap +} + +// addEnumsToEnumMap recursively finds enumerated values and adds them to the +// input enumMap. The input enumMap is intended for findEnumSet, so that tests +// that need generated enumerated names have an easy time generating them, and +// subsequently adding them to their generated state during setup. +func addEnumsToEnumMap(entry *yang.Entry, enumMap map[string]*yang.Entry) { + if entry == nil { + return + } + if e := igenutil.MappableLeaf(entry); e != nil { + enumMap[entry.Path()] = e + } + for _, e := range entry.Dir { + addEnumsToEnumMap(e, enumMap) + } +} + // TestTypeResolutionManyToOne tests cases where there can be many leaves that target the // same underlying typedef or identity, ensuring that generated names are reused where required. func TestTypeResolutionManyToOne(t *testing.T) { @@ -1267,8 +1313,8 @@ func TestTypeResolutionManyToOne(t *testing.T) { inCompressOCPaths bool inSkipEnumDedup bool // wantTypes is a map, keyed by the path of the yang.Entry within inLeaves and - // describing the MappedType that is expected to be output. - wantTypes map[string]*MappedType + // describing the ygen.MappedType that is expected to be output. + wantTypes map[string]*ygen.MappedType }{{ name: "identity with multiple identityref leaves", inLeaves: []*yang.Entry{{ @@ -1316,7 +1362,7 @@ func TestTypeResolutionManyToOne(t *testing.T) { }, }, }}, - wantTypes: map[string]*MappedType{ + wantTypes: map[string]*ygen.MappedType{ "/test-module/leaf-one": {NativeType: "E_TestModule_BaseIdentity", IsEnumeratedValue: true, ZeroValue: "0"}, "/test-module/leaf-two": {NativeType: "E_TestModule_BaseIdentity", IsEnumeratedValue: true, ZeroValue: "0"}, }, @@ -1361,7 +1407,7 @@ func TestTypeResolutionManyToOne(t *testing.T) { }, }, }}, - wantTypes: map[string]*MappedType{ + wantTypes: map[string]*ygen.MappedType{ "/base-module/leaf-one": {NativeType: "E_BaseModule_DefinedType", IsEnumeratedValue: true, ZeroValue: "0"}, "/base-module/leaf-two": {NativeType: "E_BaseModule_DefinedType", IsEnumeratedValue: true, ZeroValue: "0"}, }, @@ -1398,7 +1444,7 @@ func TestTypeResolutionManyToOne(t *testing.T) { }, }, }}, - wantTypes: map[string]*MappedType{ + wantTypes: map[string]*ygen.MappedType{ "/base-module/leaf-one": {NativeType: "E_BaseModule_LeafOne", IsEnumeratedValue: true, ZeroValue: "0"}, "/base-module/leaf-two": {NativeType: "E_BaseModule_LeafOne", IsEnumeratedValue: true, ZeroValue: "0"}, }, @@ -1436,7 +1482,7 @@ func TestTypeResolutionManyToOne(t *testing.T) { }, }}, inSkipEnumDedup: true, - wantTypes: map[string]*MappedType{ + wantTypes: map[string]*ygen.MappedType{ "/base-module/leaf-one": {NativeType: "E_BaseModule_LeafOne", IsEnumeratedValue: true, ZeroValue: "0"}, "/base-module/leaf-two": {NativeType: "E_BaseModule_LeafTwo", IsEnumeratedValue: true, ZeroValue: "0"}, }, @@ -1449,7 +1495,7 @@ func TestTypeResolutionManyToOne(t *testing.T) { t.Fatalf("findEnumSet failed: %v", err) } - gotTypes := make(map[string]*MappedType) + gotTypes := make(map[string]*ygen.MappedType) for _, leaf := range tt.inLeaves { mtype, err := s.yangTypeToGoType(resolveTypeArgs{yangType: leaf.Type, contextEntry: leaf}, tt.inCompressOCPaths, tt.inSkipEnumDedup, true, true, nil) if err != nil { @@ -2457,11 +2503,9 @@ func TestYangDefaultValueToGo(t *testing.T) { } if tt.inEntries != nil { - st, err := buildSchemaTree(tt.inEntries) - if err != nil { - t.Fatalf("buildSchemaTree(%v): could not build schema tree: %v", tt.inEntries, err) + if err := s.SetupSchemaTree(tt.inEntries); err != nil { + t.Fatalf("SetupSchemaTree(%v): could not build schema tree: %v", tt.inEntries, err) } - s.schematree = st } args := resolveTypeArgs{ @@ -2816,11 +2860,9 @@ func TestYangDefaultValueToGo(t *testing.T) { } if tt.inEntries != nil { - st, err := buildSchemaTree(tt.inEntries) - if err != nil { - t.Fatalf("buildSchemaTree(%v): could not build schema tree: %v", tt.inEntries, err) + if err := s.SetupSchemaTree(tt.inEntries); err != nil { + t.Fatalf("SetupSchemaTree(%v): could not build schema tree: %v", tt.inEntries, err) } - s.schematree = st } args := resolveTypeArgs{ diff --git a/ygen/gogen.go b/gogen/gogen.go similarity index 94% rename from ygen/gogen.go rename to gogen/gogen.go index 1a9d42ccb..c32d9301d 100644 --- a/ygen/gogen.go +++ b/gogen/gogen.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ygen +package gogen import ( "bytes" @@ -20,13 +20,14 @@ import ( "io" "sort" "strings" - "text/template" "github.com/openconfig/gnmi/errlist" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/internal/igenutil" "github.com/openconfig/ygot/util" + "github.com/openconfig/ygot/ygen" "github.com/openconfig/ygot/ygot" ) @@ -295,7 +296,7 @@ type generatedDefaultMethod struct { var ( // goCommonHeaderTemplate is populated and output at the top of the generated code package - goCommonHeaderTemplate = mustMakeTemplate("commonHeader", ` + goCommonHeaderTemplate = igenutil.MustMakeTemplate("commonHeader", ` {{- /**/ -}} /* Package {{ .PackageName }} is a generated package which contains definitions @@ -334,7 +335,7 @@ import ( // goOneOffHeaderTemplate defines the template for package code that should // be output in only one file. - goOneOffHeaderTemplate = mustMakeTemplate("oneoffHeader", ` + goOneOffHeaderTemplate = igenutil.MustMakeTemplate("oneoffHeader", ` // {{ .BinaryTypeName }} is a type that is used for fields that have a YANG type of // binary. It is used such that binary fields can be distinguished from // leaf-lists of uint8s (which are mapped to []uint8, equivalent to @@ -475,7 +476,7 @@ var ΓModelData = []*gpb.ModelData{ // them. The logic populating the generatedGoStruct handles non-scalar child schema // nodes: leaf-lists are mapped into slices; lists are mapped into a map or slice of // structs; and containers are mapped into structs. - goStructTemplate = mustMakeTemplate("struct", ` + goStructTemplate = igenutil.MustMakeTemplate("struct", ` // {{ .StructName }} represents the {{ .YANGPath }} YANG schema element. type {{ .StructName }} struct { {{- range $idx, $field := .Fields }} @@ -496,7 +497,7 @@ func (*{{ .StructName }}) IsYANGGoStruct() {} // goStructValidatorTemplate takes an input generatedGoStruct, which contains // a definition of a YANG schema node, and generates the Go validation code // from it. - goStructValidatorTemplate = mustMakeTemplate("structValidator", ` + goStructValidatorTemplate = igenutil.MustMakeTemplate("structValidator", ` // Validate validates s against the YANG schema corresponding to its type. func (t *{{ .StructName }}) ΛValidate(opts ...ygot.ValidationOption) error { if err := ytypes.Validate(SchemaTree["{{ .StructName }}"], t, opts...); err != nil { @@ -508,7 +509,7 @@ func (t *{{ .StructName }}) ΛValidate(opts ...ygot.ValidationOption) error { // goStructValidatorProxyTemplate creates a proxy for the ΛValidate function with the // user definable name. - goStructValidatorProxyTemplate = mustMakeTemplate("structValidatorProxy", ` + goStructValidatorProxyTemplate = igenutil.MustMakeTemplate("structValidatorProxy", ` // Validate validates s against the YANG schema corresponding to its type. func (t *{{ .StructName }}) {{ .ValidateProxyFnName }}(opts ...ygot.ValidationOption) error { return t.ΛValidate(opts...) @@ -517,7 +518,7 @@ func (t *{{ .StructName }}) {{ .ValidateProxyFnName }}(opts ...ygot.ValidationOp // goContainerGetterTemplate defines a template that generates a getter function // for the field of a generated struct. It is generated only for YANG containers. - goContainerGetterTemplate = mustMakeTemplate("getContainer", ` + goContainerGetterTemplate = igenutil.MustMakeTemplate("getContainer", ` // Get{{ .Field.Name }} returns the value of the {{ .Field.Name }} struct pointer // from {{ .StructName }}. If the receiver or the field {{ .Field.Name }} is nil, nil // is returned such that the Get* methods can be safely chained. @@ -532,7 +533,7 @@ func (t *{{ .StructName }}) Get{{ .Field.Name }}() {{ .Field.Type }} { // goGetOrCreateStructTemplate is a template that generates a getter // function for a struct field of the receiver struct. The function generated // creates the field if it does not exist. - goGetOrCreateStructTemplate = mustMakeTemplate("getOrCreateStruct", ` + goGetOrCreateStructTemplate = igenutil.MustMakeTemplate("getOrCreateStruct", ` // GetOrCreate{{ .Field.Name }} retrieves the value of the {{ .Field.Name }} field // or returns the existing field if it already exists. func (t *{{ .StructName }}) GetOrCreate{{ .Field.Name }}() {{ .Field.Type }} { @@ -565,7 +566,7 @@ func (t *{{ .StructName }}) GetOrCreate{{ .Field.Name }}() {{ .Field.Type }} { // // This struct is then used as the key of the map representing the list L, in // the generated struct representing the container A. - goListKeyTemplate = mustMakeTemplate("listkey", ` + goListKeyTemplate = igenutil.MustMakeTemplate("listkey", ` // {{ .KeyStructName }} represents the key for list {{ .ListName }} of element {{ .ParentPath }}. type {{ .KeyStructName }} struct { {{- range $idx, $key := .Keys }} @@ -577,7 +578,7 @@ type {{ .KeyStructName }} struct { // goEnumDefinitionTemplate takes an input generatedGoEnumeration struct // and outputs the Go code that is associated with the enumerated type to be // generated. - goEnumDefinitionTemplate = mustMakeTemplate("enumDefinition", ` + goEnumDefinitionTemplate = igenutil.MustMakeTemplate("enumDefinition", ` // E_{{ .EnumerationPrefix }} is a derived int64 type which is used to represent // the enumerated node {{ .EnumerationPrefix }}. An additional value named // {{ .EnumerationPrefix }}_UNSET is added to the enumeration which is used as @@ -611,7 +612,7 @@ const ( // of a struct within a keyed YANG list, and populates the map key, and the // key fields of the list's struct according to the input arguments of the // function. - goNewListMemberTemplate = mustMakeTemplate("newListEntry", ` + goNewListMemberTemplate = igenutil.MustMakeTemplate("newListEntry", ` // New{{ .ListName }} creates a new entry in the {{ .ListName }} list of the // {{ .Receiver}} struct. The keys of the list are populated from the input // arguments. @@ -671,7 +672,7 @@ func (t *{{ .Receiver }}) New{{ .ListName }}( // goListGetterTemplate defines a template for a function that, for a particular // list key, gets an existing map value. - goListGetterTemplate = mustMakeTemplate("getList", ` + goListGetterTemplate = igenutil.MustMakeTemplate("getList", ` // Get{{ .ListName }} retrieves the value with the specified key from // the {{ .ListName }} map field of {{ .Receiver }}. If the receiver is nil, or // the specified key is not present in the list, nil is returned such that Get* @@ -710,7 +711,7 @@ func (t *{{ .Receiver }}) Get{{ .ListName }}( // goGetOrCreateListTemplate defines a template for a function that, for a // particular list key, gets an existing map value, or creates it if it doesn't // exist. - goGetOrCreateListTemplate = mustMakeTemplate("getOrCreateList", ` + goGetOrCreateListTemplate = igenutil.MustMakeTemplate("getOrCreateList", ` // GetOrCreate{{ .ListName }} retrieves the value with the specified keys from // the receiver {{ .Receiver }}. If the entry does not exist, then it is created. // It returns the existing or new list member. @@ -753,7 +754,7 @@ func (t *{{ .Receiver }}) GetOrCreate{{ .ListName }}( // goLeafGetterTemplate defines a template for a function that, for a // particular leaf, generates a getter method. - goLeafGetterTemplate = mustMakeTemplate("getLeaf", ` + goLeafGetterTemplate = igenutil.MustMakeTemplate("getLeaf", ` // Get{{ .Name }} retrieves the value of the leaf {{ .Name }} from the {{ .Receiver }} // struct. If the field is unset but has a default value in the YANG schema, // then the default value will be returned. @@ -777,7 +778,7 @@ func (t *{{ .Receiver }}) Get{{ .Name }}() {{ .Type }} { // goDefaultMethodTemplate is a template for generating a PopulateDefaults method // for a GoStruct that recursively populates default values within the subtree. - goDefaultMethodTemplate = mustMakeTemplate("populateDefaults", ` + goDefaultMethodTemplate = igenutil.MustMakeTemplate("populateDefaults", ` // PopulateDefaults recursively populates unset leaf fields in the {{ .Receiver }} // with default values as specified in the YANG schema, instantiating any nil // container fields. @@ -812,7 +813,7 @@ func (t *{{ .Receiver }}) PopulateDefaults() { // goDeleteListTemplate defines a template for a function that, for a // particular list key, deletes an existing map value. - goDeleteListTemplate = mustMakeTemplate("deleteList", ` + goDeleteListTemplate = igenutil.MustMakeTemplate("deleteList", ` // Delete{{ .ListName }} deletes the value with the specified keys from // the receiver {{ .Receiver }}. If there is no such element, the function // is a no-op. @@ -845,7 +846,7 @@ func (t *{{ .Receiver }}) Delete{{ .ListName }}( // within values by default, we must invert the "IsScalarField" check to // ensure that we dereference elements that are pointers in the generated // code. - goListAppendTemplate = mustMakeTemplate("appendList", ` + goListAppendTemplate = igenutil.MustMakeTemplate("appendList", ` // Append{{ .ListName }} appends the supplied {{ .ListType }} struct to the // list {{ .ListName }} of {{ .Receiver }}. If the key value(s) specified in // the supplied {{ .ListType }} already exist in the list, an error is @@ -909,7 +910,7 @@ func (t *{{ .Receiver }}) Append{{ .ListName }}(v *{{ .ListType }}) error { // goListMemberRenameTemplate provides a template for a function which renames // an entry within a list. It is used to generate functions for each list within // a generated Go struct. - goListMemberRenameTemplate = mustMakeTemplate("renameListEntry", ` + goListMemberRenameTemplate = igenutil.MustMakeTemplate("renameListEntry", ` // Rename{{ .ListName }} renames an entry in the list {{ .ListName }} within // the {{ .Receiver }} struct. The entry with key oldK is renamed to newK updating // the key within the value. @@ -957,7 +958,7 @@ func (t *{{ .Receiver }}) Rename{{ .ListName }}( // goKeyMapTemplate defines the template for a function that is generated for a YANG // list type. It returns a map[string]interface{} keyed by the YANG leaf identifier of each // key leaf, and containing their values within the struct. - goKeyMapTemplate = mustMakeTemplate("keyHelper", ` + goKeyMapTemplate = igenutil.MustMakeTemplate("keyHelper", ` // ΛListKeyMap returns the keys of the {{ .Receiver }} struct, which is a YANG list entry. func (t *{{ .Receiver }}) ΛListKeyMap() (map[string]interface{}, error) { {{- range $key := .Keys -}}{{ if $key.IsPtr }} @@ -979,7 +980,7 @@ func (t *{{ .Receiver }}) ΛListKeyMap() (map[string]interface{}, error) { // goEnumMapTemplate provides a template to output a constant map which // can be used to resolve the string value of any enumeration within the // schema. - goEnumMapTemplate = mustMakeTemplate("enumMap", ` + goEnumMapTemplate = igenutil.MustMakeTemplate("enumMap", ` // ΛEnum is a map, keyed by the name of the type defined for each enum in the // generated Go code, which provides a mapping between the constant int64 value // of each value of the enumeration, and the string that is used to represent it @@ -1003,7 +1004,7 @@ var ΛEnum = map[string]map[int64]ygot.EnumDefinition{ // goEnumTypeMapTemplate provides a template to output a constant map which // can be used to resolve a schemapath to the set of enumerated types that // are valid for the leaf or leaf-list defined at the path specified. - goEnumTypeMapTemplate = mustMakeTemplate("enumTypeMap", ` + goEnumTypeMapTemplate = igenutil.MustMakeTemplate("enumTypeMap", ` // ΛEnumTypes is a map, keyed by a YANG schema path, of the enumerated types that // correspond with the leaf. The type is represented as a reflect.Type. The naming // of the map ensures that there are no clashes with valid YANG identifiers. @@ -1023,7 +1024,7 @@ func initΛEnumTypes(){ // goEnumTypeMapAccessTemplate provides a template to output an accessor // function with a generated struct as receiver, it returns the enum type // map associated with the generated code. - goEnumTypeMapAccessTemplate = mustMakeTemplate("enumTypeMapAccessor", ` + goEnumTypeMapAccessTemplate = igenutil.MustMakeTemplate("enumTypeMapAccessor", ` // ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types // that are included in the generated code. func (t *{{ .StructName }}) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes } @@ -1032,7 +1033,7 @@ func (t *{{ .StructName }}) ΛEnumTypeMap() map[string][]reflect.Type { return // goBelongingModuleTemplate provides a template to output a // function that has a generated struct as receiver, and returns the // name of the module in which namespace the generated struct belongs. - goBelongingModuleTemplate = mustMakeTemplate("belongingModuleMethod", ` + goBelongingModuleTemplate = igenutil.MustMakeTemplate("belongingModuleMethod", ` // ΛBelongingModule returns the name of the module that defines the namespace // of {{ .StructName }}. func (*{{ .StructName }}) ΛBelongingModule() string { @@ -1043,7 +1044,7 @@ func (*{{ .StructName }}) ΛBelongingModule() string { // schemaVarTemplate provides a template to output a constant byte // slice which contains the serialised schema of the YANG modules for // which code generation was performed. - schemaVarTemplate = mustMakeTemplate("schemaVar", ` + schemaVarTemplate = igenutil.MustMakeTemplate("schemaVar", ` var ( // {{ .VarName }} is a byte slice contain a gzip compressed representation of the // YANG schema from which the Go code was generated. When uncompressed the @@ -1061,7 +1062,7 @@ var ( // unionTypeTemplate outputs the type that corresponds to a multi-type union // in the YANG schema. - unionTypeTemplate = mustMakeTemplate("unionType", ` + unionTypeTemplate = igenutil.MustMakeTemplate("unionType", ` // {{ .Name }} is an interface that is implemented by valid types for the union // for the leaf {{ .LeafPath }} within the YANG schema. type {{ .Name }} interface { @@ -1085,7 +1086,7 @@ func (*{{ $intfName }}_{{ $typeName }}) Is_{{ $intfName }}() {} // unionHelperTemplate defines a template that defines a helper method // with a particular receiver type that allows an input type to be converted // to its corresponding type in the union type. - unionHelperTemplate = mustMakeTemplate("unionHelper", ` + unionHelperTemplate = igenutil.MustMakeTemplate("unionHelper", ` {{- $intfName := .Name }} {{- $path := .LeafPath }} // To_{{ .Name }} takes an input interface{} and attempts to convert it to a struct @@ -1111,7 +1112,7 @@ func (t *{{ .ParentReceiver }}) To_{{ .Name }}(i interface{}) ({{ .Name }}, erro // unionTypeSimpleTemplate outputs the type that corresponds to a multi-type union // in the YANG schema. It does so by enhancing generated typedefs. - unionTypeSimpleTemplate = mustMakeTemplate("unionTypeSimple", ` + unionTypeSimpleTemplate = igenutil.MustMakeTemplate("unionTypeSimple", ` // {{ .Name }} is an interface that is implemented by valid types for the union // for the leaf {{ .LeafPath }} within the YANG schema. // Union type can be one of [{{ .SubtypeDocumentation }}]. @@ -1131,7 +1132,7 @@ func ({{ $typeName }}) Documentation_for_{{ $intfName }}() {} // unionHelperSimpleTemplate defines a template that defines a helper method // with a particular receiver type that allows an input type to be converted // to its corresponding type in the union type. - unionHelperSimpleTemplate = mustMakeTemplate("unionHelperSimple", ` + unionHelperSimpleTemplate = igenutil.MustMakeTemplate("unionHelperSimple", ` {{- $intfName := .Name }} {{- $path := .LeafPath }} // To_{{ .Name }} takes an input interface{} and attempts to convert it to a struct @@ -1162,46 +1163,8 @@ func (t *{{ .ParentReceiver }}) To_{{ .Name }}(i interface{}) ({{ .Name }}, erro ]", i, i) } `) - - // templateHelperFunctions specifies a set of functions that are supplied as - // helpers to the templates that are used within this file. - templateHelperFunctions = template.FuncMap{ - // inc provides a means to add 1 to a number, and is used within templates - // to check whether the index of an element within a loop is the last one, - // such that special handling can be provided for it (e.g., not following - // it with a comma in a list of arguments). - "inc": func(i int) int { - return i + 1 - }, - "toUpper": strings.ToUpper, - "indentLines": func(s string) string { - var b bytes.Buffer - p := strings.Split(s, "\n") - b.WriteRune('\n') - for i, l := range p { - if l == "" { - continue - } - b.WriteString(fmt.Sprintf(" %s", l)) - if i != len(p)-1 { - b.WriteRune('\n') - } - } - return b.String() - }, - // stripAsteriskPrefix provides a template helper that removes an asterisk - // from the start of a string. It is used to remove "*" from the start of - // pointer types. - "stripAsteriskPrefix": func(s string) string { return strings.TrimPrefix(s, "*") }, - } ) -// mustMakeTemplate generates a template.Template for a particular named source -// template; with a common set of helper functions. -func mustMakeTemplate(name, src string) *template.Template { - return template.Must(template.New(name).Funcs(templateHelperFunctions).Parse(src)) -} - // writeGoHeader outputs the package header, including the package name and // comments that is to be included with the generated code. The input set of // files (yangFiles) are output to indicate the modules for which code @@ -1217,14 +1180,14 @@ func mustMakeTemplate(name, src string) *template.Template { // The header returned is split into two strings, the common header is a header that // should be used for all files within the output package. The one off header should // be included in only one file of the package. -func writeGoHeader(yangFiles, includePaths []string, cfg GeneratorConfig, rootName string, modelData []*gpb.ModelData) (string, string, error) { +func writeGoHeader(yangFiles, includePaths []string, cfg *GoCodeGenerator, rootName string, modelData []*gpb.ModelData) (string, string, error) { // Determine the running binary's name. - if cfg.Caller == "" { - cfg.Caller = genutil.CallerName() + if cfg.Config.Caller == "" { + cfg.Config.Caller = genutil.CallerName() } - if cfg.PackageName == "" { - cfg.PackageName = defaultPackageName + if cfg.Config.PackageName == "" { + cfg.Config.PackageName = defaultPackageName } if cfg.GoOptions.YgotImportPath == "" { @@ -1255,12 +1218,12 @@ func writeGoHeader(yangFiles, includePaths []string, cfg GeneratorConfig, rootNa FakeRootName string // FakeRootName is the name of the fake root struct in the YANG type ModelData []*gpb.ModelData // ModelData contains the gNMI ModelData definition for the input types. }{ - PackageName: cfg.PackageName, + PackageName: cfg.Config.PackageName, YANGFiles: yangFiles, IncludePaths: includePaths, - CompressEnabled: cfg.TransformationOptions.CompressBehaviour.CompressEnabled(), - GeneratingBinary: cfg.Caller, - GenerateSchema: cfg.GenerateJSONSchema, + CompressEnabled: cfg.Config.TransformationOptions.CompressBehaviour.CompressEnabled(), + GeneratingBinary: cfg.Config.Caller, + GenerateSchema: cfg.Config.GenerateJSONSchema, GoOptions: cfg.GoOptions, BinaryTypeName: ygot.BinaryTypeName, EmptyTypeName: ygot.EmptyTypeName, @@ -1268,7 +1231,7 @@ func writeGoHeader(yangFiles, includePaths []string, cfg GeneratorConfig, rootNa } s.FakeRootName = "nil" - if cfg.TransformationOptions.GenerateFakeRoot && rootName != "" { + if cfg.Config.TransformationOptions.GenerateFakeRoot && rootName != "" { s.FakeRootName = fmt.Sprintf("&%s{}", rootName) } @@ -1287,10 +1250,10 @@ func writeGoHeader(yangFiles, includePaths []string, cfg GeneratorConfig, rootNa // IsScalarField determines which fields should be converted to pointers when // outputting structs; this is done to allow checks against nil. -func IsScalarField(field *NodeDetails) bool { +func IsScalarField(field *ygen.NodeDetails) bool { switch { // A non-singleton-leaf always has a generated type for which nil is valid. - case field.Type != LeafNode: + case field.Type != ygen.LeafNode: return false // A union shouldn't be a pointer since its field type is an interface; case len(field.LangType.UnionTypes) >= 2: @@ -1328,7 +1291,7 @@ func IsScalarField(field *NodeDetails) bool { // of targetStruct (listKeys). // 3. Methods with the struct corresponding to targetStruct as a receiver, e.g., for each // list a NewListMember() method is generated. -func writeGoStruct(targetStruct *ParsedDirectory, goStructElements map[string]*ParsedDirectory, generatedUnions map[string]bool, ignoreShadowSchemaPaths bool, goOpts GoOpts, generateJSONSchema bool) (GoStructCodeSnippet, []error) { +func writeGoStruct(targetStruct *ygen.ParsedDirectory, goStructElements map[string]*ygen.ParsedDirectory, generatedUnions map[string]bool, ignoreShadowSchemaPaths bool, goOpts GoOpts, generateJSONSchema bool) (GoStructCodeSnippet, []error) { if targetStruct == nil { return GoStructCodeSnippet{}, []error{fmt.Errorf("cannot create code for nil targetStruct")} } @@ -1387,7 +1350,7 @@ func writeGoStruct(targetStruct *ParsedDirectory, goStructElements map[string]*P }) } - goFieldNameMap := GoFieldNameMap(targetStruct) + goFieldNameMap := ygen.GoFieldNameMap(targetStruct) // Alphabetically order fields to produce deterministic output. for _, fName := range targetStruct.OrderedFieldNames() { // Iterate through the fields of the struct that we are generating code for. @@ -1401,7 +1364,7 @@ func writeGoStruct(targetStruct *ParsedDirectory, goStructElements map[string]*P definedNameMap[fName] = &yangFieldMap{YANGName: fName, GoName: fieldName} switch field.Type { - case ListNode: + case ygen.ListNode: // If the field within the struct is a list, then generate code for this list. This // includes extracting any new types that are required to represent the key of a // list that has multiple keys. @@ -1427,7 +1390,7 @@ func writeGoStruct(targetStruct *ParsedDirectory, goStructElements map[string]*P associatedListKeyStructs = append(associatedListKeyStructs, multiKeyListKey) } - case ContainerNode: + case ygen.ContainerNode: // This is a YANG container, so it is represented in code using a pointer to the struct type that // is defined for the entity. findMappableEntities has already determined which fields are to // be output, so no filtering of the set of fields is required here. @@ -1443,7 +1406,7 @@ func writeGoStruct(targetStruct *ParsedDirectory, goStructElements map[string]*P IsYANGContainer: true, } associatedDefaultMethod.ChildContainerNames = append(associatedDefaultMethod.ChildContainerNames, fieldName) - case LeafNode, LeafListNode: + case ygen.LeafNode, ygen.LeafListNode: // Only if this union has more than one subtype do we generate the union; // otherwise, we use that subtype directly. // Also, make sure to process a union type once and only once within the struct. @@ -1505,7 +1468,7 @@ func writeGoStruct(targetStruct *ParsedDirectory, goStructElements map[string]*P fType := field.LangType.NativeType zeroValue := field.LangType.ZeroValue - if field.Type == LeafListNode { + if field.Type == ygen.LeafListNode { // We represent a leaf-list in the output // code using a slice of the type that the element was mapped to. fType = fmt.Sprintf("[]%s", fType) @@ -1592,7 +1555,7 @@ func writeGoStruct(targetStruct *ParsedDirectory, goStructElements map[string]*P metadataTagBuf.WriteString(` ygotAnnotation:"true"`) if goOpts.AddYangPresence { - if field.Type == ContainerNode && field.YANGDetails.PresenceStatement != nil { + if field.Type == ygen.ContainerNode && field.YANGDetails.PresenceStatement != nil { tagBuf.WriteString(` yangPresence:"true"`) } } @@ -1957,7 +1920,7 @@ func generateListAppend(buf *bytes.Buffer, method *generatedGoListMethod) error // "baz": *t.Baz, // } // } -func generateGetListKey(buf *bytes.Buffer, s *ParsedDirectory, nameMap map[string]*yangFieldMap) error { +func generateGetListKey(buf *bytes.Buffer, s *ygen.ParsedDirectory, nameMap map[string]*yangFieldMap) error { if s.ListKeys == nil { return nil } @@ -1994,7 +1957,7 @@ func generateGetListKey(buf *bytes.Buffer, s *ParsedDirectory, nameMap map[strin // type. // In the case that the list has multiple keys, the type generated as the key of the list is returned. // If errors are encountered during the type generation for the list, the error is returned. -func yangListFieldToGoType(listField *NodeDetails, listFieldName string, parent *ParsedDirectory, goStructElements map[string]*ParsedDirectory) (string, *generatedGoMultiKeyListStruct, *generatedGoListMethod, error) { +func yangListFieldToGoType(listField *ygen.NodeDetails, listFieldName string, parent *ygen.ParsedDirectory, goStructElements map[string]*ygen.ParsedDirectory) (string, *generatedGoMultiKeyListStruct, *generatedGoListMethod, error) { // The list itself, since it is a container, has a struct associated with it. Retrieve // this from the set of Directory structs for which code (a Go struct) will be // generated such that additional details can be used in the code generation. @@ -2150,7 +2113,7 @@ func generateBelongingModuleFunction(b io.Writer, s generatedGoStruct) error { // provided and stores it in a variable which can be written out to the generated // Go code file. func writeGoSchema(js []byte, schemaVarName string) (string, error) { - jbyte, err := WriteGzippedByteSlice(js) + jbyte, err := ygen.WriteGzippedByteSlice(js) if err != nil { return "", fmt.Errorf("could not write Byte slice: %v", err) } @@ -2165,7 +2128,7 @@ func writeGoSchema(js []byte, schemaVarName string) (string, error) { Schema []string }{ VarName: vn, - Schema: BytesToGoByteSlice(jbyte), + Schema: ygen.BytesToGoByteSlice(jbyte), } var buf bytes.Buffer @@ -2180,7 +2143,7 @@ func writeGoSchema(js []byte, schemaVarName string) (string, error) { // is unspecified, the value specified by the type is returned if it is not nil, // otherwise nil is returned to indicate no default was specified. // TODO(wenbli): This doesn't handle unions. Deprecate this for v1 release. -func goLeafDefaults(e *yang.Entry, t *MappedType) []string { +func goLeafDefaults(e *yang.Entry, t *ygen.MappedType) []string { defaultValues := e.DefaultValues() if len(defaultValues) == 0 && t.DefaultValue != nil { defaultValues = []string{*t.DefaultValue} diff --git a/ygen/gogen_test.go b/gogen/gogen_test.go similarity index 91% rename from ygen/gogen_test.go rename to gogen/gogen_test.go index 5fa5182a9..27cbd51a8 100644 --- a/ygen/gogen_test.go +++ b/gogen/gogen_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ygen +package gogen import ( "fmt" @@ -22,6 +22,7 @@ import ( "github.com/kylelemons/godebug/pretty" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/testutil" + "github.com/openconfig/ygot/ygen" "github.com/openconfig/ygot/ygot" ) @@ -40,29 +41,29 @@ type wantGoStructOut struct { func TestGoCodeStructGeneration(t *testing.T) { tests := []struct { name string - inStructToMap *ParsedDirectory + inStructToMap *ygen.ParsedDirectory // inOtherStructMap is the set of other mappable entities that are // in the same module as the struct to map - inOtherStructMap map[string]*ParsedDirectory + inOtherStructMap map[string]*ygen.ParsedDirectory inIgnoreShadowSchemaPaths bool inGoOpts GoOpts want wantGoStructOut }{{ name: "simple single leaf mapping test", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Tstruct", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "f1": { Name: "F1", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "f1", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/f1", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "int8", UnionTypes: nil, IsEnumeratedValue: false, @@ -76,15 +77,15 @@ func TestGoCodeStructGeneration(t *testing.T) { }, "f2": { Name: "F2", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "f2", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/f2", LeafrefTargetPath: "", }, - Type: LeafListNode, - LangType: &MappedType{ + Type: ygen.LeafListNode, + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, IsEnumeratedValue: false, @@ -143,20 +144,20 @@ func (*Tstruct) ΛBelongingModule() string { }, }, { name: "simple single leaf mapping test outputting shadow paths", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Tstruct", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "f1": { Name: "F1", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "f1", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/f1", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "int8", UnionTypes: nil, IsEnumeratedValue: false, @@ -170,15 +171,15 @@ func (*Tstruct) ΛBelongingModule() string { }, "f2": { Name: "F2", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "f2", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/f2", LeafrefTargetPath: "", }, - Type: LeafListNode, - LangType: &MappedType{ + Type: ygen.LeafListNode, + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, IsEnumeratedValue: false, @@ -230,20 +231,20 @@ func (*Tstruct) ΛBelongingModule() string { }, }, { name: "struct with a multi-type union", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "InputStruct", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "u1": { Name: "U1", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "u1", Defaults: nil, RootElementModule: "exmod", Path: "/module/input-struct/u1", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "InputStruct_U1_Union", UnionTypes: map[string]int{"string": 0, "int8": 1}, IsEnumeratedValue: false, @@ -334,20 +335,20 @@ func (t *InputStruct) To_InputStruct_U1_Union(i interface{}) (InputStruct_U1_Uni }, }, { name: "nested container in struct", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "InputStruct", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "c1": { Name: "C1", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "c1", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/input-struct/c1", LeafrefTargetPath: "", }, - Type: ContainerNode, + Type: ygen.ContainerNode, LangType: nil, MappedPaths: [][]string{{"c1"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -358,7 +359,7 @@ func (t *InputStruct) To_InputStruct_U1_Union(i interface{}) (InputStruct_U1_Uni Path: "/root-module/input-struct", BelongingModule: "exmod", }, - inOtherStructMap: map[string]*ParsedDirectory{ + inOtherStructMap: map[string]*ygen.ParsedDirectory{ "/root-module/input-struct/c1": { Name: "InputStruct_C1", Path: "/root-module/input-struct/c1", @@ -399,13 +400,13 @@ func (*InputStruct) ΛBelongingModule() string { }, }, { name: "nested container in struct with presence container", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "InputStruct", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "c1": { Name: "C1", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "c1", Defaults: nil, RootElementModule: "exmod", @@ -413,7 +414,7 @@ func (*InputStruct) ΛBelongingModule() string { LeafrefTargetPath: "", PresenceStatement: ygot.String("instantiated"), }, - Type: ContainerNode, + Type: ygen.ContainerNode, LangType: nil, MappedPaths: [][]string{{"c1"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -424,7 +425,7 @@ func (*InputStruct) ΛBelongingModule() string { Path: "/root-module/input-struct", BelongingModule: "exmod", }, - inOtherStructMap: map[string]*ParsedDirectory{ + inOtherStructMap: map[string]*ygen.ParsedDirectory{ "/root-module/input-struct/c1": { Name: "InputStruct_C1", Path: "/root-module/input-struct/c1", @@ -468,19 +469,19 @@ func (*InputStruct) ΛBelongingModule() string { }, }, { name: "struct with missing struct referenced", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "AStruct", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "elem": { Name: "Elem", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "elem", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/a-struct/elem", LeafrefTargetPath: "", }, - Type: ContainerNode, + Type: ygen.ContainerNode, LangType: nil, MappedPaths: [][]string{{"elem"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -494,19 +495,19 @@ func (*InputStruct) ΛBelongingModule() string { want: wantGoStructOut{wantErr: true}, }, { name: "struct with missing list referenced", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "BStruct", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "List", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/b-struct/list", LeafrefTargetPath: "", }, - Type: ListNode, + Type: ygen.ListNode, LangType: nil, MappedPaths: [][]string{{"list"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -520,19 +521,19 @@ func (*InputStruct) ΛBelongingModule() string { want: wantGoStructOut{wantErr: true}, }, { name: "struct with keyless list", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "QStruct", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "a-list": { Name: "AList", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "a-list", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/q-struct/a-list", LeafrefTargetPath: "", }, - Type: ListNode, + Type: ygen.ListNode, LangType: nil, MappedPaths: [][]string{{"a-list"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -543,7 +544,7 @@ func (*InputStruct) ΛBelongingModule() string { Path: "/root-module/q-struct", BelongingModule: "exmod", }, - inOtherStructMap: map[string]*ParsedDirectory{ + inOtherStructMap: map[string]*ygen.ParsedDirectory{ "/root-module/q-struct/a-list": { Name: "QStruct_AList", BelongingModule: "exmod", @@ -583,20 +584,20 @@ func (*QStruct) ΛBelongingModule() string { }, }, { name: "struct with single key list", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Tstruct", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "listWithKey": { Name: "ListWithKey", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "list-with-key", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey", LeafrefTargetPath: "", }, - Type: ListNode, + Type: ygen.ListNode, LangType: nil, MappedPaths: [][]string{{"listWithKey"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -607,22 +608,22 @@ func (*QStruct) ΛBelongingModule() string { Path: "/root-module/tstruct", BelongingModule: "exmod", }, - inOtherStructMap: map[string]*ParsedDirectory{ + inOtherStructMap: map[string]*ygen.ParsedDirectory{ "/root-module/tstruct/listWithKey": { Name: "ListWithKey", - Type: List, - Fields: map[string]*NodeDetails{ + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyLeaf": { Name: "keyLeaf", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyLeaf", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey/keyLeaf", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, ZeroValue: `""`, @@ -633,10 +634,10 @@ func (*QStruct) ΛBelongingModule() string { ShadowMappedPathModules: nil, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "keyLeaf": { Name: "KeyLeaf", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, ZeroValue: `""`, @@ -731,20 +732,20 @@ func (*Tstruct) ΛBelongingModule() string { }, }, { name: "missing list definition element", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Tstruct", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "listWithKey": { Name: "ListWithKey", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "list-with-key", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey", LeafrefTargetPath: "", }, - Type: ListNode, + Type: ygen.ListNode, LangType: nil, MappedPaths: [][]string{{"listWithKey"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -758,12 +759,12 @@ func (*Tstruct) ΛBelongingModule() string { want: wantGoStructOut{wantErr: true}, }, { name: "unknown kind", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "AStruct", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "anydata": { Name: "anydata", - Type: AnyDataNode, + Type: ygen.AnyDataNode, }, }, BelongingModule: "exmod", @@ -771,19 +772,19 @@ func (*Tstruct) ΛBelongingModule() string { want: wantGoStructOut{wantErr: true}, }, { name: "unknown field type", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "AStruct", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "idd": { Name: "Idd", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "idd", Defaults: nil, RootElementModule: "mod", Path: "/mod/container-two/container/idd", LeafrefTargetPath: "", }, - Type: InvalidNode, + Type: ygen.InvalidNode, LangType: nil, MappedPaths: [][]string{{"idd"}}, MappedPathModules: [][]string{{"mod"}}, @@ -796,20 +797,20 @@ func (*Tstruct) ΛBelongingModule() string { want: wantGoStructOut{wantErr: true}, }, { name: "struct with multi-key list", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Tstruct", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "listWithKey": { Name: "ListWithKey", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "list-with-key", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey", LeafrefTargetPath: "", }, - Type: ListNode, + Type: ygen.ListNode, LangType: nil, MappedPaths: [][]string{{"listWithKey"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -820,22 +821,22 @@ func (*Tstruct) ΛBelongingModule() string { Path: "/root-module/tstruct", BelongingModule: "exmod", }, - inOtherStructMap: map[string]*ParsedDirectory{ + inOtherStructMap: map[string]*ygen.ParsedDirectory{ "/root-module/tstruct/listWithKey": { Name: "Tstruct_ListWithKey", - Type: List, - Fields: map[string]*NodeDetails{ + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyLeafOne": { Name: "keyLeafOne", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyLeafOne", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey/keyLeafOne", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, ZeroValue: `""`, @@ -847,15 +848,15 @@ func (*Tstruct) ΛBelongingModule() string { }, "keyLeafTwo": { Name: "keyLeafTwo", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyLeafTwo", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey/keyLeafTwo", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "int8", UnionTypes: nil, ZeroValue: "0", @@ -866,10 +867,10 @@ func (*Tstruct) ΛBelongingModule() string { ShadowMappedPathModules: nil, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "keyLeafOne": { Name: "KeyLeafOne", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, ZeroValue: `""`, @@ -877,7 +878,7 @@ func (*Tstruct) ΛBelongingModule() string { }, "keyLeafTwo": { Name: "KeyLeafTwo", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "int8", UnionTypes: nil, ZeroValue: `""`, @@ -984,21 +985,21 @@ func (*Tstruct) ΛBelongingModule() string { }, }, { name: "annotated struct", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Tstruct", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "f1": { Name: "F1", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "f1", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/f1", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "int8", UnionTypes: nil, IsEnumeratedValue: false, @@ -1054,20 +1055,20 @@ func (*Tstruct) ΛBelongingModule() string { }, }, { name: "struct with multi-key list - append and getters", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Tstruct", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "listWithKey": { Name: "ListWithKey", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "list-with-key", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey", LeafrefTargetPath: "", }, - Type: ListNode, + Type: ygen.ListNode, LangType: nil, MappedPaths: [][]string{{"listWithKey"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -1078,22 +1079,22 @@ func (*Tstruct) ΛBelongingModule() string { Path: "/root-module/tstruct", BelongingModule: "exmod", }, - inOtherStructMap: map[string]*ParsedDirectory{ + inOtherStructMap: map[string]*ygen.ParsedDirectory{ "/root-module/tstruct/listWithKey": { Name: "Tstruct_ListWithKey", - Type: List, - Fields: map[string]*NodeDetails{ + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyLeafOne": { Name: "keyLeafOne", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyLeafOne", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey/keyLeafOne", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, ZeroValue: `""`, @@ -1105,15 +1106,15 @@ func (*Tstruct) ΛBelongingModule() string { }, "keyLeafTwo": { Name: "keyLeafTwo", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyLeafTwo", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey/keyLeafTwo", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "int8", UnionTypes: nil, ZeroValue: "0", @@ -1124,10 +1125,10 @@ func (*Tstruct) ΛBelongingModule() string { ShadowMappedPathModules: nil, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "keyLeafOne": { Name: "KeyLeafOne", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, ZeroValue: `""`, @@ -1135,7 +1136,7 @@ func (*Tstruct) ΛBelongingModule() string { }, "keyLeafTwo": { Name: "KeyLeafTwo", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "int8", UnionTypes: nil, ZeroValue: `""`, @@ -1311,20 +1312,20 @@ func (*Tstruct) ΛBelongingModule() string { }, }, { name: "struct with single key list - append and getters", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Tstruct", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "listWithKey": { Name: "ListWithKey", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "list-with-key", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey", LeafrefTargetPath: "", }, - Type: ListNode, + Type: ygen.ListNode, LangType: nil, MappedPaths: [][]string{{"listWithKey"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -1335,22 +1336,22 @@ func (*Tstruct) ΛBelongingModule() string { Path: "/root-module/tstruct", BelongingModule: "exmod", }, - inOtherStructMap: map[string]*ParsedDirectory{ + inOtherStructMap: map[string]*ygen.ParsedDirectory{ "/root-module/tstruct/listWithKey": { Name: "Tstruct_ListWithKey", - Type: List, - Fields: map[string]*NodeDetails{ + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyLeaf": { Name: "keyLeaf", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyLeaf", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/tstruct/listWithKey/keyLeaf", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, ZeroValue: `""`, @@ -1361,10 +1362,10 @@ func (*Tstruct) ΛBelongingModule() string { ShadowMappedPathModules: nil, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "keyLeaf": { Name: "KeyLeaf", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, ZeroValue: `""`, @@ -1527,20 +1528,20 @@ func (*Tstruct) ΛBelongingModule() string { }, }, { name: "struct with child container - getters generated", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "InputStruct", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "c1": { Name: "C1", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "c1", Defaults: nil, RootElementModule: "exmod", Path: "/root-module/input-struct/c1", LeafrefTargetPath: "", }, - Type: ContainerNode, + Type: ygen.ContainerNode, LangType: nil, MappedPaths: [][]string{{"c1"}}, MappedPathModules: [][]string{{"exmod"}}, @@ -1551,7 +1552,7 @@ func (*Tstruct) ΛBelongingModule() string { Path: "/root-module/input-struct", BelongingModule: "exmod", }, - inOtherStructMap: map[string]*ParsedDirectory{ + inOtherStructMap: map[string]*ygen.ParsedDirectory{ "/root-module/input-struct/c1": { Name: "InputStruct_C1", Path: "/root-module/input-struct/c1", @@ -1627,20 +1628,20 @@ func (*InputStruct) ΛBelongingModule() string { }, }, { name: "container with leaf getters", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Container", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "leaf": { Name: "Leaf", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "leaf", Defaults: nil, RootElementModule: "m1", Path: "/m1/foo/bar/leaf", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, IsEnumeratedValue: false, @@ -1720,20 +1721,20 @@ func (*Container) ΛBelongingModule() string { }, }, { name: "leaf getter with default value", - inStructToMap: &ParsedDirectory{ + inStructToMap: &ygen.ParsedDirectory{ Name: "Container", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "leaf": { Name: "Leaf", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "leaf", Defaults: []string{"DEFAULT VALUE"}, RootElementModule: "m1", Path: "/m1/foo/bar/leaf", LeafrefTargetPath: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "string", UnionTypes: nil, IsEnumeratedValue: false, @@ -1821,7 +1822,7 @@ func (*Container) ΛBelongingModule() string { t.Run(tt.name, func(t *testing.T) { // Fill the current directory into the map to reduce test size. if tt.inOtherStructMap == nil { - tt.inOtherStructMap = map[string]*ParsedDirectory{} + tt.inOtherStructMap = map[string]*ygen.ParsedDirectory{} } tt.inOtherStructMap[tt.inStructToMap.Path] = tt.inStructToMap // Always generate the JSON schema for this test. @@ -1901,14 +1902,14 @@ func TestGenGoEnumeratedTypes(t *testing.T) { tests := []struct { name string - in map[string]*EnumeratedYANGType + in map[string]*ygen.EnumeratedYANGType want map[string]*goEnumeratedType }{{ name: "enum", - in: map[string]*EnumeratedYANGType{ + in: map[string]*ygen.EnumeratedYANGType{ "foo": { Name: "EnumeratedValue", - Kind: SimpleEnumerationType, + Kind: ygen.SimpleEnumerationType, TypeName: "enumerated-value", ValToYANGDetails: []ygot.EnumDefinition{ { @@ -2120,42 +2121,42 @@ func TestGoLeafDefaults(t *testing.T) { tests := []struct { name string inLeaf *yang.Entry - inType *MappedType + inType *ygen.MappedType want []string }{{ name: "quoted default in leaf", inLeaf: &yang.Entry{ Default: []string{"a-default-value"}, }, - inType: &MappedType{NativeType: "string"}, + inType: &ygen.MappedType{NativeType: "string"}, want: []string{`"a-default-value"`}, }, { name: "unquoted default in leaf", inLeaf: &yang.Entry{ Default: []string{"42"}, }, - inType: &MappedType{NativeType: "int32"}, + inType: &ygen.MappedType{NativeType: "int32"}, want: []string{"42"}, }, { name: "two quoted defaults in leaf", inLeaf: &yang.Entry{ Default: []string{"a-default-value", "second"}, }, - inType: &MappedType{NativeType: "string"}, + inType: &ygen.MappedType{NativeType: "string"}, want: []string{`"a-default-value"`, `"second"`}, }, { name: "no default", inLeaf: &yang.Entry{}, - inType: &MappedType{NativeType: "int32"}, + inType: &ygen.MappedType{NativeType: "int32"}, }, { name: "default in type", inLeaf: &yang.Entry{}, - inType: &MappedType{NativeType: "int32", DefaultValue: ygot.String("0")}, + inType: &ygen.MappedType{NativeType: "int32", DefaultValue: ygot.String("0")}, want: []string{"0"}, }, { name: "enumerated default in leaf", inLeaf: &yang.Entry{Default: []string{"FORTY_TWO"}}, - inType: &MappedType{ + inType: &ygen.MappedType{ NativeType: fmt.Sprintf("%sEnumType", goEnumPrefix), IsEnumeratedValue: true, }, diff --git a/gogen/helpers.go b/gogen/helpers.go new file mode 100644 index 000000000..3fc36ca12 --- /dev/null +++ b/gogen/helpers.go @@ -0,0 +1,57 @@ +package gogen + +import ( + "fmt" + "strings" +) + +// safeGoEnumeratedValueName takes an input string, which is the name of an +// enumerated value from a YANG schema, and ensures that it is safe to be +// output as part of the name of the enumerated value in the Go code. The +// sanitised value is returned. Per RFC6020 Section 9.6.4, +// "The enum Statement [...] takes as an argument a string which is the +// assigned name. The string MUST NOT be empty and MUST NOT have any +// leading or trailing whitespace characters. The use of Unicode control +// codes SHOULD be avoided." +// Note: this rule is distinct and looser than the rule for YANG identifiers. +// The implementation used here replaces some (not all) characters allowed +// in a YANG enum assigned name but not in Go code. Current support is based +// on real-world feedback e.g. in OpenConfig schemas, there are currently +// a small number of identity values that contain "." and hence +// must be specifically handled. +func safeGoEnumeratedValueName(name string) string { + // NewReplacer takes pairs of strings to be replaced in the form + // old, new. + replacer := strings.NewReplacer( + ".", "_", + "-", "_", + "/", "_", + "+", "_PLUS", + ",", "_COMMA", + "@", "_AT", + "$", "_DOLLAR", + "*", "_ASTERISK", + ":", "_COLON", + " ", "_") + return replacer.Replace(name) +} + +// enumDefaultValue sanitises a default value specified for an enumeration +// which can be specified as prefix:value in the YANG schema. The baseName +// is used as the generated enumeration name stripping any prefix specified, +// (allowing removal of the enumeration type prefix if required). The default +// value in the form _ is returned as +// a pointer. +func enumDefaultValue(baseName, defVal, prefix string) string { + if strings.Contains(defVal, ":") { + defVal = strings.Split(defVal, ":")[1] + } + + if prefix != "" { + baseName = strings.TrimPrefix(baseName, prefix) + } + + defVal = safeGoEnumeratedValueName(defVal) + + return fmt.Sprintf("%s_%s", baseName, defVal) +} diff --git a/gogen/helpers_test.go b/gogen/helpers_test.go new file mode 100644 index 000000000..0b0e69004 --- /dev/null +++ b/gogen/helpers_test.go @@ -0,0 +1,32 @@ +package gogen + +import "testing" + +// TestSafeGoEnumeratedValueName tests the safeGoEnumeratedValue function to ensure +// that enumeraton value names are correctly transformed to safe Go names. +func TestSafeGoEnumeratedValueName(t *testing.T) { + tests := []struct { + in string + want string + }{ + {"SPEED_2.5G", "SPEED_2_5G"}, + {"IPV4-UNICAST", "IPV4_UNICAST"}, + {"frameRelay", "frameRelay"}, + {"coffee", "coffee"}, + {"ethernetCsmacd", "ethernetCsmacd"}, + {"SFP+", "SFP_PLUS"}, + {"LEVEL1/2", "LEVEL1_2"}, + {"DAYS1-3", "DAYS1_3"}, + {"FISH CHIPS", "FISH_CHIPS"}, + {"FOO*", "FOO_ASTERISK"}, + {"FOO:", "FOO_COLON"}, + {",,FOO:@$,", "_COMMA_COMMAFOO_COLON_AT_DOLLAR_COMMA"}, + } + + for _, tt := range tests { + got := safeGoEnumeratedValueName(tt.in) + if got != tt.want { + t.Errorf("safeGoEnumeratedValueName(%s): got: %s, want: %s", tt.in, got, tt.want) + } + } +} diff --git a/ygen/testdata/schema/openconfig-extensions.yang b/gogen/testdata/schema/openconfig-extensions.yang similarity index 100% rename from ygen/testdata/schema/openconfig-extensions.yang rename to gogen/testdata/schema/openconfig-extensions.yang diff --git a/ygen/testdata/schema/openconfig-options-compress-fakeroot-schema.json b/gogen/testdata/schema/openconfig-options-compress-fakeroot-schema.json similarity index 100% rename from ygen/testdata/schema/openconfig-options-compress-fakeroot-schema.json rename to gogen/testdata/schema/openconfig-options-compress-fakeroot-schema.json diff --git a/ygen/testdata/schema/openconfig-options-compress-fakeroot.formatted-txt b/gogen/testdata/schema/openconfig-options-compress-fakeroot.formatted-txt similarity index 100% rename from ygen/testdata/schema/openconfig-options-compress-fakeroot.formatted-txt rename to gogen/testdata/schema/openconfig-options-compress-fakeroot.formatted-txt diff --git a/ygen/testdata/schema/openconfig-options-compress-schema.json b/gogen/testdata/schema/openconfig-options-compress-schema.json similarity index 100% rename from ygen/testdata/schema/openconfig-options-compress-schema.json rename to gogen/testdata/schema/openconfig-options-compress-schema.json diff --git a/ygen/testdata/schema/openconfig-options-compress.formatted-txt b/gogen/testdata/schema/openconfig-options-compress.formatted-txt similarity index 100% rename from ygen/testdata/schema/openconfig-options-compress.formatted-txt rename to gogen/testdata/schema/openconfig-options-compress.formatted-txt diff --git a/ygen/testdata/schema/openconfig-options-explicit-schema.json b/gogen/testdata/schema/openconfig-options-explicit-schema.json similarity index 100% rename from ygen/testdata/schema/openconfig-options-explicit-schema.json rename to gogen/testdata/schema/openconfig-options-explicit-schema.json diff --git a/ygen/testdata/schema/openconfig-options-explicit.formatted-txt b/gogen/testdata/schema/openconfig-options-explicit.formatted-txt similarity index 100% rename from ygen/testdata/schema/openconfig-options-explicit.formatted-txt rename to gogen/testdata/schema/openconfig-options-explicit.formatted-txt diff --git a/ygen/testdata/schema/openconfig-options-nocompress-fakeroot-schema.json b/gogen/testdata/schema/openconfig-options-nocompress-fakeroot-schema.json similarity index 100% rename from ygen/testdata/schema/openconfig-options-nocompress-fakeroot-schema.json rename to gogen/testdata/schema/openconfig-options-nocompress-fakeroot-schema.json diff --git a/ygen/testdata/schema/openconfig-options-nocompress-fakeroot.formatted-txt b/gogen/testdata/schema/openconfig-options-nocompress-fakeroot.formatted-txt similarity index 100% rename from ygen/testdata/schema/openconfig-options-nocompress-fakeroot.formatted-txt rename to gogen/testdata/schema/openconfig-options-nocompress-fakeroot.formatted-txt diff --git a/ygen/testdata/schema/openconfig-options-nocompress-schema.json b/gogen/testdata/schema/openconfig-options-nocompress-schema.json similarity index 100% rename from ygen/testdata/schema/openconfig-options-nocompress-schema.json rename to gogen/testdata/schema/openconfig-options-nocompress-schema.json diff --git a/ygen/testdata/schema/openconfig-options-nocompress.formatted-txt b/gogen/testdata/schema/openconfig-options-nocompress.formatted-txt similarity index 100% rename from ygen/testdata/schema/openconfig-options-nocompress.formatted-txt rename to gogen/testdata/schema/openconfig-options-nocompress.formatted-txt diff --git a/ygen/testdata/schema/openconfig-options.yang b/gogen/testdata/schema/openconfig-options.yang similarity index 100% rename from ygen/testdata/schema/openconfig-options.yang rename to gogen/testdata/schema/openconfig-options.yang diff --git a/ygen/testdata/structs/choice-case-example.formatted-txt b/gogen/testdata/structs/choice-case-example.formatted-txt similarity index 100% rename from ygen/testdata/structs/choice-case-example.formatted-txt rename to gogen/testdata/structs/choice-case-example.formatted-txt diff --git a/ygen/testdata/structs/empty.formatted-txt b/gogen/testdata/structs/empty.formatted-txt similarity index 100% rename from ygen/testdata/structs/empty.formatted-txt rename to gogen/testdata/structs/empty.formatted-txt diff --git a/ygen/testdata/structs/enum-duplication-dedup.formatted-txt b/gogen/testdata/structs/enum-duplication-dedup.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-duplication-dedup.formatted-txt rename to gogen/testdata/structs/enum-duplication-dedup.formatted-txt diff --git a/ygen/testdata/structs/enum-duplication-dup.formatted-txt b/gogen/testdata/structs/enum-duplication-dup.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-duplication-dup.formatted-txt rename to gogen/testdata/structs/enum-duplication-dup.formatted-txt diff --git a/ygen/testdata/structs/enum-list-uncompressed.formatted-txt b/gogen/testdata/structs/enum-list-uncompressed.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-list-uncompressed.formatted-txt rename to gogen/testdata/structs/enum-list-uncompressed.formatted-txt diff --git a/ygen/testdata/structs/enum-list-uncompressed.wrapper-unions.formatted-txt b/gogen/testdata/structs/enum-list-uncompressed.wrapper-unions.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-list-uncompressed.wrapper-unions.formatted-txt rename to gogen/testdata/structs/enum-list-uncompressed.wrapper-unions.formatted-txt diff --git a/ygen/testdata/structs/enum-module.formatted-txt b/gogen/testdata/structs/enum-module.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-module.formatted-txt rename to gogen/testdata/structs/enum-module.formatted-txt diff --git a/ygen/testdata/structs/enum-module.long-enum-names.formatted-txt b/gogen/testdata/structs/enum-module.long-enum-names.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-module.long-enum-names.formatted-txt rename to gogen/testdata/structs/enum-module.long-enum-names.formatted-txt diff --git a/ygen/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.formatted-txt b/gogen/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.formatted-txt rename to gogen/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.formatted-txt diff --git a/ygen/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.wrapper-unions.formatted-txt b/gogen/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.wrapper-unions.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.wrapper-unions.formatted-txt rename to gogen/testdata/structs/enum-module.long-enum-names.residing-module-typedef-enum-name.wrapper-unions.formatted-txt diff --git a/ygen/testdata/structs/enum-module.residing-module-typedef-enum-name.formatted-txt b/gogen/testdata/structs/enum-module.residing-module-typedef-enum-name.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-module.residing-module-typedef-enum-name.formatted-txt rename to gogen/testdata/structs/enum-module.residing-module-typedef-enum-name.formatted-txt diff --git a/ygen/testdata/structs/enum-module.wrapper-unions.formatted-txt b/gogen/testdata/structs/enum-module.wrapper-unions.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-module.wrapper-unions.formatted-txt rename to gogen/testdata/structs/enum-module.wrapper-unions.formatted-txt diff --git a/ygen/testdata/structs/enum-multi-module.formatted-txt b/gogen/testdata/structs/enum-multi-module.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-multi-module.formatted-txt rename to gogen/testdata/structs/enum-multi-module.formatted-txt diff --git a/ygen/testdata/structs/enum-union-with-enum-defaults.formatted-txt b/gogen/testdata/structs/enum-union-with-enum-defaults.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-union-with-enum-defaults.formatted-txt rename to gogen/testdata/structs/enum-union-with-enum-defaults.formatted-txt diff --git a/ygen/testdata/structs/enum-union.consistent.formatted-txt b/gogen/testdata/structs/enum-union.consistent.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-union.consistent.formatted-txt rename to gogen/testdata/structs/enum-union.consistent.formatted-txt diff --git a/ygen/testdata/structs/enum-union.formatted-txt b/gogen/testdata/structs/enum-union.formatted-txt similarity index 100% rename from ygen/testdata/structs/enum-union.formatted-txt rename to gogen/testdata/structs/enum-union.formatted-txt diff --git a/ygen/testdata/structs/exclude-state-ro-list.formatted-txt b/gogen/testdata/structs/exclude-state-ro-list.formatted-txt similarity index 100% rename from ygen/testdata/structs/exclude-state-ro-list.formatted-txt rename to gogen/testdata/structs/exclude-state-ro-list.formatted-txt diff --git a/ygen/testdata/structs/excluded-module.formatted-txt b/gogen/testdata/structs/excluded-module.formatted-txt similarity index 100% rename from ygen/testdata/structs/excluded-module.formatted-txt rename to gogen/testdata/structs/excluded-module.formatted-txt diff --git a/ygen/testdata/structs/openconfig-augmented.formatted-txt b/gogen/testdata/structs/openconfig-augmented.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-augmented.formatted-txt rename to gogen/testdata/structs/openconfig-augmented.formatted-txt diff --git a/ygen/testdata/structs/openconfig-camelcase-compress.formatted-txt b/gogen/testdata/structs/openconfig-camelcase-compress.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-camelcase-compress.formatted-txt rename to gogen/testdata/structs/openconfig-camelcase-compress.formatted-txt diff --git a/ygen/testdata/structs/openconfig-config-false-compressed.formatted-txt b/gogen/testdata/structs/openconfig-config-false-compressed.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-config-false-compressed.formatted-txt rename to gogen/testdata/structs/openconfig-config-false-compressed.formatted-txt diff --git a/ygen/testdata/structs/openconfig-config-false-uncompressed.formatted-txt b/gogen/testdata/structs/openconfig-config-false-uncompressed.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-config-false-uncompressed.formatted-txt rename to gogen/testdata/structs/openconfig-config-false-uncompressed.formatted-txt diff --git a/ygen/testdata/structs/openconfig-enumcamelcase-compress.formatted-txt b/gogen/testdata/structs/openconfig-enumcamelcase-compress.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-enumcamelcase-compress.formatted-txt rename to gogen/testdata/structs/openconfig-enumcamelcase-compress.formatted-txt diff --git a/ygen/testdata/structs/openconfig-fakeroot-nc.formatted-txt b/gogen/testdata/structs/openconfig-fakeroot-nc.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-fakeroot-nc.formatted-txt rename to gogen/testdata/structs/openconfig-fakeroot-nc.formatted-txt diff --git a/ygen/testdata/structs/openconfig-fakeroot.formatted-txt b/gogen/testdata/structs/openconfig-fakeroot.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-fakeroot.formatted-txt rename to gogen/testdata/structs/openconfig-fakeroot.formatted-txt diff --git a/ygen/testdata/structs/openconfig-leaflist-default.formatted-txt b/gogen/testdata/structs/openconfig-leaflist-default.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-leaflist-default.formatted-txt rename to gogen/testdata/structs/openconfig-leaflist-default.formatted-txt diff --git a/ygen/testdata/structs/openconfig-list-enum-key.formatted-txt b/gogen/testdata/structs/openconfig-list-enum-key.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-list-enum-key.formatted-txt rename to gogen/testdata/structs/openconfig-list-enum-key.formatted-txt diff --git a/ygen/testdata/structs/openconfig-list-enum-key.getters-append.formatted-txt b/gogen/testdata/structs/openconfig-list-enum-key.getters-append.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-list-enum-key.getters-append.formatted-txt rename to gogen/testdata/structs/openconfig-list-enum-key.getters-append.formatted-txt diff --git a/ygen/testdata/structs/openconfig-list-enum-key.leaf-getters.formatted-txt b/gogen/testdata/structs/openconfig-list-enum-key.leaf-getters.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-list-enum-key.leaf-getters.formatted-txt rename to gogen/testdata/structs/openconfig-list-enum-key.leaf-getters.formatted-txt diff --git a/ygen/testdata/structs/openconfig-list-enum-key.trimmed-enum.formatted-txt b/gogen/testdata/structs/openconfig-list-enum-key.trimmed-enum.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-list-enum-key.trimmed-enum.formatted-txt rename to gogen/testdata/structs/openconfig-list-enum-key.trimmed-enum.formatted-txt diff --git a/ygen/testdata/structs/openconfig-multikey-list-name-conflict.formatted-txt b/gogen/testdata/structs/openconfig-multikey-list-name-conflict.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-multikey-list-name-conflict.formatted-txt rename to gogen/testdata/structs/openconfig-multikey-list-name-conflict.formatted-txt diff --git a/ygen/testdata/structs/openconfig-simple-annotations.formatted-txt b/gogen/testdata/structs/openconfig-simple-annotations.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-simple-annotations.formatted-txt rename to gogen/testdata/structs/openconfig-simple-annotations.formatted-txt diff --git a/ygen/testdata/structs/openconfig-simple-excludestate.formatted-txt b/gogen/testdata/structs/openconfig-simple-excludestate.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-simple-excludestate.formatted-txt rename to gogen/testdata/structs/openconfig-simple-excludestate.formatted-txt diff --git a/ygen/testdata/structs/openconfig-simple-no-compress.formatted-txt b/gogen/testdata/structs/openconfig-simple-no-compress.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-simple-no-compress.formatted-txt rename to gogen/testdata/structs/openconfig-simple-no-compress.formatted-txt diff --git a/ygen/testdata/structs/openconfig-simple-no-compress.trimmed-enum.formatted-txt b/gogen/testdata/structs/openconfig-simple-no-compress.trimmed-enum.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-simple-no-compress.trimmed-enum.formatted-txt rename to gogen/testdata/structs/openconfig-simple-no-compress.trimmed-enum.formatted-txt diff --git a/ygen/testdata/structs/openconfig-simple.formatted-txt b/gogen/testdata/structs/openconfig-simple.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-simple.formatted-txt rename to gogen/testdata/structs/openconfig-simple.formatted-txt diff --git a/ygen/testdata/structs/openconfig-simple.long-enum-names.trimmed-enum.formatted-txt b/gogen/testdata/structs/openconfig-simple.long-enum-names.trimmed-enum.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-simple.long-enum-names.trimmed-enum.formatted-txt rename to gogen/testdata/structs/openconfig-simple.long-enum-names.trimmed-enum.formatted-txt diff --git a/ygen/testdata/structs/openconfig-unione.formatted-txt b/gogen/testdata/structs/openconfig-unione.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-unione.formatted-txt rename to gogen/testdata/structs/openconfig-unione.formatted-txt diff --git a/ygen/testdata/structs/openconfig-unione.wrapper-unions.formatted-txt b/gogen/testdata/structs/openconfig-unione.wrapper-unions.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-unione.wrapper-unions.formatted-txt rename to gogen/testdata/structs/openconfig-unione.wrapper-unions.formatted-txt diff --git a/ygen/testdata/structs/openconfig-versioned-mod.formatted-txt b/gogen/testdata/structs/openconfig-versioned-mod.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-versioned-mod.formatted-txt rename to gogen/testdata/structs/openconfig-versioned-mod.formatted-txt diff --git a/ygen/testdata/structs/openconfig-withlist-opstate.formatted-txt b/gogen/testdata/structs/openconfig-withlist-opstate.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-withlist-opstate.formatted-txt rename to gogen/testdata/structs/openconfig-withlist-opstate.formatted-txt diff --git a/ygen/testdata/structs/openconfig-withlist.formatted-txt b/gogen/testdata/structs/openconfig-withlist.formatted-txt similarity index 100% rename from ygen/testdata/structs/openconfig-withlist.formatted-txt rename to gogen/testdata/structs/openconfig-withlist.formatted-txt diff --git a/ygen/testdata/structs/presence-container-example.formatted-txt b/gogen/testdata/structs/presence-container-example.formatted-txt similarity index 100% rename from ygen/testdata/structs/presence-container-example.formatted-txt rename to gogen/testdata/structs/presence-container-example.formatted-txt diff --git a/ygen/testdata/structs/root-entities.formatted-txt b/gogen/testdata/structs/root-entities.formatted-txt similarity index 100% rename from ygen/testdata/structs/root-entities.formatted-txt rename to gogen/testdata/structs/root-entities.formatted-txt diff --git a/internal/igenutil/genutil.go b/internal/igenutil/genutil.go new file mode 100644 index 000000000..2e77d2799 --- /dev/null +++ b/internal/igenutil/genutil.go @@ -0,0 +1,119 @@ +// Copyright 2022 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package igenutil contains internal generation utilities. +package igenutil + +import ( + "bytes" + "fmt" + "strings" + "text/template" + + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/util" + + log "github.com/golang/glog" +) + +const ( + // RootElementNodeName is the synthesised node name that is used for an + // element that represents the root. Such an element is generated only + // when the GenerateFakeRoot bool is set to true within the + // YANGCodeGenerator instance used as a receiver. + RootElementNodeName = "!fakeroot!" + // DefaultRootName is the default name for the root structure if GenerateFakeRoot is + // set to true. + DefaultRootName = "device" +) + +var ( + // TemplateHelperFunctions specifies a set of functions that are supplied as + // helpers to the templates that are used within this file. + TemplateHelperFunctions = template.FuncMap{ + // inc provides a means to add 1 to a number, and is used within templates + // to check whether the index of an element within a loop is the last one, + // such that special handling can be provided for it (e.g., not following + // it with a comma in a list of arguments). + "inc": func(i int) int { + return i + 1 + }, + "toUpper": strings.ToUpper, + "indentLines": func(s string) string { + var b bytes.Buffer + p := strings.Split(s, "\n") + b.WriteRune('\n') + for i, l := range p { + if l == "" { + continue + } + b.WriteString(fmt.Sprintf(" %s", l)) + if i != len(p)-1 { + b.WriteRune('\n') + } + } + return b.String() + }, + // stripAsteriskPrefix provides a template helper that removes an asterisk + // from the start of a string. It is used to remove "*" from the start of + // pointer types. + "stripAsteriskPrefix": func(s string) string { return strings.TrimPrefix(s, "*") }, + } +) + +// MustMakeTemplate generates a template.Template for a particular named source +// template; with a common set of helper functions. +func MustMakeTemplate(name, src string) *template.Template { + return template.Must(template.New(name).Funcs(TemplateHelperFunctions).Parse(src)) +} + +// IsFakeRoot checks whether a given entry is the generated fake root. +func IsFakeRoot(e *yang.Entry) bool { + return e != nil && e.Node != nil && e.Node.NName() == RootElementNodeName +} + +// MappableLeaf determines whether the yang.Entry e is leaf with an +// enumerated value, such that the referenced enumerated type (enumeration or +// identity) should have code generated for it. If it is an enumerated type +// the leaf is returned. +func MappableLeaf(e *yang.Entry) *yang.Entry { + if e.Type == nil { + // If the type of the leaf is nil, then this is not a valid + // leaf within the schema - since goyang must populate the + // entry Type. + // TODO(robjs): Add this as an error case that can be handled by + // the caller directly. + log.Warningf("got unexpected nil value type for leaf %s (%s), entry: %v", e.Name, e.Path(), e) + return nil + } + + var types []*yang.YangType + switch { + case util.IsEnumeratedType(e.Type): + // Handle the case that this leaf is an enumeration or identityref itself. + // This also handles cases where the leaf is a typedef that is an enumeration + // or identityref, since the util.IsEnumeratedType check does not use the name of the + // type. + types = append(types, e.Type) + case util.IsUnionType(e.Type): + // Check for leaves that include a union that itself + // includes an identityref or enumerated value. + types = append(types, util.EnumeratedUnionTypes(e.Type.Type)...) + } + + if types != nil { + return e + } + return nil +} diff --git a/ygen/codegen.go b/ygen/codegen.go index dd4f7ed00..b4f9720b1 100644 --- a/ygen/codegen.go +++ b/ygen/codegen.go @@ -23,12 +23,11 @@ import ( "sort" "strings" - log "github.com/golang/glog" - "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" "github.com/openconfig/ygot/util" - "github.com/openconfig/ygot/ygot" + + "github.com/openconfig/ygot/internal/igenutil" gpb "github.com/openconfig/gnmi/proto/gnmi" ) @@ -62,9 +61,6 @@ type GeneratorConfig struct { // may be transformed from a simple 1:1 mapping with respect to the // given YANG schema. TransformationOptions TransformationOpts - // GoOptions stores a struct which stores Go code generation specific - // options for the code generaton. - GoOptions GoOpts // ProtoOptions stores a struct which contains Protobuf specific options. ProtoOptions ProtoOpts // IncludeDescriptions specifies that YANG entry descriptions are added @@ -72,22 +68,6 @@ type GeneratorConfig struct { IncludeDescriptions bool } -// DirectoryGenConfig contains the configuration necessary to generate a set of -// Directory objects for a given schema. The set of Directory objects is the -// intermediate representation generated by ygen, which can be useful for -// external code generation libraries which can make use of it. -type DirectoryGenConfig struct { - // ParseOptions contains parsing options for a given set of schema files. - ParseOptions ParseOpts - // TransformationOptions contains options for how the generated code - // may be transformed from a simple 1:1 mapping with respect to the - // given YANG schema. - TransformationOptions TransformationOpts - // GoOptions stores a struct which stores Go code generation specific - // options for the code generaton. - GoOptions GoOpts -} - // ParseOpts contains parsing configuration for a given schema. type ParseOpts struct { // ExcludeModules specifies any modules that are included within the set of @@ -167,86 +147,6 @@ type TransformationOpts struct { EnumerationsUseUnderscores bool } -// GoOpts stores Go specific options for the code generation library. -type GoOpts struct { - // SchemaVarName is the name for the variable which stores the compressed - // JSON schema in the generated Go code. JSON schema output is only - // produced if the GenerateJSONSchema YANGCodeGenerator field is set to - // true. - SchemaVarName string - // GoyangImportPath specifies the path that should be used in the generated - // code for importing the goyang/pkg/yang package. - GoyangImportPath string - // YgotImportPath specifies the path to the ygot library that should be used - // in the generated code. - YgotImportPath string - // YtypesImportPath specifies the path to ytypes library that should be used - // in the generated code. - YtypesImportPath string - // GenerateRenameMethod specifies whether methods for renaming list entries - // should be generated in the output Go code. - GenerateRenameMethod bool - // AddAnnotationFields specifies whether annotation fields should be added to - // the generated structs. When set to true, a metadata field is added for each - // struct, and for each field of each struct. Metadata field's names are - // prefixed by the string specified in the AnnotationPrefix argument. - AddAnnotationFields bool - // AnnotationPrefix specifies the string which is prefixed to the name of - // annotation fields. It defaults to Λ. - AnnotationPrefix string - // AddYangPresence specifies whether tags should be added to the generated - // fields of a struct. When set to true, a struct tag will be added to the field - // when a YANG container is a presence container - // https://datatracker.ietf.org/doc/html/rfc6020#section-7.5.1 - // a field tag of `yangPresence="true"` will only be added if the container is - // a YANG presence container, and will be omitted if this is not the case. - AddYangPresence bool - // GenerateGetters specifies whether GetOrCreate* methods should be created - // for struct pointer (YANG container) and map (YANG list) fields of generated - // structs. - GenerateGetters bool - // GenerateDeleteMethod specifies whether Delete* methods should be created for - // map (YANG list) fields of generated structs. - GenerateDeleteMethod bool - // GenerateAppendList specifies whether Append* methods should be created for - // list fields of a struct. These methods take an input list member type, extract - // the key and append the supplied value to the list. - GenerateAppendMethod bool - // GenerateSimpleUnions specifies whether simple typedefs are used to - // represent union subtypes in the generated code instead of using - // wrapper types. - GenerateSimpleUnions bool - // GenerateLeafGetters specifies whether Get* methods should be created for - // leaf fields of a struct. Care should be taken with this option since a Get - // method returns the *Go* zero value for a particular entity if the field is - // unset. This means that it is not possible for a caller of method to know - // whether a field has been explicitly set to the zero value (i.e., an integer - // field is set to 0), or whether the field was actually unset. - GenerateLeafGetters bool - // GeneratePopulateDefault specifies whether a PopulateDefaults method - // should be generated for every GoStruct that recursively populates - // default values within the subtree. - GeneratePopulateDefault bool - // GNMIProtoPath specifies the path to the generated gNMI protobuf, which - // is used to store the catalogue entries for generated modules. - GNMIProtoPath string - // ValidateFunctionName specifies the name of a function that proxies ΛValidate. - ValidateFunctionName string - // IncludeModelData specifies whether gNMI ModelData messages should be generated - // in the output code. - IncludeModelData bool - // AppendEnumSuffixForSimpleUnionEnums appends an "Enum" suffix to the - // enumeration name for simple (i.e. non-typedef) leaves which are - // unions with an enumeration inside. This makes all inlined - // enumerations within unions, whether typedef or not, have this - // suffix, achieving consistency. Since this flag is planned to be a - // v1 compatibility flag along with - // UseDefiningModuleForTypedefEnumNames, and will be removed in v1, it - // only applies when useDefiningModuleForTypedefEnumNames is also set - // to true. - AppendEnumSuffixForSimpleUnionEnums bool -} - // ProtoOpts stores Protobuf specific options for the code generation library. type ProtoOpts struct { // BaseImportPath stores the root URL or path for imports that are @@ -312,43 +212,6 @@ type yangEnum struct { id string } -// GeneratedGoCode contains generated code snippets that can be processed by the calling -// application. The generated code is divided into two types of objects - both represented -// as a slice of strings: Structs contains a set of Go structures that have been generated, -// and Enums contains the code for generated enumerated types (corresponding to identities, -// or enumerated values within the YANG models for which code is being generated). Additionally -// the header with package comment of the generated code is returned in Header, along with the -// a slice of strings containing the packages that are required for the generated Go code to -// be compiled is returned. -// -// For schemas that contain enumerated types (identities, or enumerations), a code snippet is -// returned as the EnumMap field that allows the string values from the YANG schema to be resolved. -// The keys of the map are strings corresponding to the name of the generated type, with the -// map values being maps of the int64 identifier for each value of the enumeration to the name of -// the element, as used in the YANG schema. -type GeneratedGoCode struct { - Structs []GoStructCodeSnippet // Structs is the generated set of structs representing containers or lists in the input YANG models. - Enums []string // Enums is the generated set of enum definitions corresponding to identities and enumerations in the input YANG models. - CommonHeader string // CommonHeader is the header that should be used for all output Go files. - OneOffHeader string // OneOffHeader defines the header that should be included in only one output Go file - such as package init statements. - EnumMap string // EnumMap is a Go map that allows the YANG string values of enumerated types to be resolved. - // JSONSchemaCode contains code defining a variable storing a serialised JSON schema for the - // generated Go structs. When deserialised it consists of a map[string]*yang.Entry. The - // entries are the root level yang.Entry definitions along with their corresponding - // hierarchy (i.e., the yang.Entry for /foo contains /foo/... - all of foo's descendents). - // Each yang.Entry which corresponds to a generated Go struct has two extra fields defined: - // - schemapath - the path to this entry within the schema. This is provided since the Path() method of - // the deserialised yang.Entry does not return the path since the Parent pointer is not - // populated. - // - structname - the name of the struct that was generated for the schema element. - JSONSchemaCode string - // RawJSONSchema stores the JSON document which is serialised and stored in JSONSchemaCode. - // It is populated only if the StoreRawSchema YANGCodeGenerator boolean is set to true. - RawJSONSchema []byte - // EnumTypeMap is a Go map that allows YANG schemapaths to be mapped to reflect.Type values. - EnumTypeMap string -} - // GeneratedProto3 stores a set of generated Protobuf packages. type GeneratedProto3 struct { // Packages stores a map, keyed by the Protobuf package name, and containing the contents of the protobuf3 @@ -367,17 +230,6 @@ type Proto3Package struct { UsesYextImport bool // UsesYextImport indicates whether the yext proto package is used within the generated package. } -const ( - // rootElementNodeName is the synthesised node name that is used for an - // element that represents the root. Such an element is generated only - // when the GenerateFakeRoot bool is set to true within the - // YANGCodeGenerator instance used as a receiver. - rootElementNodeName = "!fakeroot!" - // defaultRootName is the default name for the root structure if GenerateFakeRoot is - // set to true. - defaultRootName = "device" -) - // generatedLanguage represents a language supported in this package. type generatedLanguage int64 @@ -388,285 +240,6 @@ const ( protobuf ) -// IsFakeRoot checks whether a given entry is the generated fake root. -func IsFakeRoot(e *yang.Entry) bool { - return e != nil && e.Node != nil && e.Node.NName() == rootElementNodeName -} - -// checkForBinaryKeys returns a non-empty list of errors if the input directory -// has one or more binary types (including union types containing binary types) -// as a list key. -func checkForBinaryKeys(dir *ParsedDirectory) []error { - var errs []error - for _, k := range dir.ListKeys { - if k.LangType.NativeType == ygot.BinaryTypeName { - errs = append(errs, fmt.Errorf("list %s has a binary key -- this is unsupported", dir.Path)) - continue - } - for typeName := range k.LangType.UnionTypes { - if typeName == ygot.BinaryTypeName { - errs = append(errs, fmt.Errorf("list %s has a union key containing a binary -- this is unsupported", dir.Path)) - } - } - } - return errs -} - -// GenerateGoCode takes a slice of strings containing the path to a set of YANG -// files which contain YANG modules, and a second slice of strings which -// specifies the set of paths that are to be searched for associated models (e.g., -// modules that are included by the specified set of modules, or submodules of those -// modules). It extracts the set of modules that are to be generated, and returns -// a GeneratedGoCode struct which contains: -// 1. A struct definition for each container or list that is within the specified -// set of models. -// 2. Enumerated values which correspond to the set of enumerated entities (leaves -// of type enumeration, identities, typedefs that reference an enumeration) -// within the specified models. -// If errors are encountered during code generation, an error is returned. -func (cg *YANGCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*GeneratedGoCode, util.Errors) { - opts := IROptions{ - ParseOptions: cg.Config.ParseOptions, - TransformationOptions: cg.Config.TransformationOptions, - NestedDirectories: false, - AbsoluteMapPaths: false, - AppendEnumSuffixForSimpleUnionEnums: cg.Config.GoOptions.AppendEnumSuffixForSimpleUnionEnums, - } - - var codegenErr util.Errors - ir, err := GenerateIR(yangFiles, includePaths, NewGoLangMapper(cg.Config.GoOptions.GenerateSimpleUnions), opts) - if err != nil { - return nil, util.AppendErr(codegenErr, err) - } - - var rootName string - if cg.Config.TransformationOptions.GenerateFakeRoot { - rootName = cg.Config.TransformationOptions.FakeRootName - if rootName == "" { - rootName = defaultRootName - } - if r, ok := ir.Directories[fmt.Sprintf("/%s", rootName)]; ok { - rootName = r.Name - } - } - commonHeader, oneoffHeader, err := writeGoHeader(yangFiles, includePaths, cg.Config, rootName, ir.ModelData) - if err != nil { - return nil, util.AppendErr(codegenErr, err) - } - - usedEnumeratedTypes := map[string]bool{} - // generatedUnions stores a map, keyed by the output name for a union, - // that has already been output in the generated code. This ensures that - // where two entities re-use a union that has already been created (e.g., - // a leafref to a union) then it is output only once in the generated code. - generatedUnions := map[string]bool{} - enumTypeMap := map[string][]string{} - structSnippets := []GoStructCodeSnippet{} - - isBuiltInType := func(fType string) bool { - _, ok := validGoBuiltinTypes[fType] - return ok - } - - // Range through the directories to find the enumerated and union types that we - // need. We have to do this without writing the code out, since we require some - // knowledge of these types to do code generation along with the values. - for _, directoryPath := range ir.OrderedDirectoryPathsByName() { - dir := ir.Directories[directoryPath] - - // Generate structs. - if errs := checkForBinaryKeys(dir); len(errs) != 0 { - codegenErr = util.AppendErrs(codegenErr, errs) - continue - } - structOut, errs := writeGoStruct(dir, ir.Directories, generatedUnions, opts.TransformationOptions.IgnoreShadowSchemaPaths, cg.Config.GoOptions, cg.Config.GenerateJSONSchema) - if errs != nil { - codegenErr = util.AppendErrs(codegenErr, errs) - continue - } - structSnippets = append(structSnippets, structOut) - - // Record down all the enum types we encounter in each field. - - // definedUnionTypes keeps track of which unions we have - // already processed to avoid processing the same one twice. - definedUnionTypes := map[string]bool{} - for _, fn := range dir.OrderedFieldNames() { - field := dir.Fields[fn] - - // Strip the module name from the path. - schemaPath := util.SlicePathToString(append([]string{""}, strings.Split(field.YANGDetails.Path, "/")[2:]...)) - switch { - case field.LangType == nil: - // This is a directory, so we continue. - continue - case field.LangType.IsEnumeratedValue: - usedEnumeratedTypes[field.LangType.NativeType] = true - enumTypeMap[schemaPath] = []string{field.LangType.NativeType} - case len(field.LangType.UnionTypes) > 1: - if definedUnionTypes[field.LangType.NativeType] { - continue - } - definedUnionTypes[field.LangType.NativeType] = true - - for ut := range field.LangType.UnionTypes { - if !isBuiltInType(ut) { - // non-builtin union types are always enumerated types. - usedEnumeratedTypes[ut] = true - if enumTypeMap[schemaPath] == nil { - enumTypeMap[schemaPath] = []string{} - } - enumTypeMap[schemaPath] = append(enumTypeMap[schemaPath], ut) - } - } - // Sort the enumerated types into schema order. - sort.Slice(enumTypeMap[schemaPath], func(i, j int) bool { - return field.LangType.UnionTypes[enumTypeMap[schemaPath][i]] < field.LangType.UnionTypes[enumTypeMap[schemaPath][j]] - }) - } - } - } - - processedEnums, err := genGoEnumeratedTypes(ir.Enums) - if err != nil { - return nil, append(codegenErr, err) - } - - genum, err := writeGoEnumeratedTypes(processedEnums, usedEnumeratedTypes) - if err != nil { - return nil, append(codegenErr, err) - } - - var rawSchema []byte - var jsonSchema string - var enumTypeMapCode string - if cg.Config.GenerateJSONSchema { - var err error - rawSchema, err = ir.SchemaTree(cg.Config.IncludeDescriptions) - if err != nil { - codegenErr = util.AppendErr(codegenErr, fmt.Errorf("error marshalling JSON schema: %v", err)) - } - - if rawSchema != nil { - if jsonSchema, err = writeGoSchema(rawSchema, cg.Config.GoOptions.SchemaVarName); err != nil { - codegenErr = util.AppendErr(codegenErr, err) - } - } - - if enumTypeMapCode, err = generateEnumTypeMap(enumTypeMap); err != nil { - codegenErr = util.AppendErr(codegenErr, err) - } - } - - // Return any errors that were encountered during code generation. - if len(codegenErr) != 0 { - return nil, codegenErr - } - - return &GeneratedGoCode{ - CommonHeader: commonHeader, - OneOffHeader: oneoffHeader, - Structs: structSnippets, - Enums: genum.enums, - EnumMap: genum.valMap, - JSONSchemaCode: jsonSchema, - RawJSONSchema: rawSchema, - EnumTypeMap: enumTypeMapCode, - }, nil -} - -// goEnumeratedType contains the intermediate representation of an enumerated -// type (identityref or enumeration) suitable for Go code generation. -type goEnumeratedType struct { - Name string - CodeValues map[int64]string - YANGValues map[int64]ygot.EnumDefinition -} - -// enumGeneratedCode contains generated Go code for enumerated types. -type enumGeneratedCode struct { - enums []string - valMap string -} - -// genGoEnumeratedTypes converts the input map of EnumeratedYANGType objects to -// another intermediate representation suitable for Go code generation. -func genGoEnumeratedTypes(enums map[string]*EnumeratedYANGType) (map[string]*goEnumeratedType, error) { - et := map[string]*goEnumeratedType{} - for _, e := range enums { - // initialised to be UNSET, such that it is possible to determine that the enumerated value - // was not modified. - values := map[int64]string{ - 0: "UNSET", - } - - // origValues stores the original set of value names, these are not maintained to be - // Go-safe, and are rather used to map back to the original schema values if required. - // 0 is not populated within this map, such that the values can be used to check whether - // there was a valid entry in the original schema. The value is stored as a ygot - // EnumDefinition, which stores the name, and in the case of identity values, the - // module within which the identity was defined. - origValues := map[int64]ygot.EnumDefinition{} - - switch e.Kind { - case IdentityType, SimpleEnumerationType, DerivedEnumerationType, UnionEnumerationType, DerivedUnionEnumerationType: - for i, v := range e.ValToYANGDetails { - values[int64(i)+1] = safeGoEnumeratedValueName(v.Name) - origValues[int64(i)+1] = v - } - default: - return nil, fmt.Errorf("unknown enumerated type %v", e.Kind) - } - - et[e.Name] = &goEnumeratedType{ - Name: e.Name, - CodeValues: values, - YANGValues: origValues, - } - } - return et, nil -} - -// writeGoEnumeratedTypes generates Go code for the input enumerations if they -// are present in the usedEnums map. -func writeGoEnumeratedTypes(enums map[string]*goEnumeratedType, usedEnums map[string]bool) (*enumGeneratedCode, error) { - orderedEnumNames := []string{} - for _, e := range enums { - orderedEnumNames = append(orderedEnumNames, e.Name) - } - sort.Strings(orderedEnumNames) - - enumValMap := map[string]map[int64]ygot.EnumDefinition{} - enumSnippets := []string{} - - for _, en := range orderedEnumNames { - e := enums[en] - if _, ok := usedEnums[fmt.Sprintf("%s%s", goEnumPrefix, e.Name)]; !ok { - // Don't output enumerated types that are not used in the code that we have - // such that we don't create generated code for a large array of types that - // just happen to be in modules that were included by other modules. - continue - } - enumOut, err := writeGoEnum(e) - if err != nil { - return nil, err - } - enumSnippets = append(enumSnippets, enumOut) - enumValMap[e.Name] = e.YANGValues - } - - // Write the map of string -> int -> YANG enum name string out. - vmap, err := writeGoEnumMap(enumValMap) - if err != nil { - return nil, err - } - - return &enumGeneratedCode{ - enums: enumSnippets, - valMap: vmap, - }, nil -} - // GenerateProto3 generates Protobuf 3 code for the input set of YANG files. // The YANG schemas for which protobufs are to be created is supplied as the // yangFiles argument, with included modules being searched for in includePaths. @@ -992,41 +565,6 @@ func mappedDefinitions(yangFiles, includePaths []string, cfg *GeneratorConfig) ( }, nil } -// mappableLeaf determines whether the yang.Entry e is leaf with an -// enumerated value, such that the referenced enumerated type (enumeration or -// identity) should have code generated for it. If it is an enumerated type -// the leaf is returned. -func mappableLeaf(e *yang.Entry) *yang.Entry { - if e.Type == nil { - // If the type of the leaf is nil, then this is not a valid - // leaf within the schema - since goyang must populate the - // entry Type. - // TODO(robjs): Add this as an error case that can be handled by - // the caller directly. - log.Warningf("got unexpected nil value type for leaf %s (%s), entry: %v", e.Name, e.Path(), e) - return nil - } - - var types []*yang.YangType - switch { - case util.IsEnumeratedType(e.Type): - // Handle the case that this leaf is an enumeration or identityref itself. - // This also handles cases where the leaf is a typedef that is an enumeration - // or identityref, since the util.IsEnumeratedType check does not use the name of the - // type. - types = append(types, e.Type) - case util.IsUnionType(e.Type): - // Check for leaves that include a union that itself - // includes an identityref or enumerated value. - types = append(types, util.EnumeratedUnionTypes(e.Type.Type)...) - } - - if types != nil { - return e - } - return nil -} - // findMappableEntities finds the descendants of a yang.Entry (e) that should be mapped in // the generated code. The descendants that represent directories are appended to the dirs // map (keyed by the schema path). Those that represent enumerated types (identityref, enumeration, @@ -1054,7 +592,7 @@ func findMappableEntities(e *yang.Entry, dirs map[string]*yang.Entry, enums map[ // Leaves are not mapped as directories so do not map them unless we find // something that will be an enumeration - so that we can deal with this // as a top-level code entity. - if e := mappableLeaf(ch); e != nil { + if e := igenutil.MappableLeaf(ch); e != nil { enums[ch.Path()] = e } case util.IsConfigState(ch) && compressPaths: @@ -1077,7 +615,7 @@ func findMappableEntities(e *yang.Entry, dirs map[string]*yang.Entry, enums map[ // so we need to check whether it is an enumerated leaf that // should have code generated for it. if gch.IsLeaf() || gch.IsLeafList() { - if e := mappableLeaf(gch); e != nil { + if e := igenutil.MappableLeaf(gch); e != nil { enums[e.Path()] = e } continue @@ -1143,7 +681,7 @@ func MakeFakeRoot(rootName string) *yang.Entry { // Create a fake node that corresponds to the fake root, this // ensures that we can match the element elsewhere. Node: &yang.Value{ - Name: rootElementNodeName, + Name: igenutil.RootElementNodeName, }, } } @@ -1161,7 +699,7 @@ func MakeFakeRoot(rootName string) *yang.Entry { // indicating the root name. func createFakeRoot(structs map[string]*yang.Entry, rootElems []*yang.Entry, rootName string, compressPaths bool) error { if rootName == "" { - rootName = defaultRootName + rootName = igenutil.DefaultRootName } fakeRoot := MakeFakeRoot(rootName) diff --git a/ygen/codegen_test.go b/ygen/codegen_test.go index 5626e0d6e..a3aa64ad1 100644 --- a/ygen/codegen_test.go +++ b/ygen/codegen_test.go @@ -16,18 +16,17 @@ package ygen import ( "bytes" - "encoding/json" "fmt" "io/ioutil" "path/filepath" "sort" "testing" - "github.com/google/go-cmp/cmp" "github.com/kylelemons/godebug/pretty" "github.com/openconfig/gnmi/errdiff" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/internal/igenutil" "github.com/openconfig/ygot/testutil" ) @@ -402,949 +401,6 @@ func TestFindMappableEntities(t *testing.T) { } } -// yangTestCase describs a test case for which code generation is performed -// through Goyang's API, it provides the input set of parameters in a way that -// can be reused across tests. -type yangTestCase struct { - name string // Name is the identifier for the test. - inFiles []string // inFiles is the set of inputFiles for the test. - inIncludePaths []string // inIncludePaths is the set of paths that should be searched for imports. - inExcludeModules []string // inExcludeModules is the set of modules that should be excluded from code generation. - inConfig GeneratorConfig // inConfig specifies the configuration that should be used for the generator test case. - wantStructsCodeFile string // wantsStructsCodeFile is the path of the generated Go code that the output of the test should be compared to. - wantErrSubstring string // wantErrSubstring specifies whether the test should expect an error. - wantSchemaFile string // wantSchemaFile is the path to the schema JSON that the output of the test should be compared to. -} - -// TestSimpleStructs tests the processModules, GenerateGoCode and writeGoCode -// functions. It takes the set of YANG modules described in the slice of -// yangTestCases and generates the struct code for them, comparing the output -// to the wantStructsCodeFile. In order to simplify the files that are used, -// the GenerateGoCode structs are concatenated before comparison with the -// expected output. If the generated code matches the expected output, it is -// run against the Go parser to ensure that the code is valid Go - this is -// expected, but it ensures that the input file does not contain Go which is -// invalid. -func TestSimpleStructs(t *testing.T) { - tests := []yangTestCase{{ - name: "simple openconfig test, with compression, with (useless) enum org name trimming", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple.formatted-txt"), - }, { - name: "simple openconfig test, with excluded state, with compression, with enum org name trimming", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.ExcludeDerivedState, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-excludestate.formatted-txt"), - }, { - name: "simple openconfig test, with no compression", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - }, - TransformationOptions: TransformationOpts{ - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-no-compress.formatted-txt"), - }, { - name: "simple openconfig test, with compression, without shortened enum leaf names, with enum org name trimming", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple.long-enum-names.trimmed-enum.formatted-txt"), - }, { - name: "simple openconfig test, with no compression, with enum org name trimming", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-no-compress.trimmed-enum.formatted-txt"), - }, { - name: "OpenConfig leaf-list defaults test, with compression", - inFiles: []string{filepath.Join(datapath, "openconfig-leaflist-default.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-leaflist-default.formatted-txt"), - }, { - name: "OpenConfig schema test - with annotations", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - AddAnnotationFields: true, - AnnotationPrefix: "ᗩ", - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-simple-annotations.formatted-txt"), - }, { - name: "OpenConfig schema test - list and associated method (rename, new)", - inFiles: []string{filepath.Join(datapath, "openconfig-withlist.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-withlist.formatted-txt"), - }, { - name: "OpenConfig schema test - list and associated method (rename, new) - using operational state", - inFiles: []string{filepath.Join(datapath, "openconfig-withlist.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferOperationalState, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-withlist-opstate.formatted-txt"), - }, { - name: "OpenConfig schema test - multi-keyed list key struct name conflict and associated method (rename, new)", - inFiles: []string{filepath.Join(datapath, "openconfig-multikey-list-name-conflict.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-multikey-list-name-conflict.formatted-txt"), - }, { - name: "simple openconfig test, with a list that has an enumeration key", - inFiles: []string{filepath.Join(datapath, "openconfig-list-enum-key.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - IgnoreShadowSchemaPaths: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-list-enum-key.formatted-txt"), - }, { - name: "simple openconfig test, with a list that has an enumeration key, with enum org name trimming", - inFiles: []string{filepath.Join(datapath, "openconfig-list-enum-key.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-list-enum-key.trimmed-enum.formatted-txt"), - }, { - name: "openconfig test with a identityref union", - inFiles: []string{filepath.Join(datapath, "openconfig-unione.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-unione.formatted-txt"), - }, { - name: "openconfig test with a identityref union (wrapper unions)", - inFiles: []string{filepath.Join(datapath, "openconfig-unione.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-unione.wrapper-unions.formatted-txt"), - }, { - name: "openconfig tests with fakeroot", - inFiles: []string{filepath.Join(datapath, "openconfig-fakeroot.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-fakeroot.formatted-txt"), - }, { - name: "openconfig noncompressed tests with fakeroot", - inFiles: []string{filepath.Join(datapath, "openconfig-fakeroot.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-fakeroot-nc.formatted-txt"), - }, { - name: "schema test with compression", - inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GenerateJSONSchema: true, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress.formatted-txt"), - wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress-schema.json"), - }, { - name: "schema test without compression", - inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - EnumerationsUseUnderscores: true, - }, - GenerateJSONSchema: true, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress.formatted-txt"), - wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress-schema.json"), - }, { - name: "schema test with fakeroot", - inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GenerateJSONSchema: true, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress-fakeroot.formatted-txt"), - wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress-fakeroot-schema.json"), - }, { - name: "schema test with fakeroot and no compression", - inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GenerateJSONSchema: true, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress-fakeroot.formatted-txt"), - wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress-fakeroot-schema.json"), - }, { - name: "schema test with camelcase annotations", - inFiles: []string{filepath.Join(datapath, "openconfig-camelcase.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-camelcase-compress.formatted-txt"), - }, { - name: "structs test with camelcase annotations", - inFiles: []string{filepath.Join(datapath, "openconfig-enumcamelcase.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-enumcamelcase-compress.formatted-txt"), - }, { - name: "structs test with choices and cases", - inFiles: []string{filepath.Join(datapath, "choice-case-example.yang")}, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/choice-case-example.formatted-txt"), - }, { - name: "module with augments", - inFiles: []string{ - filepath.Join(datapath, "openconfig-simple-target.yang"), - filepath.Join(datapath, "openconfig-simple-augment.yang"), - }, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-augmented.formatted-txt"), - }, { - name: "variable and import explicitly specified", - inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - FakeRootName: "fakeroot", - EnumerationsUseUnderscores: true, - }, - Caller: "testcase", - StoreRawSchema: true, - GenerateJSONSchema: true, - GoOptions: GoOpts{ - SchemaVarName: "YANGSchema", - GoyangImportPath: "foo/goyang", - YgotImportPath: "bar/ygot", - YtypesImportPath: "baz/ytypes", - GenerateSimpleUnions: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-explicit.formatted-txt"), - wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-explicit-schema.json"), - }, { - name: "module with entities at the root", - inFiles: []string{filepath.Join(datapath, "root-entities.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - FakeRootName: "fakeroot", - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, - }, - Caller: "testcase", - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/root-entities.formatted-txt"), - }, { - name: "module with empty leaf", - inFiles: []string{filepath.Join(datapath, "empty.yang")}, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/empty.formatted-txt"), - }, { - name: "module with excluded modules", - inFiles: []string{filepath.Join(datapath, "excluded-module.yang")}, - inExcludeModules: []string{"excluded-module-two"}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - FakeRootName: "office", - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/excluded-module.formatted-txt"), - }, { - name: "module with excluded config false", - inFiles: []string{filepath.Join(datapath, "", "openconfig-config-false.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.UncompressedExcludeDerivedState, - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-config-false-uncompressed.formatted-txt"), - }, { - name: "module with excluded config false - with compression", - inFiles: []string{filepath.Join(datapath, "", "openconfig-config-false.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - CompressBehaviour: genutil.ExcludeDerivedState, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-config-false-compressed.formatted-txt"), - }, { - name: "module with getters, delete and append methods", - inFiles: []string{filepath.Join(datapath, "", "openconfig-list-enum-key.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - GenerateAppendMethod: true, - GenerateGetters: true, - GenerateDeleteMethod: true, - GenerateSimpleUnions: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-list-enum-key.getters-append.formatted-txt"), - }, { - name: "module with excluded state, with RO list, path compression on", - inFiles: []string{filepath.Join(datapath, "", "exclude-state-ro-list.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - CompressBehaviour: genutil.ExcludeDerivedState, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "exclude-state-ro-list.formatted-txt"), - }, { - name: "different union enumeration types", - inFiles: []string{filepath.Join(datapath, "", "enum-union.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-union.formatted-txt"), - }, { - name: "different union enumeration types with consistent naming for union-inlined enums", - inFiles: []string{filepath.Join(datapath, "", "enum-union.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - AppendEnumSuffixForSimpleUnionEnums: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-union.consistent.formatted-txt"), - }, { - name: "different union enumeration types with default enum values", - inFiles: []string{filepath.Join(datapath, "", "enum-union-with-enum-defaults.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - AppendEnumSuffixForSimpleUnionEnums: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-union-with-enum-defaults.formatted-txt"), - }, { - name: "different union enumeration types with default enum values (wrapper union)", - inFiles: []string{filepath.Join(datapath, "", "enum-union-with-enum-defaults.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - AppendEnumSuffixForSimpleUnionEnums: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantErrSubstring: "default value not supported for wrapper union values, please generate using simplified union leaves", - }, { - name: "enumeration behaviour - resolution across submodules and grouping re-use within union", - inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.formatted-txt"), - }, { - name: "enumeration behaviour (wrapper unions) - resolution across submodules and grouping re-use within union", - inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateLeafGetters: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.wrapper-unions.formatted-txt"), - }, { - name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with enumeration leaf names not shortened", - inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.long-enum-names.formatted-txt"), - }, { - name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with typedef enum names being prefixed by the module of their use/residence rather than of their definition", - inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.residing-module-typedef-enum-name.formatted-txt"), - }, { - name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with typedef enum names being prefixed by the module of their use/residence rather than of their definition, and enumeration leaf names not shortened", - inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.long-enum-names.residing-module-typedef-enum-name.formatted-txt"), - }, { - name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with typedef enum names being prefixed by the module of their use/residence rather than of their definition, and enumeration leaf names not shortened", - inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.long-enum-names.residing-module-typedef-enum-name.wrapper-unions.formatted-txt"), - }, { - name: "enumeration behaviour - multiple enumerations within a union", - inFiles: []string{filepath.Join(datapath, "", "enum-multi-module.yang")}, - inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GenerateJSONSchema: true, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-multi-module.formatted-txt"), - }, { - name: "module with leaf getters", - inFiles: []string{filepath.Join(datapath, "", "openconfig-list-enum-key.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - CompressBehaviour: genutil.PreferIntendedConfig, - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - GenerateSimpleUnions: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-list-enum-key.leaf-getters.formatted-txt"), - }, { - name: "uncompressed module with two different enums", - inFiles: []string{filepath.Join(datapath, "", "enum-list-uncompressed.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-list-uncompressed.formatted-txt"), - }, { - name: "uncompressed module with two different enums (wrapper unions)", - inFiles: []string{filepath.Join(datapath, "", "enum-list-uncompressed.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-list-uncompressed.wrapper-unions.formatted-txt"), - }, { - name: "with model data", - inFiles: []string{filepath.Join(datapath, "", "openconfig-versioned-mod.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - CompressBehaviour: genutil.PreferIntendedConfig, - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - IncludeModelData: true, - GenerateSimpleUnions: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-versioned-mod.formatted-txt"), - }, { - name: "model with deduplicated enums", - inFiles: []string{filepath.Join(datapath, "enum-duplication.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-duplication-dedup.formatted-txt"), - }, { - name: "model with enums that are in the same grouping duplicated", - inFiles: []string{filepath.Join(datapath, "enum-duplication.yang")}, - inConfig: GeneratorConfig{ - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, - }, - ParseOptions: ParseOpts{ - SkipEnumDeduplication: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-duplication-dup.formatted-txt"), - }, { - name: "OpenConfig schema test - list with binary key", - inFiles: []string{filepath.Join(datapath, "openconfig-binary-list.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, - }, - }, - wantErrSubstring: "has a binary key", - }, { - name: "OpenConfig schema test - multi-keyed list with binary key", - inFiles: []string{filepath.Join(datapath, "openconfig-binary-multi-list.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, - }, - }, - wantErrSubstring: "has a binary key", - }, { - name: "OpenConfig schema test - list with union key containing binary", - inFiles: []string{filepath.Join(datapath, "openconfig-union-binary-list.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, - }, - }, - wantErrSubstring: "has a union key containing a binary", - }, { - name: "module with presence containers", - inFiles: []string{filepath.Join(datapath, "presence-container-example.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - FakeRootName: "device", - EnumerationsUseUnderscores: true, - }, - GoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - AddYangPresence: true, - }, - }, - wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/presence-container-example.formatted-txt"), - }} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - genCode := func() (*GeneratedGoCode, string, map[string]interface{}, error) { - // Set defaults within the supplied configuration for these tests. - if tt.inConfig.Caller == "" { - // Set the name of the caller explicitly to avoid issues when - // the unit tests are called by external test entities. - tt.inConfig.Caller = "codegen-tests" - } - tt.inConfig.StoreRawSchema = true - tt.inConfig.ParseOptions.ExcludeModules = tt.inExcludeModules - - cg := NewYANGCodeGenerator(&tt.inConfig) - - gotGeneratedCode, errs := cg.GenerateGoCode(tt.inFiles, tt.inIncludePaths) - var err error - if len(errs) > 0 { - err = fmt.Errorf("%w", errs) - } - if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" { - t.Fatalf("%s: cg.GenerateCode(%v, %v): Config: %+v, Did not get expected error: %s", tt.name, tt.inFiles, tt.inIncludePaths, tt.inConfig, diff) - } - if err != nil { - return nil, "", nil, err - } - - // Write all the received structs into a single file such that - // it can be compared to the received file. - var gotCode bytes.Buffer - fmt.Fprint(&gotCode, gotGeneratedCode.CommonHeader) - fmt.Fprint(&gotCode, gotGeneratedCode.OneOffHeader) - for _, gotStruct := range gotGeneratedCode.Structs { - fmt.Fprint(&gotCode, gotStruct.String()) - } - - for _, gotEnum := range gotGeneratedCode.Enums { - fmt.Fprint(&gotCode, gotEnum) - } - - // Write generated enumeration map out. - fmt.Fprint(&gotCode, gotGeneratedCode.EnumMap) - - var gotJSON map[string]interface{} - if tt.inConfig.GenerateJSONSchema { - // Write the schema byte array out. - fmt.Fprint(&gotCode, gotGeneratedCode.JSONSchemaCode) - fmt.Fprint(&gotCode, gotGeneratedCode.EnumTypeMap) - - if err := json.Unmarshal(gotGeneratedCode.RawJSONSchema, &gotJSON); err != nil { - t.Fatalf("%s: json.Unmarshal(..., %v), could not unmarshal received JSON: %v", tt.name, gotGeneratedCode.RawJSONSchema, err) - } - } - return gotGeneratedCode, gotCode.String(), gotJSON, nil - } - - gotGeneratedCode, gotCode, gotJSON, err := genCode() - if err != nil { - return - } - - if tt.wantSchemaFile != "" { - wantSchema, rferr := ioutil.ReadFile(tt.wantSchemaFile) - if rferr != nil { - t.Fatalf("%s: ioutil.ReadFile(%q) error: %v", tt.name, tt.wantSchemaFile, rferr) - } - - var wantJSON map[string]interface{} - if err := json.Unmarshal(wantSchema, &wantJSON); err != nil { - t.Fatalf("%s: json.Unmarshal(..., [contents of %s]), could not unmarshal golden JSON file: %v", tt.name, tt.wantSchemaFile, err) - } - - if !cmp.Equal(gotJSON, wantJSON) { - diff, _ := testutil.GenerateUnifiedDiff(string(wantSchema), string(gotGeneratedCode.RawJSONSchema)) - t.Fatalf("%s: GenerateGoCode(%v, %v), Config: %+v, did not return correct JSON (file: %v), diff: \n%s", tt.name, tt.inFiles, tt.inIncludePaths, tt.inConfig, tt.wantSchemaFile, diff) - } - } - - wantCodeBytes, rferr := ioutil.ReadFile(tt.wantStructsCodeFile) - if rferr != nil { - t.Fatalf("%s: ioutil.ReadFile(%q) error: %v", tt.name, tt.wantStructsCodeFile, rferr) - } - - wantCode := string(wantCodeBytes) - - if gotCode != wantCode { - // Use difflib to generate a unified diff between the - // two code snippets such that this is simpler to debug - // in the test output. - diff, _ := testutil.GenerateUnifiedDiff(wantCode, gotCode) - t.Errorf("%s: GenerateGoCode(%v, %v), Config: %+v, did not return correct code (file: %v), diff:\n%s", - tt.name, tt.inFiles, tt.inIncludePaths, tt.inConfig, tt.wantStructsCodeFile, diff) - } - - for i := 0; i < deflakeRuns; i++ { - _, gotAttempt, _, _ := genCode() - if gotAttempt != gotCode { - diff, _ := testutil.GenerateUnifiedDiff(gotAttempt, gotCode) - t.Fatalf("flaky code generation, diff:\n%s", diff) - } - } - }) - } -} - func TestGenerateErrs(t *testing.T) { tests := []struct { name string @@ -1382,16 +438,19 @@ func TestGenerateErrs(t *testing.T) { for _, tt := range tests { cg := NewYANGCodeGenerator(&tt.inConfig) - _, goErr := cg.GenerateGoCode(tt.inFiles, tt.inPath) - switch { - case tt.wantGoOK && goErr != nil: - t.Errorf("%s: cg.GenerateGoCode(%v, %v): got unexpected error, got: %v, want: nil", tt.name, tt.inFiles, tt.inPath, goErr) - case tt.wantGoOK: - default: - if diff := errdiff.Substring(goErr, tt.wantGoErrSubstring); diff != "" { - t.Errorf("%s: cg.GenerateGoCode(%v, %v): %v", tt.name, tt.inFiles, tt.inPath, diff) + // TODO(wenbli): Move this to integration_tests. + /* + _, goErr := cg.GenerateGoCode(tt.inFiles, tt.inPath) + switch { + case tt.wantGoOK && goErr != nil: + t.Errorf("%s: cg.GenerateGoCode(%v, %v): got unexpected error, got: %v, want: nil", tt.name, tt.inFiles, tt.inPath, goErr) + case tt.wantGoOK: + default: + if diff := errdiff.Substring(goErr, tt.wantGoErrSubstring); diff != "" { + t.Errorf("%s: cg.GenerateGoCode(%v, %v): %v", tt.name, tt.inFiles, tt.inPath, diff) + } } - } + */ if tt.wantSameErrSubstring { tt.wantProtoErrSubstring = tt.wantGoErrSubstring @@ -1990,7 +1049,7 @@ func TestMakeFakeRoot(t *testing.T) { Kind: yang.DirectoryEntry, Dir: map[string]*yang.Entry{}, Node: &yang.Value{ - Name: rootElementNodeName, + Name: igenutil.RootElementNodeName, }, }, }, { @@ -2001,7 +1060,7 @@ func TestMakeFakeRoot(t *testing.T) { Kind: yang.DirectoryEntry, Dir: map[string]*yang.Entry{}, Node: &yang.Value{ - Name: rootElementNodeName, + Name: igenutil.RootElementNodeName, }, }, }} @@ -2012,7 +1071,7 @@ func TestMakeFakeRoot(t *testing.T) { if diff := pretty.Compare(tt.want, got); diff != "" { t.Errorf("(-want +got):\n%s", diff) } - if !IsFakeRoot(got) { + if !igenutil.IsFakeRoot(got) { t.Errorf("IsFakeRoot returned false for entry %v", got) } }) @@ -2055,7 +1114,7 @@ func TestCreateFakeRoot(t *testing.T) { inRootName: "", inCompressPaths: false, wantRoot: &yang.Entry{ - Name: defaultRootName, + Name: igenutil.DefaultRootName, Kind: yang.DirectoryEntry, Dir: map[string]*yang.Entry{ "foo": { @@ -2074,7 +1133,7 @@ func TestCreateFakeRoot(t *testing.T) { }, }, Node: &yang.Value{ - Name: rootElementNodeName, + Name: igenutil.RootElementNodeName, }, }, }, { @@ -2114,7 +1173,7 @@ func TestCreateFakeRoot(t *testing.T) { t.Errorf("%s: createFakeRoot(%v, %v, %s, %v): did not get expected root struct, diff(-got,+want):\n%s", tt.name, tt.inStructs, tt.inRootElems, tt.inRootName, tt.inCompressPaths, diff) } - if !IsFakeRoot(tt.inStructs["/"]) { + if !igenutil.IsFakeRoot(tt.inStructs["/"]) { t.Errorf("IsFakeRoot returned false for entry %v", tt.inStructs["/"]) } } diff --git a/ygen/genir_test.go b/ygen/genir_test.go index 4e5895945..6a5cceb90 100644 --- a/ygen/genir_test.go +++ b/ygen/genir_test.go @@ -22,7 +22,6 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/openconfig/gnmi/errdiff" gpb "github.com/openconfig/gnmi/proto/gnmi" - "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" "github.com/openconfig/ygot/ygot" "google.golang.org/protobuf/testing/protocmp" @@ -923,26 +922,6 @@ func protoIR(nestedDirectories bool) *IR { } } -type goLangMapper struct { - *GoLangMapper -} - -// PopulateFieldFlags populates extra information given a particular -// field of a ParsedDirectory and the corresponding AST node. -func (goLangMapper) PopulateFieldFlags(nd NodeDetails, field *yang.Entry) map[string]string { - if field.Path() == "/openconfig-simple/parent" { - return map[string]string{"foo": "bar"} - } else { - return nil - } -} - -// PopulateEnumFlags populates extra information given a particular -// enumerated type its corresponding AST representation. -func (goLangMapper) PopulateEnumFlags(et EnumeratedYANGType, yangtype *yang.YangType) map[string]string { - return map[string]string{"typename": yangtype.Name} -} - func TestGenerateIR(t *testing.T) { tests := []struct { desc string @@ -954,1804 +933,6 @@ func TestGenerateIR(t *testing.T) { wantIR *IR wantErrSubstring string }{{ - desc: "simple openconfig test with compression", - inYANGFiles: []string{ - filepath.Join(datapath, "openconfig-simple.yang"), - filepath.Join(datapath, "openconfig-simple-augment2.yang"), - }, - inLangMapper: goLangMapper{GoLangMapper: NewGoLangMapper(true)}, - inOpts: IROptions{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - GenerateFakeRoot: true, - }, - AppendEnumSuffixForSimpleUnionEnums: true, - }, - wantIR: &IR{ - Directories: map[string]*ParsedDirectory{ - "/device": { - Name: "Device", - Type: Container, - Path: "/device", - Fields: map[string]*NodeDetails{ - "parent": { - Name: "Parent", - YANGDetails: YANGNodeDetails{ - Name: "parent", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent", - SchemaPath: "/parent", - LeafrefTargetPath: "", - Description: "I am a parent container\nthat has 4 children.", - }, - Type: ContainerNode, - MappedPaths: [][]string{{"parent"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - Flags: map[string]string{"foo": "bar"}, - }, - "remote-container": { - Name: "RemoteContainer", - YANGDetails: YANGNodeDetails{ - Name: "remote-container", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - Path: "/openconfig-simple/remote-container", - SchemaPath: "/remote-container", - LeafrefTargetPath: "", - Description: "", - }, - Type: ContainerNode, - MappedPaths: [][]string{{"remote-container"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - IsFakeRoot: true, - }, - "/openconfig-simple/parent": { - Name: "Parent", - Type: Container, - Path: "/openconfig-simple/parent", - Fields: map[string]*NodeDetails{ - "child": { - Name: "Child", - YANGDetails: YANGNodeDetails{ - Name: "child", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child", - SchemaPath: "/parent/child", - LeafrefTargetPath: "", - Description: "", - }, - Type: ContainerNode, - LangType: nil, - MappedPaths: [][]string{{"child"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - }, - "/openconfig-simple/parent/child": { - Name: "Parent_Child", - Type: Container, - Path: "/openconfig-simple/parent/child", - Fields: map[string]*NodeDetails{ - "zero": { - Name: "Zero", - YANGDetails: YANGNodeDetails{ - Name: "zero", - BelongingModule: "openconfig-simple-augment2", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple-grouping", - Path: "/openconfig-simple/parent/child/state/zero", - SchemaPath: "/parent/child/state/zero", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "string", - ZeroValue: `""`, - }, - MappedPaths: [][]string{{"state", "zero"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple-augment2"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "one": { - Name: "One", - YANGDetails: YANGNodeDetails{ - Name: "one", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/config/one", - SchemaPath: "/parent/child/config/one", - ShadowSchemaPath: "/parent/child/state/one", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"config", "one"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: [][]string{{"state", "one"}}, - ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - }, - "two": { - Name: "Two", - YANGDetails: YANGNodeDetails{ - Name: "two", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state/two", - SchemaPath: "/parent/child/state/two", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"state", "two"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "three": { - Name: "Three", - YANGDetails: YANGNodeDetails{ - Name: "three", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/config/three", - SchemaPath: "/parent/child/config/three", - ShadowSchemaPath: "/parent/child/state/three", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "E_Child_Three", - UnionTypes: nil, - IsEnumeratedValue: true, - ZeroValue: "0", - DefaultValue: nil, - }, - MappedPaths: [][]string{{"config", "three"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: [][]string{{"state", "three"}}, - ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - }, - "four": { - Name: "Four", - YANGDetails: YANGNodeDetails{ - Name: "four", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/config/four", - SchemaPath: "/parent/child/config/four", - ShadowSchemaPath: "/parent/child/state/four", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "Binary", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: "nil", - DefaultValue: nil, - }, - MappedPaths: [][]string{{"config", "four"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: [][]string{{"state", "four"}}, - ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - }, - }, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - }, - "/openconfig-simple/remote-container": { - Name: "RemoteContainer", - Type: Container, - Path: "/openconfig-simple/remote-container", - Fields: map[string]*NodeDetails{ - "a-leaf": { - Name: "ALeaf", - YANGDetails: YANGNodeDetails{ - Name: "a-leaf", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - Path: "/openconfig-simple/remote-container/config/a-leaf", - SchemaPath: "/remote-container/config/a-leaf", - ShadowSchemaPath: "/remote-container/state/a-leaf", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"config", "a-leaf"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: [][]string{{"state", "a-leaf"}}, - ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - }, - }, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - }, - }, - Enums: map[string]*EnumeratedYANGType{ - "/openconfig-simple/parent-config/three": { - Name: "Child_Three", - Kind: SimpleEnumerationType, - TypeName: "enumeration", - ValToYANGDetails: []ygot.EnumDefinition{{ - Name: "ONE", - Value: 0, - }, { - Name: "TWO", - Value: 1, - }}, - Flags: map[string]string{"typename": "enumeration"}, - }, - }, - ModelData: []*gpb.ModelData{{Name: "openconfig-remote"}, {Name: "openconfig-simple"}, {Name: "openconfig-simple-augment2"}, {Name: "openconfig-simple-grouping"}}, - }, - }, { - desc: "simple openconfig test compression prefer state no underscores", - inYANGFiles: []string{ - filepath.Join(datapath, "openconfig-simple.yang"), - filepath.Join(datapath, "openconfig-simple-augment2.yang"), - }, - inLangMapper: NewGoLangMapper(true), - inOpts: IROptions{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferOperationalState, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: false, - GenerateFakeRoot: true, - }, - AppendEnumSuffixForSimpleUnionEnums: true, - }, - wantIR: &IR{ - Directories: map[string]*ParsedDirectory{ - "/device": { - Name: "Device", - Type: Container, - Path: "/device", - Fields: map[string]*NodeDetails{ - "parent": { - Name: "Parent", - YANGDetails: YANGNodeDetails{ - Name: "parent", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent", - SchemaPath: "/parent", - LeafrefTargetPath: "", - Description: "I am a parent container\nthat has 4 children.", - }, - Type: ContainerNode, - MappedPaths: [][]string{{"parent"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "remote-container": { - Name: "RemoteContainer", - YANGDetails: YANGNodeDetails{ - Name: "remote-container", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - Path: "/openconfig-simple/remote-container", - SchemaPath: "/remote-container", - LeafrefTargetPath: "", - Description: "", - }, - Type: ContainerNode, - MappedPaths: [][]string{{"remote-container"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - IsFakeRoot: true, - }, - "/openconfig-simple/parent": { - Name: "Parent", - Type: Container, - Path: "/openconfig-simple/parent", - Fields: map[string]*NodeDetails{ - "child": { - Name: "Child", - YANGDetails: YANGNodeDetails{ - Name: "child", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child", - SchemaPath: "/parent/child", - LeafrefTargetPath: "", - Description: "", - }, - Type: ContainerNode, - LangType: nil, - MappedPaths: [][]string{{"child"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - }, - "/openconfig-simple/parent/child": { - Name: "Parent_Child", - Type: Container, - Path: "/openconfig-simple/parent/child", - Fields: map[string]*NodeDetails{ - "zero": { - Name: "Zero", - YANGDetails: YANGNodeDetails{ - Name: "zero", - BelongingModule: "openconfig-simple-augment2", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple-grouping", - Path: "/openconfig-simple/parent/child/state/zero", - SchemaPath: "/parent/child/state/zero", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{NativeType: "string", ZeroValue: `""`}, - MappedPaths: [][]string{{"state", "zero"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple-augment2"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "one": { - Name: "One", - YANGDetails: YANGNodeDetails{ - Name: "one", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state/one", - SchemaPath: "/parent/child/state/one", - ShadowSchemaPath: "/parent/child/config/one", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"state", "one"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: [][]string{{"config", "one"}}, - ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - }, - "two": { - Name: "Two", - YANGDetails: YANGNodeDetails{ - Name: "two", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state/two", - SchemaPath: "/parent/child/state/two", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"state", "two"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "three": { - Name: "Three", - YANGDetails: YANGNodeDetails{ - Name: "three", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state/three", - SchemaPath: "/parent/child/state/three", - ShadowSchemaPath: "/parent/child/config/three", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "E_ChildThree", - UnionTypes: nil, - IsEnumeratedValue: true, - ZeroValue: "0", - DefaultValue: nil, - }, - MappedPaths: [][]string{{"state", "three"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: [][]string{{"config", "three"}}, - ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - }, - "four": { - Name: "Four", - YANGDetails: YANGNodeDetails{ - Name: "four", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state/four", - SchemaPath: "/parent/child/state/four", - ShadowSchemaPath: "/parent/child/config/four", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "Binary", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: "nil", - DefaultValue: nil, - }, - MappedPaths: [][]string{{"state", "four"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: [][]string{{"config", "four"}}, - ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - }, - }, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - }, - "/openconfig-simple/remote-container": { - Name: "RemoteContainer", - Type: Container, - Path: "/openconfig-simple/remote-container", - Fields: map[string]*NodeDetails{ - "a-leaf": { - Name: "ALeaf", - YANGDetails: YANGNodeDetails{ - Name: "a-leaf", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - Path: "/openconfig-simple/remote-container/state/a-leaf", - SchemaPath: "/remote-container/state/a-leaf", - ShadowSchemaPath: "/remote-container/config/a-leaf", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"state", "a-leaf"}}, - MappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - ShadowMappedPaths: [][]string{{"config", "a-leaf"}}, - ShadowMappedPathModules: [][]string{{"openconfig-simple", "openconfig-simple"}}, - }, - }, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - }, - }, - Enums: map[string]*EnumeratedYANGType{ - "/openconfig-simple/parent-config/three": { - Name: "ChildThree", - Kind: SimpleEnumerationType, - TypeName: "enumeration", - ValToYANGDetails: []ygot.EnumDefinition{{ - Name: "ONE", - Value: 0, - }, { - Name: "TWO", - Value: 1, - }}, - }, - }, - ModelData: []*gpb.ModelData{{Name: "openconfig-remote"}, {Name: "openconfig-simple"}, {Name: "openconfig-simple-augment2"}, {Name: "openconfig-simple-grouping"}}, - }, - }, { - desc: "simple openconfig test without compression", - inYANGFiles: []string{ - filepath.Join(datapath, "openconfig-simple.yang"), - filepath.Join(datapath, "openconfig-simple-augment2.yang"), - }, - inLangMapper: NewGoLangMapper(true), - inOpts: IROptions{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.Uncompressed, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - GenerateFakeRoot: true, - }, - AppendEnumSuffixForSimpleUnionEnums: true, - }, - wantIR: &IR{ - Directories: map[string]*ParsedDirectory{ - "/device": { - Name: "Device", - Type: Container, - Path: "/device", - Fields: map[string]*NodeDetails{ - "parent": { - Name: "Parent", - YANGDetails: YANGNodeDetails{ - Name: "parent", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent", - SchemaPath: "/parent", - LeafrefTargetPath: "", - Description: "I am a parent container\nthat has 4 children.", - }, - Type: ContainerNode, - MappedPaths: [][]string{{"parent"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "remote-container": { - Name: "RemoteContainer", - YANGDetails: YANGNodeDetails{ - Name: "remote-container", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - Path: "/openconfig-simple/remote-container", - SchemaPath: "/remote-container", - LeafrefTargetPath: "", - Description: "", - }, - Type: ContainerNode, - MappedPaths: [][]string{{"remote-container"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - IsFakeRoot: true, - }, - "/openconfig-simple/parent": { - Name: "OpenconfigSimple_Parent", - Type: Container, - Path: "/openconfig-simple/parent", - Fields: map[string]*NodeDetails{ - "child": { - Name: "Child", - YANGDetails: YANGNodeDetails{ - Name: "child", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child", - SchemaPath: "/parent/child", - LeafrefTargetPath: "", - Description: "", - }, - Type: ContainerNode, - LangType: nil, - MappedPaths: [][]string{{"child"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - }, - "/openconfig-simple/parent/child": { - Name: "OpenconfigSimple_Parent_Child", - Type: Container, - Path: "/openconfig-simple/parent/child", - Fields: map[string]*NodeDetails{ - "config": { - Name: "Config", - YANGDetails: YANGNodeDetails{ - Name: "config", - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/config", - SchemaPath: "/parent/child/config", - LeafrefTargetPath: "", - Description: "", - }, - Type: 1, - MappedPaths: [][]string{{"config"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - }, - "state": { - Name: "State", - YANGDetails: YANGNodeDetails{ - Name: "state", - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state", - SchemaPath: "/parent/child/state", - LeafrefTargetPath: "", - Description: "", - }, - Type: 1, - MappedPaths: [][]string{{"state"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - }, - }, - ListKeys: nil, - PackageName: "", - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - }, - "/openconfig-simple/parent/child/config": { - Name: "OpenconfigSimple_Parent_Child_Config", - Type: Container, - Path: "/openconfig-simple/parent/child/config", - Fields: map[string]*NodeDetails{ - "four": { - Name: "Four", - YANGDetails: YANGNodeDetails{ - Name: "four", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/config/four", - SchemaPath: "/parent/child/config/four", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "Binary", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: "nil", - DefaultValue: nil, - }, - MappedPaths: [][]string{{"four"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "one": { - Name: "One", - YANGDetails: YANGNodeDetails{ - Name: "one", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/config/one", - SchemaPath: "/parent/child/config/one", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"one"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "three": { - Name: "Three", - YANGDetails: YANGNodeDetails{ - Name: "three", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/config/three", - SchemaPath: "/parent/child/config/three", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "E_Simple_Parent_Child_Config_Three", - UnionTypes: nil, - IsEnumeratedValue: true, - ZeroValue: "0", - DefaultValue: nil, - }, - MappedPaths: [][]string{{"three"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - ListKeys: nil, - PackageName: "", - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - }, - "/openconfig-simple/parent/child/state": { - Name: "OpenconfigSimple_Parent_Child_State", - Type: Container, - Path: "/openconfig-simple/parent/child/state", - Fields: map[string]*NodeDetails{ - "four": { - Name: "Four", - YANGDetails: YANGNodeDetails{ - Name: "four", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state/four", - SchemaPath: "/parent/child/state/four", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "Binary", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: "nil", - DefaultValue: nil, - }, - MappedPaths: [][]string{{"four"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "one": { - Name: "One", - YANGDetails: YANGNodeDetails{ - Name: "one", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state/one", - SchemaPath: "/parent/child/state/one", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"one"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "three": { - Name: "Three", - YANGDetails: YANGNodeDetails{ - Name: "three", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state/three", - SchemaPath: "/parent/child/state/three", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "E_Simple_Parent_Child_Config_Three", - UnionTypes: nil, - IsEnumeratedValue: true, - ZeroValue: "0", - DefaultValue: nil, - }, - MappedPaths: [][]string{{"three"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "two": { - Name: "Two", - YANGDetails: YANGNodeDetails{ - Name: "two", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - Path: "/openconfig-simple/parent/child/state/two", - SchemaPath: "/parent/child/state/two", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"two"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "zero": { - Name: "Zero", - YANGDetails: YANGNodeDetails{ - Name: "zero", - Defaults: nil, - BelongingModule: "openconfig-simple-augment2", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple-grouping", - Path: "/openconfig-simple/parent/child/state/zero", - SchemaPath: "/parent/child/state/zero", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"zero"}}, - MappedPathModules: [][]string{{"openconfig-simple-augment2"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - ListKeys: nil, - PackageName: "", - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-simple", - ConfigFalse: true, - }, - "/openconfig-simple/remote-container": { - Name: "OpenconfigSimple_RemoteContainer", - Type: Container, - Path: "/openconfig-simple/remote-container", - Fields: map[string]*NodeDetails{ - "config": { - Name: "Config", - YANGDetails: YANGNodeDetails{ - Name: "config", - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - Path: "/openconfig-simple/remote-container/config", - SchemaPath: "/remote-container/config", - LeafrefTargetPath: "", - Description: "", - }, - Type: 1, - MappedPaths: [][]string{{"config"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - }, - "state": { - Name: "State", - YANGDetails: YANGNodeDetails{ - Name: "state", - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - Path: "/openconfig-simple/remote-container/state", - SchemaPath: "/remote-container/state", - LeafrefTargetPath: "", - Description: "", - }, - Type: 1, - MappedPaths: [][]string{{"state"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - }, - }, - ListKeys: nil, - PackageName: "", - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - }, - "/openconfig-simple/remote-container/config": { - Name: "OpenconfigSimple_RemoteContainer_Config", - Type: Container, - Path: "/openconfig-simple/remote-container/config", - Fields: map[string]*NodeDetails{ - "a-leaf": { - Name: "ALeaf", - YANGDetails: YANGNodeDetails{ - Name: "a-leaf", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - Path: "/openconfig-simple/remote-container/config/a-leaf", - SchemaPath: "/remote-container/config/a-leaf", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"a-leaf"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - }, - "/openconfig-simple/remote-container/state": { - Name: "OpenconfigSimple_RemoteContainer_State", - Type: Container, - Path: "/openconfig-simple/remote-container/state", - Fields: map[string]*NodeDetails{ - "a-leaf": { - Name: "ALeaf", - YANGDetails: YANGNodeDetails{ - Name: "a-leaf", - Defaults: nil, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - Path: "/openconfig-simple/remote-container/state/a-leaf", - SchemaPath: "/remote-container/state/a-leaf", - LeafrefTargetPath: "", - Description: "", - }, - Type: 3, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"a-leaf"}}, - MappedPathModules: [][]string{{"openconfig-simple"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - BelongingModule: "openconfig-simple", - RootElementModule: "openconfig-simple", - DefiningModule: "openconfig-remote", - ConfigFalse: true, - }, - }, - Enums: map[string]*EnumeratedYANGType{ - "/openconfig-simple/parent-config/three": { - Name: "Simple_Parent_Child_Config_Three", - Kind: 1, - TypeName: "enumeration", - ValToYANGDetails: []ygot.EnumDefinition{{ - Name: "ONE", - Value: 0, - }, { - Name: "TWO", - Value: 1, - }}, - }, - }, - ModelData: []*gpb.ModelData{{Name: "openconfig-remote"}, {Name: "openconfig-simple"}, {Name: "openconfig-simple-augment2"}, {Name: "openconfig-simple-grouping"}}, - }, - }, { - desc: "exclude module test with compression", - inYANGFiles: []string{filepath.Join(datapath, "excluded-module-noimport.yang")}, - inExcludeModules: []string{"excluded-module-two"}, - inLangMapper: NewGoLangMapper(true), - inOpts: IROptions{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - GenerateFakeRoot: true, - }, - AppendEnumSuffixForSimpleUnionEnums: true, - }, - wantIR: &IR{ - Directories: map[string]*ParsedDirectory{ - "/device": { - Name: "Device", - Type: Container, - Path: "/device", - Fields: map[string]*NodeDetails{ - "e1": { - Name: "E1", - YANGDetails: YANGNodeDetails{ - Name: "e1", - Defaults: nil, - BelongingModule: "excluded-module-noimport", - RootElementModule: "excluded-module-noimport", - DefiningModule: "excluded-module-noimport", - Path: "/excluded-module-noimport/e1", - SchemaPath: "/e1", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "string", - UnionTypes: nil, - IsEnumeratedValue: false, - ZeroValue: `""`, - DefaultValue: nil, - }, - MappedPaths: [][]string{{"e1"}}, - MappedPathModules: [][]string{{"excluded-module-noimport"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - IsFakeRoot: true, - }, - }, - // TODO(wenbli): Determine whether "excluded-module-two" should be here. - ModelData: []*gpb.ModelData{{Name: "excluded-module-noimport"}, {Name: "excluded-module-two"}}, - }, - }, { - desc: "complex openconfig test with compression", - inYANGFiles: []string{filepath.Join(datapath, "openconfig-complex.yang")}, - inLangMapper: NewGoLangMapper(true), - inOpts: IROptions{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, - GenerateFakeRoot: true, - }, - AppendEnumSuffixForSimpleUnionEnums: true, - }, - wantIR: &IR{ - Directories: map[string]*ParsedDirectory{ - "/device": { - Name: "Device", - Type: Container, - Path: "/device", - Fields: map[string]*NodeDetails{ - "model": { - Name: "Model", - YANGDetails: YANGNodeDetails{ - Name: "model", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model", - SchemaPath: "/model", - LeafrefTargetPath: "", - Description: "", - }, - Type: ContainerNode, - LangType: nil, - MappedPaths: [][]string{{"model"}}, - MappedPathModules: [][]string{{"openconfig-complex"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "example-presence": { - Name: "ExamplePresence", - YANGDetails: YANGNodeDetails{ - Name: "example-presence", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/example-presence", - SchemaPath: "/example-presence", - LeafrefTargetPath: "", - PresenceStatement: ygot.String("This is an example presence container"), - Description: "", - }, - Type: ContainerNode, - LangType: nil, - MappedPaths: [][]string{{"example-presence"}}, - MappedPathModules: [][]string{{"openconfig-complex"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - }, - IsFakeRoot: true, - }, - "/openconfig-complex/example-presence": { - Name: "ExamplePresence", - Type: Container, - Path: "/openconfig-complex/example-presence", - Fields: map[string]*NodeDetails{}, - PackageName: "", - ListKeys: nil, - IsFakeRoot: false, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - }, - "/openconfig-complex/model": { - Name: "Model", - Type: Container, - Path: "/openconfig-complex/model", - Fields: map[string]*NodeDetails{ - "anydata-leaf": { - Name: "AnydataLeaf", - YANGDetails: YANGNodeDetails{ - Name: "anydata-leaf", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/anydata-leaf", - SchemaPath: "/model/anydata-leaf", - LeafrefTargetPath: "", - Description: "some anydata leaf", - }, - Type: AnyDataNode, - LangType: nil, - MappedPaths: [][]string{{"anydata-leaf"}}, - MappedPathModules: [][]string{{"openconfig-complex"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "dateref": { - Name: "Dateref", - YANGDetails: YANGNodeDetails{ - Name: "dateref", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/dateref", - SchemaPath: "/model/dateref", - LeafrefTargetPath: "/openconfig-complex/model/a/single-key/config/dates", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "uint8", - ZeroValue: "0", - DefaultValue: ygot.String("5"), - }, - MappedPaths: [][]string{{"dateref"}}, - MappedPathModules: [][]string{{"openconfig-complex"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "multi-key": { - Name: "MultiKey", - YANGDetails: YANGNodeDetails{ - Name: "multi-key", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/b/multi-key", - SchemaPath: "/model/b/multi-key", - LeafrefTargetPath: "", - Description: "", - }, - Type: ListNode, - LangType: nil, - MappedPaths: [][]string{{"b", "multi-key"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "single-key": { - Name: "SingleKey", - YANGDetails: YANGNodeDetails{ - Name: "single-key", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key", - SchemaPath: "/model/a/single-key", - LeafrefTargetPath: "", - Description: "", - }, - Type: ListNode, - LangType: nil, - MappedPaths: [][]string{{"a", "single-key"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: nil, - ShadowMappedPathModules: nil, - }, - "unkeyed-list": { - Name: "UnkeyedList", - YANGDetails: YANGNodeDetails{ - Name: "unkeyed-list", - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/c/unkeyed-list", - SchemaPath: "/model/c/unkeyed-list", - }, - Type: ListNode, - MappedPaths: [][]string{{"c", "unkeyed-list"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - }, - }, - ListKeys: nil, - PackageName: "", - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - }, - "/openconfig-complex/model/a/single-key": { - Name: "Model_SingleKey", - Type: List, - Path: "/openconfig-complex/model/a/single-key", - Fields: map[string]*NodeDetails{ - "dates": { - Name: "Dates", - YANGDetails: YANGNodeDetails{ - Name: "dates", - Defaults: []string{"5"}, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key/config/dates", - SchemaPath: "/model/a/single-key/config/dates", - ShadowSchemaPath: "/model/a/single-key/state/dates", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafListNode, - LangType: &MappedType{NativeType: "uint8", ZeroValue: "0", DefaultValue: ygot.String("[]uint8{5}")}, - MappedPaths: [][]string{{"config", "dates"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "dates"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - }, - "dates-with-defaults": { - Name: "DatesWithDefaults", - YANGDetails: YANGNodeDetails{ - Name: "dates-with-defaults", - Defaults: []string{"1", "2"}, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key/config/dates-with-defaults", - SchemaPath: "/model/a/single-key/config/dates-with-defaults", - ShadowSchemaPath: "/model/a/single-key/state/dates-with-defaults", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafListNode, - LangType: &MappedType{NativeType: "uint8", ZeroValue: "0", DefaultValue: ygot.String("[]uint8{1, 2}")}, - MappedPaths: [][]string{{"config", "dates-with-defaults"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "dates-with-defaults"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - }, - "iref": { - Name: "Iref", - YANGDetails: YANGNodeDetails{ - Name: "iref", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key/config/iref", - SchemaPath: "/model/a/single-key/config/iref", - ShadowSchemaPath: "/model/a/single-key/state/iref", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "E_Complex_SOFTWARE", - IsEnumeratedValue: true, - ZeroValue: "0", - }, - MappedPaths: [][]string{{"config", "iref"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "iref"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - }, - "key": { - Name: "Key", - YANGDetails: YANGNodeDetails{ - Name: "key", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key/config/key", - SchemaPath: "/model/a/single-key/config/key", - ShadowSchemaPath: "/model/a/single-key/state/key", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "Model_SingleKey_Key_Union", - UnionTypes: map[string]int{ - "E_Complex_WeekendDays": 1, - "uint8": 0, - }, - ZeroValue: "nil", - }, - MappedPaths: [][]string{{"config", "key"}, {"key"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "key"}, {"key"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, - }, - "leaf-default-override": { - Name: "LeafDefaultOverride", - YANGDetails: YANGNodeDetails{ - Name: "leaf-default-override", - Defaults: []string{"3"}, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key/config/leaf-default-override", - SchemaPath: "/model/a/single-key/config/leaf-default-override", - ShadowSchemaPath: "/model/a/single-key/state/leaf-default-override", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "Model_SingleKey_LeafDefaultOverride_Union", - UnionTypes: map[string]int{ - "E_Complex_CycloneScales_Enum": 1, - "uint8": 0, - }, - ZeroValue: "nil", - DefaultValue: ygot.String("UnionUint8(3)"), - }, - MappedPaths: [][]string{{"config", "leaf-default-override"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "leaf-default-override"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - }, - "simple-union-enum": { - Name: "SimpleUnionEnum", - YANGDetails: YANGNodeDetails{ - Name: "simple-union-enum", - Defaults: []string{"TWO"}, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key/config/simple-union-enum", - SchemaPath: "/model/a/single-key/config/simple-union-enum", - ShadowSchemaPath: "/model/a/single-key/state/simple-union-enum", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "Model_SingleKey_SimpleUnionEnum_Union", - UnionTypes: map[string]int{ - "E_SingleKey_SimpleUnionEnum_Enum": 1, - "uint64": 0, - }, - ZeroValue: "nil", - DefaultValue: ygot.String("SingleKey_SimpleUnionEnum_Enum_TWO"), - }, - MappedPaths: [][]string{{"config", "simple-union-enum"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "simple-union-enum"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - }, - "singleton-union-enum": { - Name: "SingletonUnionEnum", - YANGDetails: YANGNodeDetails{ - Name: "singleton-union-enum", - Defaults: []string{"DEUX"}, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key/config/singleton-union-enum", - SchemaPath: "/model/a/single-key/config/singleton-union-enum", - ShadowSchemaPath: "/model/a/single-key/state/singleton-union-enum", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "E_SingleKey_SingletonUnionEnum_Enum", - UnionTypes: map[string]int{ - "E_SingleKey_SingletonUnionEnum_Enum": 0, - }, - IsEnumeratedValue: true, - ZeroValue: "0", - DefaultValue: ygot.String("SingleKey_SingletonUnionEnum_Enum_DEUX"), - }, - MappedPaths: [][]string{{"config", "singleton-union-enum"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "singleton-union-enum"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - }, - "typedef-enum": { - Name: "TypedefEnum", - YANGDetails: YANGNodeDetails{ - Name: "typedef-enum", - Defaults: []string{"SATURDAY"}, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key/config/typedef-enum", - SchemaPath: "/model/a/single-key/config/typedef-enum", - ShadowSchemaPath: "/model/a/single-key/state/typedef-enum", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "E_Complex_WeekendDays", - IsEnumeratedValue: true, - ZeroValue: "0", - DefaultValue: ygot.String("Complex_WeekendDays_SATURDAY"), - }, - MappedPaths: [][]string{{"config", "typedef-enum"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "typedef-enum"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - }, - "typedef-union-enum": { - Name: "TypedefUnionEnum", - YANGDetails: YANGNodeDetails{ - Name: "typedef-union-enum", - Defaults: []string{"SUPER"}, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/a/single-key/config/typedef-union-enum", - SchemaPath: "/model/a/single-key/config/typedef-union-enum", - ShadowSchemaPath: "/model/a/single-key/state/typedef-union-enum", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "Model_SingleKey_TypedefUnionEnum_Union", - UnionTypes: map[string]int{ - "E_Complex_CycloneScales_Enum": 1, - "uint8": 0, - }, - ZeroValue: "nil", - DefaultValue: ygot.String("Complex_CycloneScales_Enum_SUPER"), - }, - MappedPaths: [][]string{{"config", "typedef-union-enum"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "typedef-union-enum"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, - }, - }, - ListKeys: map[string]*ListKey{ - "key": { - Name: "Key", - LangType: &MappedType{ - NativeType: "Model_SingleKey_Key_Union", - UnionTypes: map[string]int{ - "E_Complex_WeekendDays": 1, - "uint8": 0, - }, - ZeroValue: "nil", - }, - }, - }, - ListKeyYANGNames: []string{"key"}, - PackageName: "", - IsFakeRoot: false, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - }, - "/openconfig-complex/model/b/multi-key": { - Name: "Model_MultiKey", - Type: List, - Path: "/openconfig-complex/model/b/multi-key", - Fields: map[string]*NodeDetails{ - "key1": { - Name: "Key1", - YANGDetails: YANGNodeDetails{ - Name: "key1", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/b/multi-key/config/key1", - SchemaPath: "/model/b/multi-key/config/key1", - ShadowSchemaPath: "/model/b/multi-key/state/key1", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{ - NativeType: "uint32", - UnionTypes: map[string]int{"uint32": 0}, - ZeroValue: "0", - }, - MappedPaths: [][]string{{"config", "key1"}, {"key1"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "key1"}, {"key1"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, - }, - "key2": { - Name: "Key2", - YANGDetails: YANGNodeDetails{ - Name: "key2", - Defaults: nil, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/b/multi-key/config/key2", - SchemaPath: "/model/b/multi-key/config/key2", - ShadowSchemaPath: "/model/b/multi-key/state/key2", - LeafrefTargetPath: "", - Description: "", - }, - Type: LeafNode, - LangType: &MappedType{NativeType: "E_MultiKey_Key2", IsEnumeratedValue: true, ZeroValue: "0"}, - MappedPaths: [][]string{{"config", "key2"}, {"key2"}}, - MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, - ShadowMappedPaths: [][]string{{"state", "key2"}, {"key2"}}, - ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}, {"openconfig-complex"}}, - }, - }, - ListKeys: map[string]*ListKey{ - "key1": { - Name: "Key1", - LangType: &MappedType{ - NativeType: "uint32", - UnionTypes: map[string]int{"uint32": 0}, - ZeroValue: "0", - }, - }, - "key2": { - Name: "Key2", - LangType: &MappedType{NativeType: "E_MultiKey_Key2", IsEnumeratedValue: true, ZeroValue: "0"}, - }, - }, - ListKeyYANGNames: []string{"key1", "key2"}, - PackageName: "", - IsFakeRoot: false, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - }, - "/openconfig-complex/model/c/unkeyed-list": { - Name: "Model_UnkeyedList", - Type: List, - Path: "/openconfig-complex/model/c/unkeyed-list", - Fields: map[string]*NodeDetails{ - "field": { - Name: "Field", - YANGDetails: YANGNodeDetails{ - Name: "field", - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - Path: "/openconfig-complex/model/c/unkeyed-list/field", - SchemaPath: "/model/c/unkeyed-list/field", - }, - Type: LeafNode, - LangType: &MappedType{NativeType: "Binary", ZeroValue: "nil"}, - MappedPaths: [][]string{{"field"}}, - MappedPathModules: [][]string{{"openconfig-complex"}}, - }, - }, - BelongingModule: "openconfig-complex", - RootElementModule: "openconfig-complex", - DefiningModule: "openconfig-complex", - ConfigFalse: true, - }, - }, - Enums: map[string]*EnumeratedYANGType{ - "/openconfig-complex/cyclone-scales": { - Name: "Complex_CycloneScales_Enum", - Kind: DerivedUnionEnumerationType, - TypeName: "cyclone-scales", - ValToYANGDetails: []ygot.EnumDefinition{ - { - Name: "NORMAL", - DefiningModule: "", - Value: 0, - }, - { - Name: "SUPER", - DefiningModule: "", - Value: 1, - }, - }, - }, - "/openconfig-complex/SOFTWARE": { - Name: "Complex_SOFTWARE", - Kind: IdentityType, - IdentityBaseName: "SOFTWARE", - TypeName: "identityref", - ValToYANGDetails: []ygot.EnumDefinition{ - {Name: "OS", DefiningModule: "openconfig-complex"}, - }, - }, - "/openconfig-complex/multi-key-config/key2": { - Name: "MultiKey_Key2", - Kind: SimpleEnumerationType, - TypeName: "enumeration", - ValToYANGDetails: []ygot.EnumDefinition{ - { - Name: "RED", - DefiningModule: "", - Value: 0, - }, - { - Name: "BLUE", - DefiningModule: "", - Value: 1, - }, - }, - }, - "/openconfig-complex/weekend-days": { - Name: "Complex_WeekendDays", - Kind: DerivedEnumerationType, - TypeName: "weekend-days", - ValToYANGDetails: []ygot.EnumDefinition{ - { - Name: "SATURDAY", - DefiningModule: "", - Value: 0, - }, - { - Name: "SUNDAY", - DefiningModule: "", - Value: 1, - }, - }, - }, - "/openconfig-complex/single-key-config/simple-union-enum": { - Name: "SingleKey_SimpleUnionEnum_Enum", - Kind: UnionEnumerationType, - TypeName: "union", - ValToYANGDetails: []ygot.EnumDefinition{ - { - Name: "ONE", - DefiningModule: "", - Value: 0, - }, - { - Name: "TWO", - DefiningModule: "", - Value: 1, - }, - { - Name: "THREE", - DefiningModule: "", - Value: 2, - }, - }, - }, - "/openconfig-complex/single-key-config/singleton-union-enum": { - Name: "SingleKey_SingletonUnionEnum_Enum", - Kind: UnionEnumerationType, - TypeName: "union", - ValToYANGDetails: []ygot.EnumDefinition{ - { - Name: "UN", - DefiningModule: "", - Value: 0, - }, - { - Name: "DEUX", - DefiningModule: "", - Value: 1, - }, - { - Name: "TROIS", - DefiningModule: "", - Value: 2, - }, - }, - }, - }, - ModelData: []*gpb.ModelData{{Name: "openconfig-complex"}}, - }, - }, { desc: "complex openconfig test with compression using ProtoLangMapper with nested directories", inYANGFiles: []string{filepath.Join(datapath, "openconfig-complex.yang")}, inLangMapper: func() LangMapper { diff --git a/ygen/genstate.go b/ygen/genstate.go index 4057ab269..da3a4a1cb 100644 --- a/ygen/genstate.go +++ b/ygen/genstate.go @@ -21,6 +21,7 @@ import ( "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/internal/igenutil" "github.com/openconfig/ygot/util" "github.com/openconfig/ygot/ygot" ) @@ -172,7 +173,7 @@ func buildDirectoryDefinitions(langMapper LangMapper, entries map[string]*yang.E elem.Path = strings.Split(util.SchemaTreePath(e), "/") // Mark this struct as the fake root if it is specified to be. - if IsFakeRoot(e) { + if igenutil.IsFakeRoot(e) { elem.IsFakeRoot = true } diff --git a/ygen/genstate_test.go b/ygen/genstate_test.go index a6f2fdd91..2610ef7f5 100644 --- a/ygen/genstate_test.go +++ b/ygen/genstate_test.go @@ -15,13 +15,12 @@ package ygen import ( - "fmt" "testing" "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/internal/igenutil" ) func TestOrderedUnionTypes(t *testing.T) { @@ -85,6 +84,7 @@ func TestOrderedUnionTypes(t *testing.T) { } } +/* func TestBuildDirectoryDefinitions(t *testing.T) { tests := []struct { name string @@ -1480,6 +1480,7 @@ func TestBuildDirectoryDefinitions(t *testing.T) { } } } +*/ // enumMapFromEntries recursively finds enumerated values from a slice of // entries and returns an enumMap. The input enumMap is intended for @@ -1519,7 +1520,7 @@ func addEnumsToEnumMap(entry *yang.Entry, enumMap map[string]*yang.Entry) { if entry == nil { return } - if e := mappableLeaf(entry); e != nil { + if e := igenutil.MappableLeaf(entry); e != nil { enumMap[entry.Path()] = e } for _, e := range entry.Dir { @@ -1527,6 +1528,53 @@ func addEnumsToEnumMap(entry *yang.Entry, enumMap map[string]*yang.Entry) { } } +type BuildListKeyMapper struct { + // LangMapperBase being embedded is a requirement for GoLangMapper to + // implement the LangMapper interface, and also gives it access to + // built-in methods. + LangMapperBase + + // UnimplementedLangMapperExt ensures BuildListKeyMapper implements the + // LangMapperExt interface for forwards compatibility. + UnimplementedLangMapperExt +} + +// DirectoryName generates the final name to be used for a particular YANG +// schema element in the generated Go code. If path compressing is active, +// schemapaths are compressed, otherwise the name is returned simply as camel +// case. +// Although name conversion is lossy, name uniquification occurs at this stage +// since all generated struct names reside in the package namespace. +func (*BuildListKeyMapper) DirectoryName(e *yang.Entry, compressBehaviour genutil.CompressBehaviour) (string, error) { + return "", nil +} + +// FieldName maps the input entry's name to what the Go name of the field would be. +// Since this conversion is lossy, a later step should resolve any naming +// conflicts between different fields. +func (s *BuildListKeyMapper) FieldName(e *yang.Entry) (string, error) { + return genutil.EntryCamelCaseName(e), nil +} + +// LeafType maps the input leaf entry to a MappedType object containing the +// type information about the field. +func (s *BuildListKeyMapper) LeafType(e *yang.Entry, opts IROptions) (*MappedType, error) { + return nil, nil +} + +// LeafType maps the input list key entry to a MappedType object containing the +// type information about the key field. +func (s *BuildListKeyMapper) KeyLeafType(e *yang.Entry, opts IROptions) (*MappedType, error) { + return &MappedType{ + NativeType: e.Type.Kind.String(), + }, nil +} + +// PackageName is not used by Go generation. +func (s *BuildListKeyMapper) PackageName(*yang.Entry, genutil.CompressBehaviour, bool) (string, error) { + return "", nil +} + // TestBuildListKey takes an input yang.Entry and ensures that the correct YangListAttr // struct is returned representing the keys of the list e. func TestBuildListKey(t *testing.T) { @@ -1643,7 +1691,7 @@ func TestBuildListKey(t *testing.T) { "keyleaf": { Name: "Keyleaf", LangType: &MappedType{ - NativeType: "E_Container_Keyleaf", + NativeType: "enumeration", }, }, }, @@ -2078,7 +2126,7 @@ func TestBuildListKey(t *testing.T) { "keyleafref": { Name: "Keyleafref", LangType: &MappedType{ - NativeType: "string", + NativeType: "leafref", }, }, }, @@ -2089,168 +2137,11 @@ func TestBuildListKey(t *testing.T) { }, }, }, - }, { - name: "list enum key -- already seen", - in: &yang.Entry{ - Name: "list", - ListAttr: &yang.ListAttr{}, - Key: "keyleaf", - Dir: map[string]*yang.Entry{ - "keyleaf": { - Name: "keyleaf", - Type: &yang.YangType{ - Name: "enumeration", - Enum: &yang.EnumType{}, - Kind: yang.Yenum, - }, - Node: &yang.Enum{ - Name: "enumeration", - Parent: &yang.Grouping{ - Name: "foo", - Parent: &yang.Module{ - Name: "base-module", - }, - }, - }, - Parent: &yang.Entry{ - Name: "config", - Parent: &yang.Entry{ - Name: "container", - Parent: &yang.Entry{Name: "base-module"}, - }, - }, - }, - }, - }, - inEnumEntries: []*yang.Entry{{ - Name: "enum-leaf-lexicographically-earlier", - Type: &yang.YangType{ - Name: "enumeration", - Enum: &yang.EnumType{}, - Kind: yang.Yenum, - }, - Node: &yang.Enum{ - Name: "enumeration", - Parent: &yang.Grouping{ - Name: "foo", - Parent: &yang.Module{ - Name: "base-module", - }, - }, - }, - Parent: &yang.Entry{ - Name: "config", - Parent: &yang.Entry{ - Name: "container", - Parent: &yang.Entry{Name: "base-module"}, - }, - }, - }}, - inCompress: true, - want: YangListAttr{ - Keys: map[string]*ListKey{ - "keyleaf": { - Name: "Keyleaf", - LangType: &MappedType{ - NativeType: "E_Container_EnumLeafLexicographicallyEarlier", - }, - }, - }, - KeyElems: []*yang.Entry{ - { - Name: "keyleaf", - Type: &yang.YangType{ - Name: "enumeration", - Enum: &yang.EnumType{}, - Kind: yang.Yenum, - }, - }, - }, - }, - }, { - name: "list enum key -- already seen but skip enum dedup", - in: &yang.Entry{ - Name: "list", - ListAttr: &yang.ListAttr{}, - Key: "keyleaf", - Dir: map[string]*yang.Entry{ - "keyleaf": { - Name: "keyleaf", - Type: &yang.YangType{ - Name: "enumeration", - Enum: &yang.EnumType{}, - Kind: yang.Yenum, - }, - Node: &yang.Enum{ - Name: "enumeration", - Parent: &yang.Grouping{ - Name: "foo", - Parent: &yang.Module{ - Name: "base-module", - }, - }, - }, - Parent: &yang.Entry{ - Name: "config", - Parent: &yang.Entry{ - Name: "container", - Parent: &yang.Entry{Name: "base-module"}, - }, - }, - }, - }, - }, - inEnumEntries: []*yang.Entry{{ - Name: "enum-leaf-lexicographically-earlier", - Type: &yang.YangType{ - Name: "enumeration", - Enum: &yang.EnumType{}, - Kind: yang.Yenum, - }, - Node: &yang.Enum{ - Name: "enumeration", - Parent: &yang.Grouping{ - Name: "foo", - Parent: &yang.Module{ - Name: "base-module", - }, - }, - }, - Parent: &yang.Entry{ - Name: "config", - Parent: &yang.Entry{ - Name: "container", - Parent: &yang.Entry{Name: "base-module"}, - }, - }, - }}, - inCompress: true, - inSkipEnumDedup: true, - want: YangListAttr{ - Keys: map[string]*ListKey{ - "keyleaf": { - Name: "Keyleaf", - LangType: &MappedType{ - NativeType: "E_Container_Keyleaf", - }, - }, - }, - KeyElems: []*yang.Entry{ - { - Name: "keyleaf", - Type: &yang.YangType{ - Name: "enumeration", - Enum: &yang.EnumType{}, - Kind: yang.Yenum, - }, - }, - }, - }, }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewGoLangMapper(true) + var s LangMapper = &BuildListKeyMapper{} enumMap := enumMapFromEntries(tt.inEnumEntries) addEnumsToEnumMap(tt.in, enumMap) diff --git a/ygen/helpers.go b/ygen/helpers.go index 39726a6c3..d38be1799 100644 --- a/ygen/helpers.go +++ b/ygen/helpers.go @@ -14,42 +14,6 @@ package ygen -import ( - "fmt" - "strings" -) - -// safeGoEnumeratedValueName takes an input string, which is the name of an -// enumerated value from a YANG schema, and ensures that it is safe to be -// output as part of the name of the enumerated value in the Go code. The -// sanitised value is returned. Per RFC6020 Section 9.6.4, -// "The enum Statement [...] takes as an argument a string which is the -// assigned name. The string MUST NOT be empty and MUST NOT have any -// leading or trailing whitespace characters. The use of Unicode control -// codes SHOULD be avoided." -// Note: this rule is distinct and looser than the rule for YANG identifiers. -// The implementation used here replaces some (not all) characters allowed -// in a YANG enum assigned name but not in Go code. Current support is based -// on real-world feedback e.g. in OpenConfig schemas, there are currently -// a small number of identity values that contain "." and hence -// must be specifically handled. -func safeGoEnumeratedValueName(name string) string { - // NewReplacer takes pairs of strings to be replaced in the form - // old, new. - replacer := strings.NewReplacer( - ".", "_", - "-", "_", - "/", "_", - "+", "_PLUS", - ",", "_COMMA", - "@", "_AT", - "$", "_DOLLAR", - "*", "_ASTERISK", - ":", "_COLON", - " ", "_") - return replacer.Replace(name) -} - // addNewKeys appends entries from the newKeys string slice to the // existing map if the entry is not an existing key. The existing // map is modified in place. @@ -70,26 +34,6 @@ func stringKeys(m map[string]interface{}) []string { return ss } -// enumDefaultValue sanitises a default value specified for an enumeration -// which can be specified as prefix:value in the YANG schema. The baseName -// is used as the generated enumeration name stripping any prefix specified, -// (allowing removal of the enumeration type prefix if required). The default -// value in the form _ is returned as -// a pointer. -func enumDefaultValue(baseName, defVal, prefix string) string { - if strings.Contains(defVal, ":") { - defVal = strings.Split(defVal, ":")[1] - } - - if prefix != "" { - baseName = strings.TrimPrefix(baseName, prefix) - } - - defVal = safeGoEnumeratedValueName(defVal) - - return fmt.Sprintf("%s_%s", baseName, defVal) -} - // resolveRootName resolves the name of the fakeroot by taking configuration // and the default values, along with a boolean indicating whether the fake // root is to be generated. It returns an empty string if the root is not diff --git a/ygen/helpers_test.go b/ygen/helpers_test.go index 15fe47be3..ed5c34393 100644 --- a/ygen/helpers_test.go +++ b/ygen/helpers_test.go @@ -18,35 +18,6 @@ import ( "testing" ) -// TestSafeGoEnumeratedValueName tests the safeGoEnumeratedValue function to ensure -// that enumeraton value names are correctly transformed to safe Go names. -func TestSafeGoEnumeratedValueName(t *testing.T) { - tests := []struct { - in string - want string - }{ - {"SPEED_2.5G", "SPEED_2_5G"}, - {"IPV4-UNICAST", "IPV4_UNICAST"}, - {"frameRelay", "frameRelay"}, - {"coffee", "coffee"}, - {"ethernetCsmacd", "ethernetCsmacd"}, - {"SFP+", "SFP_PLUS"}, - {"LEVEL1/2", "LEVEL1_2"}, - {"DAYS1-3", "DAYS1_3"}, - {"FISH CHIPS", "FISH_CHIPS"}, - {"FOO*", "FOO_ASTERISK"}, - {"FOO:", "FOO_COLON"}, - {",,FOO:@$,", "_COMMA_COMMAFOO_COLON_AT_DOLLAR_COMMA"}, - } - - for _, tt := range tests { - got := safeGoEnumeratedValueName(tt.in) - if got != tt.want { - t.Errorf("safeGoEnumeratedValueName(%s): got: %s, want: %s", tt.in, got, tt.want) - } - } -} - func TestResolveRootName(t *testing.T) { tests := []struct { name string diff --git a/ygen/ir.go b/ygen/ir.go index d727ed81b..81da7908a 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -204,8 +204,8 @@ func (b *LangMapperBase) ResolveLeafrefTarget(path string, contextEntry *yang.En // // In testing contexts, this function requires SetupEnumSet to be called prior // to being usable. -func (b *LangMapperBase) EnumeratedTypedefTypeName(args resolveTypeArgs, prefix string, noUnderscores, useDefiningModuleForTypedefEnumNames bool) (string, string, error) { - return b.enumSet.enumeratedTypedefTypeName(args, prefix, noUnderscores, useDefiningModuleForTypedefEnumNames) +func (b *LangMapperBase) EnumeratedTypedefTypeName(yangType *yang.YangType, contextEntry *yang.Entry, prefix string, noUnderscores, useDefiningModuleForTypedefEnumNames bool) (string, string, error) { + return b.enumSet.enumeratedTypedefTypeName(resolveTypeArgs{yangType: yangType, contextEntry: contextEntry}, prefix, noUnderscores, useDefiningModuleForTypedefEnumNames) } // EnumName retrieves the type name of the input enum *yang.Entry that will be diff --git a/ygen/protoelements.go b/ygen/protoelements.go index aeb21dad2..c10cc7f39 100644 --- a/ygen/protoelements.go +++ b/ygen/protoelements.go @@ -21,6 +21,7 @@ import ( "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/internal/igenutil" "github.com/openconfig/ygot/util" ) @@ -79,6 +80,22 @@ func NewProtoLangMapper(basePackageName, enumPackageName string) *ProtoLangMappe } } +// resolveTypeArgs is a structure used as an input argument to the yangTypeToGoType +// function which allows extra context to be handed on. This provides the ability +// to use not only the YangType but also the yang.Entry that the type was part of +// to resolve the possible type name. +type resolveTypeArgs struct { + // yangType is a pointer to the yang.YangType that is to be mapped. + yangType *yang.YangType + // contextEntry is an optional yang.Entry which is supplied where a + // type requires knowledge of the leaf that it is used within to be + // mapped. For example, where a leaf is defined to have a type of a + // user-defined type (typedef) that in turn has enumerated values - the + // context of the yang.Entry is required such that the leaf's context + // can be established. + contextEntry *yang.Entry +} + // DirectoryName generates the proto message name to be used for a particular // YANG schema element in the generated code. // Since this conversion is lossy, a later step should resolve any naming @@ -150,7 +167,7 @@ func (s *ProtoLangMapper) KeyLeafType(e *yang.Entry, opts IROptions) (*MappedTyp func (s *ProtoLangMapper) PackageName(e *yang.Entry, compressBehaviour genutil.CompressBehaviour, nestedMessages bool) (string, error) { compressPaths := compressBehaviour.CompressEnabled() switch { - case IsFakeRoot(e): + case igenutil.IsFakeRoot(e): // In this case, we explicitly leave the package name as nil, which is interpeted // as meaning that the base package is used throughout the handling code. return "", nil @@ -250,7 +267,7 @@ func yangEnumTypeToProtoType(args resolveTypeArgs) (*MappedType, error) { // for additional details as to the transformation from YANG to Protobuf. func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { // Handle typedef cases. - typedefName, key, err := s.EnumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) + typedefName, key, err := s.EnumeratedTypedefTypeName(args.yangType, args.contextEntry, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { return nil, err } @@ -329,7 +346,7 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv // value cannot be nil/unset. func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { // Handle typedef cases. - typedefName, key, err := s.EnumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) + typedefName, key, err := s.EnumeratedTypedefTypeName(args.yangType, args.contextEntry, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { return nil, err } @@ -566,7 +583,7 @@ func (s *ProtoLangMapper) protoMsgName(e *yang.Entry, compressPaths bool) string // becomes interface (since modules, surrounding containers, and config/state containers // are not considered with path compression enabled. func (s *ProtoLangMapper) protobufPackage(e *yang.Entry, compressPaths bool) string { - if IsFakeRoot(e) { + if igenutil.IsFakeRoot(e) { return "" } diff --git a/ygen/protogen.go b/ygen/protogen.go index d6300165c..04b5db89d 100644 --- a/ygen/protogen.go +++ b/ygen/protogen.go @@ -25,6 +25,7 @@ import ( "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/internal/igenutil" "github.com/openconfig/ygot/util" ) @@ -164,7 +165,7 @@ var disallowedInProtoIDRegexp = regexp.MustCompile(`[^a-zA-Z0-9_]`) var ( // protoHeaderTemplate is populated and output at the top of the protobuf code output. - protoHeaderTemplate = mustMakeTemplate("header", ` + protoHeaderTemplate = igenutil.MustMakeTemplate("header", ` {{- /**/ -}} // {{ .PackageName }} is generated by {{ .CallerName }} as a protobuf // representation of a YANG schema. @@ -202,7 +203,7 @@ option go_package = "{{ .GoPackageName }}"; // protoMessageTemplate is populated for each entity that is mapped to a message // within the output protobuf. - protoMessageTemplate = mustMakeTemplate("msg", ` + protoMessageTemplate = igenutil.MustMakeTemplate("msg", ` {{ if .PathComment -}} // {{ .Name }} represents the {{ .YANGPath }} YANG schema element. {{ end -}} @@ -245,7 +246,7 @@ message {{ .Name }} { // protoEnumTemplate is the template used to generate enumerations that are // not within a message. Such enums are used where there are referenced YANG // identity nodes, and where there are typedefs which include an enumeration. - protoEnumTemplate = mustMakeTemplate("enum", ` + protoEnumTemplate = igenutil.MustMakeTemplate("enum", ` // {{ .Name }} represents an enumerated type generated for the {{ .Description }}. enum {{ .Name }} { {{- range $i, $val := .Values }} diff --git a/ypathgen/pathgen.go b/ypathgen/pathgen.go index 131f8d698..e395072f1 100644 --- a/ypathgen/pathgen.go +++ b/ypathgen/pathgen.go @@ -32,6 +32,7 @@ import ( "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/gogen" "github.com/openconfig/ygot/util" "github.com/openconfig/ygot/ygen" "github.com/openconfig/ygot/ygot" @@ -203,7 +204,7 @@ type GoImports struct { } type goLangMapper struct { - *ygen.GoLangMapper + *gogen.GoLangMapper } // PopulateFieldFlags populates extra field information for pathgen. @@ -267,7 +268,7 @@ func (cg *GenConfig) GeneratePathCode(yangFiles, includePaths []string) (map[str } var errs util.Errors - ir, err := ygen.GenerateIR(yangFiles, includePaths, goLangMapper{GoLangMapper: ygen.NewGoLangMapper(true)}, opts) + ir, err := ygen.GenerateIR(yangFiles, includePaths, goLangMapper{GoLangMapper: gogen.NewGoLangMapper(true)}, opts) if err != nil { return nil, nil, util.AppendErr(errs, err) } @@ -687,7 +688,7 @@ func getNodeDataMap(ir *ygen.IR, fakeRootName, schemaStructPkgAccessor, pathStru GoFieldName: goFieldNameMap[fieldName], SubsumingGoStructName: subsumingGoStructName, IsLeaf: isLeaf, - IsScalarField: ygen.IsScalarField(field), + IsScalarField: gogen.IsScalarField(field), HasDefault: isLeaf && (len(field.YANGDetails.Defaults) > 0 || mType.DefaultValue != nil), YANGTypeName: yangTypeName, YANGPath: field.YANGDetails.Path, From bf9ace1f5300a5167d37d17a6a5e135a456b00c7 Mon Sep 17 00:00:00 2001 From: wenovus Date: Mon, 6 Jun 2022 13:47:01 -0700 Subject: [PATCH 09/19] Fix Go workflow --- .github/workflows/go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 10726b7cb..4394ebade 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -87,7 +87,7 @@ jobs: # fake ygot and ytype paths specified in generation options. "openconfig-options-explicit.formatted-txt.go" ) - for f in ygen/testdata/schema/*.formatted-txt; do + for f in gogen/testdata/schema/*.formatted-txt; do if [[ ${skipped[@]} =~ $(basename $f) ]]; then continue fi @@ -96,7 +96,7 @@ jobs: skipped=( ) - for f in ygen/testdata/structs/*.formatted-txt; do + for f in gogen/testdata/structs/*.formatted-txt; do if [[ ${skipped[@]} =~ $(basename $f) ]]; then continue fi From 38c179f2831b659e84aeffe64c0b898d0aa477d2 Mon Sep 17 00:00:00 2001 From: wenovus Date: Mon, 6 Jun 2022 13:51:06 -0700 Subject: [PATCH 10/19] remove unused code --- ygen/codegen.go | 10 ---------- ygen/genstate_test.go | 8 -------- 2 files changed, 18 deletions(-) diff --git a/ygen/codegen.go b/ygen/codegen.go index b4f9720b1..987054fcc 100644 --- a/ygen/codegen.go +++ b/ygen/codegen.go @@ -230,16 +230,6 @@ type Proto3Package struct { UsesYextImport bool // UsesYextImport indicates whether the yext proto package is used within the generated package. } -// generatedLanguage represents a language supported in this package. -type generatedLanguage int64 - -const ( - // golang indicates that Go code is being generated. - golang generatedLanguage = iota - // protobuf indicates that Protobuf messages are being generated. - protobuf -) - // GenerateProto3 generates Protobuf 3 code for the input set of YANG files. // The YANG schemas for which protobufs are to be created is supplied as the // yangFiles argument, with included modules being searched for in includePaths. diff --git a/ygen/genstate_test.go b/ygen/genstate_test.go index 2610ef7f5..71485c7fe 100644 --- a/ygen/genstate_test.go +++ b/ygen/genstate_test.go @@ -1504,14 +1504,6 @@ func enumMapFromArgs(args []resolveTypeArgs) map[string]*yang.Entry { return enumMap } -// enumMapFromEntries recursively finds enumerated values from an entry and -// returns an enumMap. The input enumMap is intended for findEnumSet. -func enumMapFromEntry(entry *yang.Entry) map[string]*yang.Entry { - enumMap := map[string]*yang.Entry{} - addEnumsToEnumMap(entry, enumMap) - return enumMap -} - // addEnumsToEnumMap recursively finds enumerated values and adds them to the // input enumMap. The input enumMap is intended for findEnumSet, so that tests // that need generated enumerated names have an easy time generating them, and From c224ae0331d4e70030c38e9862a7d8a534b7d5cd Mon Sep 17 00:00:00 2001 From: wenovus Date: Mon, 6 Jun 2022 13:59:04 -0700 Subject: [PATCH 11/19] Fix workflow --- .github/workflows/go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 4394ebade..33225f049 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -114,8 +114,8 @@ jobs: fi filename=$(basename $f) f_prefix="${filename%%.*}" - ygen_file="ygen/testdata/structs/${f_prefix}.formatted-txt" - test-go-build $f $ygen_file + go_file="gogen/testdata/structs/${f_prefix}.formatted-txt" + test-go-build $f $go_file done static_analysis: From 2d17a7bfeddd19794de6d9ffdae8aa409da141af Mon Sep 17 00:00:00 2001 From: wenovus Date: Mon, 6 Jun 2022 19:43:39 -0700 Subject: [PATCH 12/19] Use bash instead of overalls --- .github/workflows/go.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 10726b7cb..576039df3 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -144,7 +144,17 @@ jobs: - name: Run coverage run: | - overalls -project ${GITHUB_WORKSPACE} -covermode=count -ignore=".git,vendor,integration_tests,ygot/schema_tests,ygen/schema_tests,ypathgen/path_tests,demo,experimental/ygotutils,generator,ytypes/schema_tests,ypathgen/generator" + pkgs="$(go list ./... | grep -v 'github.com/openconfig/ygot/exampleoc' | grep -v 'github.com/openconfig/ygot/uexampleoc' | grep -v 'github.com/openconfig/ygot/proto/' | grep -v 'github.com/openconfig/ygot/demo/' | grep -v 'github.com/openconfig/ygot/integration_tests' | grep -v 'github.com/openconfig/ygot/generator' | tr '\n' ' ')" + for p in $pkgs; do + dir=$(echo $p | sed -e 's/^github\.com\/openconfig\/ygot\///') + go test -covermode count -coverprofile profile.coverprofile -outputdir $dir $p + done + + echo 'mode: count' > overalls.coverprofile + for p in $pkgs; do + dir=$(echo $p | sed -e 's/^github\.com\/openconfig\/ygot\///') + tail -n +2 $dir/profile.coverprofile >> overalls.coverprofile + done - name: Submit coverage uses: shogo82148/actions-goveralls@v1 From c155151212cbf81cb507d118918ca2556243705f Mon Sep 17 00:00:00 2001 From: wenovus Date: Mon, 6 Jun 2022 19:55:45 -0700 Subject: [PATCH 13/19] Overwrite report for gogen --- .github/workflows/go.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 47e5509f2..93e775ab9 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -150,6 +150,11 @@ jobs: go test -covermode count -coverprofile profile.coverprofile -outputdir $dir $p done + # Overwrite results for gogen/protogen since they also cover ygen's + # code. + go test -covermode count -coverprofile profile.coverprofile -outputdir gogen -coverpkg github.com/openconfig/ygot/gogen,github.com/openconfig/ygot/ygen github.com/openconfig/ygot/gogen + #go test -covermode count -coverprofile profile.coverprofile -outputdir protogen -coverpkg github.com/openconfig/ygot/protogen,github.com/openconfig/ygot/ygen github.com/openconfig/ygot/protogen + echo 'mode: count' > overalls.coverprofile for p in $pkgs; do dir=$(echo $p | sed -e 's/^github\.com\/openconfig\/ygot\///') From 5a055702d452a81c18c96567dbc94a0fa4ed3dbf Mon Sep 17 00:00:00 2001 From: wenovus Date: Tue, 7 Jun 2022 16:30:01 -0700 Subject: [PATCH 14/19] Improve comments --- ygen/ir.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ygen/ir.go b/ygen/ir.go index 28653a516..e36a7867a 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -107,6 +107,8 @@ type LangMapper interface { type LangMapperExt interface { // PopulateFieldFlags populates extra information given a particular // field of a ParsedDirectory and the corresponding AST node. + // Fields of a ParsedDirectory can be any non-choice/case node (e.g. + // YANG leafs, containers, lists). PopulateFieldFlags(NodeDetails, *yang.Entry) map[string]string // PopulateEnumFlags populates extra information given a particular // enumerated type its corresponding AST representation. @@ -403,10 +405,10 @@ type NodeDetails struct { // Shadow paths are paths that have sibling config/state values // that have been compressed out due to path compression. ShadowMappedPathModules [][]string - // LangMapper during IR generation to assist the code generation stage. // Flags contains extra information that can be populated by the // LangMapper during IR generation to assist the code generation stage. - // It needs to implement the LangMapperExt interface. + // Specifically, this field is set by the + // LangMapperExt.PopulateFieldFlags function. Flags map[string]string } @@ -606,6 +608,7 @@ type EnumeratedYANGType struct { ValToYANGDetails []ygot.EnumDefinition // Flags contains extra information that can be populated by the // LangMapper during IR generation to assist the code generation stage. - // It needs to implement the LangMapperExt interface. + // Specifically, this field is set by the + // LangMapperExt.PopulateEnumFlags function. Flags map[string]string } From a5d70dc01ccb8c39e9042875126ef70d4d4c33d9 Mon Sep 17 00:00:00 2001 From: wenovus Date: Tue, 7 Jun 2022 16:53:05 -0700 Subject: [PATCH 15/19] Change enumeratedTypedefTypeName to return a bool for whether the input type is actually a typedef --- ygen/enumgen.go | 20 ++++++++++---------- ygen/goelements.go | 8 ++++---- ygen/protoelements.go | 8 ++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ygen/enumgen.go b/ygen/enumgen.go index ba8bb3a33..9071a7883 100644 --- a/ygen/enumgen.go +++ b/ygen/enumgen.go @@ -204,18 +204,18 @@ func (s *enumSet) enumName(e *yang.Entry, compressPaths, noUnderscores, skipDedu // a typedef which is either an identityref or an enumeration). The resolved // name is prefixed with the prefix supplied. If the type that was supplied // within the resolveTypeArgs struct is not a type definition which includes an -// enumerated type, an empty string is returned as the first returned value; -// otherwise it should be populated. The second value returned is a string key -// that uniquely identifies this enumerated value among all possible enumerated -// values in the input set of YANG files. -func (s *enumSet) enumeratedTypedefTypeName(args resolveTypeArgs, prefix string, noUnderscores, useDefiningModuleForTypedefEnumNames bool) (string, string, error) { +// enumerated type, the third returned value (boolean) will be false. +// The second value returned is a string key that uniquely identifies this +// enumerated value among all possible enumerated values in the input set of +// YANG files. +func (s *enumSet) enumeratedTypedefTypeName(args resolveTypeArgs, prefix string, noUnderscores, useDefiningModuleForTypedefEnumNames bool) (string, string, bool, error) { switch args.yangType.Kind { case yang.Yenum, yang.Yidentityref: // In the case of a typedef that specifies an enumeration or identityref // then generate a enumerated type in the Go code according to the contextEntry // which has been provided by the calling code. if args.contextEntry == nil { - return "", "", fmt.Errorf("error mapping node %s due to lack of context", args.yangType.Name) + return "", "", false, fmt.Errorf("error mapping node %s due to lack of context", args.yangType.Name) } // If the type that is specified is not a built-in type (i.e., one of those // types which is defined in RFC6020/RFC7950) then we establish what the type @@ -223,19 +223,19 @@ func (s *enumSet) enumeratedTypedefTypeName(args resolveTypeArgs, prefix string, // the type that is specified in the schema. definingType, err := util.DefiningType(args.yangType, args.contextEntry.Type) if err != nil { - return "", "", err + return "", "", false, err } enumIsTypedef := args.yangType.Kind == yang.Yenum && !util.IsYANGBaseType(definingType) if !util.IsYANGBaseType(args.yangType) || (useDefiningModuleForTypedefEnumNames && enumIsTypedef) { tn, key, err := s.typedefEnumeratedName(args, noUnderscores, useDefiningModuleForTypedefEnumNames) if err != nil { - return "", "", err + return "", "", false, err } - return fmt.Sprintf("%s%s", prefix, tn), key, nil + return fmt.Sprintf("%s%s", prefix, tn), key, true, nil } } - return "", "", nil + return "", "", false, nil } // typedefEnumeratedName retrieves the generated name of the input *yang.Entry diff --git a/ygen/goelements.go b/ygen/goelements.go index 416021bd0..0b83c89c6 100644 --- a/ygen/goelements.go +++ b/ygen/goelements.go @@ -310,14 +310,14 @@ func (s *GoLangMapper) SetSchemaTree(st *schemaTree) { func (s *GoLangMapper) yangTypeToGoType(args resolveTypeArgs, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (*MappedType, error) { defVal := genutil.TypeDefaultValue(args.yangType) // Handle the case of a typedef which is actually an enumeration. - typedefName, _, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) + typedefName, _, isTypedef, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) if err != nil { // err is non nil when this was a typedef which included // an invalid enumerated type. return nil, err } - if typedefName != "" { + if isTypedef { return &MappedType{ NativeType: typedefName, IsEnumeratedValue: true, @@ -621,13 +621,13 @@ func generateGoDefaultValue(field *yang.Entry, mtype *MappedType, gogen *GoLangM // type for each leaf is created. func (s *GoLangMapper) yangDefaultValueToGo(value string, args resolveTypeArgs, isSingletonUnion, compressOCPaths, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames bool, enumOrgPrefixesToTrim []string) (string, yang.TypeKind, error) { // Handle the case of a typedef which is actually an enumeration. - typedefName, _, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) + typedefName, _, isTypedef, err := s.enumSet.enumeratedTypedefTypeName(args, goEnumPrefix, false, useDefiningModuleForTypedefEnumNames) if err != nil { // err is non nil when this was a typedef which included // an invalid enumerated type. return "", yang.Ynone, err } - if typedefName != "" { + if isTypedef { if strings.Contains(value, ":") { value = strings.Split(value, ":")[1] } diff --git a/ygen/protoelements.go b/ygen/protoelements.go index 3cd9e8991..caaa809c9 100644 --- a/ygen/protoelements.go +++ b/ygen/protoelements.go @@ -260,13 +260,13 @@ func yangEnumTypeToProtoType(args resolveTypeArgs) (*MappedType, error) { // for additional details as to the transformation from YANG to Protobuf. func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { // Handle typedef cases. - typedefName, key, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) + typedefName, key, isTypedef, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { return nil, err } // typedefName is set to non-empty-string when this was a valid enumeration // within a typedef. - if typedefName != "" { + if isTypedef { return &MappedType{ NativeType: typedefName, IsEnumeratedValue: true, @@ -339,13 +339,13 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv // value cannot be nil/unset. func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { // Handle typedef cases. - typedefName, key, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) + typedefName, key, isTypedef, err := s.enumSet.enumeratedTypedefTypeName(args, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { return nil, err } // typedefName is set to non-empty-string when this was a valid enumeration // within a typedef. - if typedefName != "" { + if isTypedef { return &MappedType{ NativeType: typedefName, IsEnumeratedValue: true, From 558fe5cce26f7a2b59b29f604a0698c75a893f7c Mon Sep 17 00:00:00 2001 From: wenovus Date: Wed, 8 Jun 2022 10:37:42 -0700 Subject: [PATCH 16/19] Rename Setup to Inject and fix-up comments --- ygen/genstate_test.go | 12 +++++------ ygen/goelements_test.go | 10 ++++----- ygen/ir.go | 42 +++++++++++++++++++------------------- ygen/protoelements_test.go | 8 ++++---- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/ygen/genstate_test.go b/ygen/genstate_test.go index d12be3b35..a36cdb089 100644 --- a/ygen/genstate_test.go +++ b/ygen/genstate_test.go @@ -1370,11 +1370,11 @@ func TestBuildDirectoryDefinitions(t *testing.T) { t.Run(fmt.Sprintf("%s:buildDirectoryDefinitions(CompressBehaviour:%v,Language:%s,excludeState:%v)", tt.name, c.compressBehaviour, langName(c.lang), c.excludeState), func(t *testing.T) { gogen := NewGoLangMapper(true) - if err := gogen.SetupSchemaTree(tt.in); err != nil { + if err := gogen.InjectSchemaTree(tt.in); err != nil { t.Fatalf("buildSchemaTree(%v), got unexpected err: %v", tt.in, err) } protogen := NewProtoLangMapper(DefaultBasePackageName, DefaultEnumPackageName) - if err := protogen.SetupSchemaTree(tt.in); err != nil { + if err := protogen.InjectSchemaTree(tt.in); err != nil { t.Fatalf("buildSchemaTree(%v), got unexpected err: %v", tt.in, err) } @@ -2266,16 +2266,16 @@ func TestBuildListKey(t *testing.T) { enumMap := enumMapFromEntries(tt.inEnumEntries) addEnumsToEnumMap(tt.in, enumMap) - if err := s.SetupEnumSet(enumMap, tt.inCompress, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { + if err := s.InjectEnumSet(enumMap, tt.inCompress, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { if !tt.wantErr { - t.Errorf("SetupEnumSet failed: %v", err) + t.Errorf("InjectEnumSet failed: %v", err) } return } if tt.inEntries != nil { - if err := s.SetupSchemaTree(tt.inEntries); err != nil { - t.Fatalf("%s: SetupSchemaTree(%v), could not build tree: %v", tt.name, tt.inEntries, err) + if err := s.InjectSchemaTree(tt.inEntries); err != nil { + t.Fatalf("%s: InjectSchemaTree(%v), could not build tree: %v", tt.name, tt.inEntries, err) } } diff --git a/ygen/goelements_test.go b/ygen/goelements_test.go index 2997a8ee2..5aef18d20 100644 --- a/ygen/goelements_test.go +++ b/ygen/goelements_test.go @@ -441,7 +441,7 @@ func TestUnionSubTypes(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := NewGoLangMapper(true) - if err := s.SetupEnumSet(enumMapFromEntry(tt.inCtxEntry), false, false, false, true, true, true, nil); err != nil { + if err := s.InjectEnumSet(enumMapFromEntry(tt.inCtxEntry), false, false, false, true, true, true, nil); err != nil { t.Fatal(err) } @@ -1091,7 +1091,7 @@ func TestYangTypeToGoType(t *testing.T) { s := NewGoLangMapper(true) enumMap := enumMapFromEntries(tt.inEnumEntries) addEnumsToEnumMap(tt.ctx, enumMap) - if err := s.SetupEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { + if err := s.InjectEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { if !tt.wantErr { t.Errorf("findEnumSet failed: %v", err) } @@ -1468,7 +1468,7 @@ func TestTypeResolutionManyToOne(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := NewGoLangMapper(true) - if err := s.SetupEnumSet(enumMapFromEntries(tt.inLeaves), tt.inCompressOCPaths, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { + if err := s.InjectEnumSet(enumMapFromEntries(tt.inLeaves), tt.inCompressOCPaths, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { t.Fatalf("findEnumSet failed: %v", err) } @@ -2472,7 +2472,7 @@ func TestYangDefaultValueToGo(t *testing.T) { s := NewGoLangMapper(true) enumMap := enumMapFromEntries(tt.inEnumEntries) addEnumsToEnumMap(tt.inCtx, enumMap) - if err := s.SetupEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { + if err := s.InjectEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { if !tt.wantErr { t.Errorf("findEnumSet failed: %v", err) } @@ -2831,7 +2831,7 @@ func TestYangDefaultValueToGo(t *testing.T) { s := NewGoLangMapper(true) enumMap := enumMapFromEntries(tt.inEnumEntries) addEnumsToEnumMap(tt.inCtx, enumMap) - if err := s.SetupEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { + if err := s.InjectEnumSet(enumMap, tt.inCompressPath, false, tt.inSkipEnumDedup, true, true, true, nil); err != nil { if !tt.wantErr { t.Errorf("findEnumSet failed: %v", err) } diff --git a/ygen/ir.go b/ygen/ir.go index b688a26ee..35903025c 100644 --- a/ygen/ir.go +++ b/ygen/ir.go @@ -105,17 +105,17 @@ type LangMapperBaseSetup interface { // their target leaves. setSchemaTree(*schemaTree) - // SetupEnumSet is intended to be called by unit tests in order to set up the + // InjectEnumSet is intended to be called by unit tests in order to set up the // LangMapperBase such that generated enumeration/identity names can be looked // up. The input parameters correspond to fields in IROptions. // It returns an error if there is a failure to generate the enumerated // values' names. - SetupEnumSet(entries map[string]*yang.Entry, compressPaths, noUnderscores, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, appendEnumSuffixForSimpleUnionEnums bool, enumOrgPrefixesToTrim []string) error + InjectEnumSet(entries map[string]*yang.Entry, compressPaths, noUnderscores, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, appendEnumSuffixForSimpleUnionEnums bool, enumOrgPrefixesToTrim []string) error - // SetupSchemaTree is intended to be called by unit tests in order to set up + // InjectSchemaTree is intended to be called by unit tests in order to set up // the LangMapperBase such that leafrefs targets may be looked up. // It returns an error if there is duplication within the set of entries. - SetupSchemaTree(entries []*yang.Entry) error + InjectSchemaTree(entries []*yang.Entry) error } // LangMapperBase contains unexported base types and exported built-in methods @@ -134,10 +134,10 @@ type LangMapperBase struct { // setEnumSet is used to supply a set of enumerated values to the // mapper such that leaves that have enumerated types can be looked up. // -// NB: This method is a set-up method that the user does not need to be invoked -// within the GenerateIR context. In testing contexts outside of GenerateIR, -// however, this needs to be called prior to certain built-in methods of -// LangMapperBase are available for use. +// NB: This method is a set-up method that GenerateIR automatically invokes. +// In testing contexts outside of GenerateIR, however, the corresponding +// exported Inject method needs to be called in order for certain built-in +// methods of LangMapperBase to be available for use. func (s *LangMapperBase) setEnumSet(e *enumSet) { s.enumSet = e } @@ -146,22 +146,22 @@ func (s *LangMapperBase) setEnumSet(e *enumSet) { // the mapped such that leaves of type leafref can be resolved to // their target leaves. // -// NB: This method is a set-up method that the user does not need to be invoked -// within the GenerateIR context. In testing contexts outside of GenerateIR, -// however, this needs to be called prior to certain built-in methods of -// LangMapperBase are available for use. +// NB: This method is a set-up method that GenerateIR automatically invokes. +// In testing contexts outside of GenerateIR, however, the corresponding +// exported Inject method needs to be called in order for certain built-in +// methods of LangMapperBase to be available for use. func (s *LangMapperBase) setSchemaTree(st *schemaTree) { s.schematree = st } -// SetupEnumSet is intended to be called by unit tests in order to set up the +// InjectEnumSet is intended to be called by unit tests in order to set up the // LangMapperBase such that generated enumeration/identity names can be looked // up. It walks the input map of enumerated value leaves keyed by path and // creates generates names for them. The input parameters correspond to fields // in IROptions. // It returns an error if there is a failure to generate the enumerated values' // names. -func (s *LangMapperBase) SetupEnumSet(entries map[string]*yang.Entry, compressPaths, noUnderscores, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, appendEnumSuffixForSimpleUnionEnums bool, enumOrgPrefixesToTrim []string) error { +func (s *LangMapperBase) InjectEnumSet(entries map[string]*yang.Entry, compressPaths, noUnderscores, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, appendEnumSuffixForSimpleUnionEnums bool, enumOrgPrefixesToTrim []string) error { enumSet, _, errs := findEnumSet(entries, compressPaths, noUnderscores, skipEnumDedup, shortenEnumLeafNames, useDefiningModuleForTypedefEnumNames, appendEnumSuffixForSimpleUnionEnums, enumOrgPrefixesToTrim) if errs != nil { return fmt.Errorf("%v", errs) @@ -170,11 +170,11 @@ func (s *LangMapperBase) SetupEnumSet(entries map[string]*yang.Entry, compressPa return nil } -// SetupSchemaTree is intended to be called by unit tests in order to set up +// InjectSchemaTree is intended to be called by unit tests in order to set up // the LangMapperBase such that leafrefs targets may be looked up. It maps a // set of yang.Entry pointers into a ctree structure. // It returns an error if there is duplication within the set of entries. -func (s *LangMapperBase) SetupSchemaTree(entries []*yang.Entry) error { +func (s *LangMapperBase) InjectSchemaTree(entries []*yang.Entry) error { schematree, err := buildSchemaTree(entries) if err != nil { return err @@ -189,7 +189,7 @@ func (s *LangMapperBase) SetupSchemaTree(entries []*yang.Entry) error { // is associated with the target, and the target yang.Entry, such that the // caller can map this to the relevant language type. // -// In testing contexts, this function requires SetupSchemaTree to be called +// In testing contexts, this function requires InjectSchemaTree to be called // prior to being usable. func (b *LangMapperBase) ResolveLeafrefTarget(path string, contextEntry *yang.Entry) (*yang.Entry, error) { return b.schematree.resolveLeafrefTarget(path, contextEntry) @@ -204,7 +204,7 @@ func (b *LangMapperBase) ResolveLeafrefTarget(path string, contextEntry *yang.En // enumerated value among all possible enumerated values in the input set of // YANG files. // -// In testing contexts, this function requires SetupEnumSet to be called prior +// In testing contexts, this function requires InjectEnumSet to be called prior // to being usable. func (b *LangMapperBase) EnumeratedTypedefTypeName(args resolveTypeArgs, prefix string, noUnderscores, useDefiningModuleForTypedefEnumNames bool) (string, string, bool, error) { return b.enumSet.enumeratedTypedefTypeName(args, prefix, noUnderscores, useDefiningModuleForTypedefEnumNames) @@ -215,7 +215,7 @@ func (b *LangMapperBase) EnumeratedTypedefTypeName(args resolveTypeArgs, prefix // value returned is a string key that uniquely identifies this enumerated // value among all possible enumerated values in the input set of YANG files. // -// In testing contexts, this function requires SetupEnumSet to be called prior +// In testing contexts, this function requires InjectEnumSet to be called prior // to being usable. func (b *LangMapperBase) EnumName(e *yang.Entry, compressPaths, noUnderscores, skipDedup, shortenEnumLeafNames, addEnumeratedUnionSuffix bool, enumOrgPrefixesToTrim []string) (string, string, error) { return b.enumSet.enumName(e, compressPaths, noUnderscores, skipDedup, shortenEnumLeafNames, addEnumeratedUnionSuffix, enumOrgPrefixesToTrim) @@ -227,7 +227,7 @@ func (b *LangMapperBase) EnumName(e *yang.Entry, compressPaths, noUnderscores, s // value returned is a string key that uniquely identifies this enumerated // value among all possible enumerated values in the input set of YANG files. // -// In testing contexts, this function requires SetupEnumSet to be called prior +// In testing contexts, this function requires InjectEnumSet to be called prior // to being usable. func (b *LangMapperBase) IdentityrefBaseTypeFromIdentity(i *yang.Identity) (string, string, error) { return b.enumSet.identityrefBaseTypeFromIdentity(i) @@ -243,7 +243,7 @@ func (b *LangMapperBase) IdentityrefBaseTypeFromIdentity(i *yang.Identity) (stri // covers the common case that the caller is interested in determining the name // from an identityref leaf, rather than directly from the identity. // -// In testing contexts, this function requires SetupEnumSet to be called prior +// In testing contexts, this function requires InjectEnumSet to be called prior // to being usable. func (b *LangMapperBase) IdentityrefBaseTypeFromLeaf(idr *yang.Entry) (string, string, error) { return b.enumSet.identityrefBaseTypeFromIdentity(idr.Type.IdentityBase) diff --git a/ygen/protoelements_test.go b/ygen/protoelements_test.go index 1ef200531..2bd865b4a 100644 --- a/ygen/protoelements_test.go +++ b/ygen/protoelements_test.go @@ -600,8 +600,8 @@ func TestYangTypeToProtoType(t *testing.T) { // Seed the schema tree with the injected entries, used to ensure leafrefs can // be resolved. if tt.inEntries != nil { - if err := s.SetupSchemaTree(tt.inEntries); err != nil { - t.Fatalf("%s: SetupSchemaTree(%v): got unexpected error, got: %v, want: nil", tt.name, tt.inEntries, err) + if err := s.InjectSchemaTree(tt.inEntries); err != nil { + t.Fatalf("%s: InjectSchemaTree(%v): got unexpected error, got: %v, want: nil", tt.name, tt.inEntries, err) } } // Seed the enumSet with the injected enum entries, @@ -610,9 +610,9 @@ func TestYangTypeToProtoType(t *testing.T) { for _, e := range enumMapFromEntries(tt.inEntries) { addEnumsToEnumMap(e, enumMap) } - if err := s.SetupEnumSet(enumMap, false, true, false, true, true, true, nil); err != nil { + if err := s.InjectEnumSet(enumMap, false, true, false, true, true, true, nil); err != nil { if !tt.wantErr { - t.Errorf("SetupEnumSet failed: %v", err) + t.Errorf("InjectEnumSet failed: %v", err) } return } From 166c99a66fa7729e19903fcaf9e81bf2cda105ca Mon Sep 17 00:00:00 2001 From: wenovus Date: Wed, 8 Jun 2022 10:41:00 -0700 Subject: [PATCH 17/19] Don't install overalls --- .github/workflows/go.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 576039df3..8a213408c 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -131,7 +131,6 @@ jobs: - name: Install required static analysis tools run: | - go install github.com/go-playground/overalls@latest go install github.com/mattn/goveralls@latest go install honnef.co/go/tools/cmd/staticcheck@latest @@ -150,16 +149,16 @@ jobs: go test -covermode count -coverprofile profile.coverprofile -outputdir $dir $p done - echo 'mode: count' > overalls.coverprofile + echo 'mode: count' > concatenated.coverprofile for p in $pkgs; do dir=$(echo $p | sed -e 's/^github\.com\/openconfig\/ygot\///') - tail -n +2 $dir/profile.coverprofile >> overalls.coverprofile + tail -n +2 $dir/profile.coverprofile >> concatenated.coverprofile done - name: Submit coverage uses: shogo82148/actions-goveralls@v1 with: - path-to-profile: overalls.coverprofile + path-to-profile: concatenated.coverprofile - name: Go vet run: | From bd8c8ba1a985c8155fe38d2e22888901c8e764e8 Mon Sep 17 00:00:00 2001 From: wenovus Date: Wed, 8 Jun 2022 11:25:06 -0700 Subject: [PATCH 18/19] Remove Go from some some public types/functions in gogen --- demo/getting_started/interfaces_test.go | 6 +-- generator/generator.go | 12 ++--- generator/generator_test.go | 28 +++++------ gogen/codegen.go | 24 +++++----- gogen/codegen_test.go | 14 +++--- gogen/gogen.go | 63 ++++++++++++++----------- internal/igenutil/genutil.go | 6 --- ygen/protogen.go | 13 +++-- 8 files changed, 87 insertions(+), 79 deletions(-) diff --git a/demo/getting_started/interfaces_test.go b/demo/getting_started/interfaces_test.go index 0963be6e4..4822cad9c 100644 --- a/demo/getting_started/interfaces_test.go +++ b/demo/getting_started/interfaces_test.go @@ -68,10 +68,10 @@ func TestGenerateCode(t *testing.T) { }} for _, tt := range tests { - cg := gogen.NewGoCodeGenerator(tt.inConfig, tt.inGoOpts) - got, err := cg.GenerateGoCode(tt.inFiles, tt.inPaths) + cg := gogen.NewCodeGenerator(tt.inConfig, tt.inGoOpts) + got, err := cg.Generate(tt.inFiles, tt.inPaths) if err != nil { - t.Errorf("%s: GenerateGoCode(%v, %v): Config: %v, got unexpected error: %v", tt.name, tt.inFiles, tt.inPaths, tt.inConfig, err) + t.Errorf("%s: Generate(%v, %v): Config: %v, got unexpected error: %v", tt.name, tt.inFiles, tt.inPaths, tt.inConfig, err) continue } diff --git a/generator/generator.go b/generator/generator.go index 91cbd4523..28b6515ed 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -109,10 +109,10 @@ var ( packageSuffix = flag.String("path_struct_package_suffix", "path", "Suffix to append to generated Go package names, when split_pathstructs_by_module=true.") ) -// writeGoCodeSingleFile takes a ygen.GeneratedGoCode struct and writes the Go code +// writeGoCodeSingleFile takes a gogen.GeneratedCode struct and writes the Go code // snippets contained within it to the io.Writer, w, provided as an argument. // The output includes a package header which is generated. -func writeGoCodeSingleFile(w io.Writer, goCode *gogen.GeneratedGoCode) error { +func writeGoCodeSingleFile(w io.Writer, goCode *gogen.GeneratedCode) error { // Write the package header to the supplier writer. fmt.Fprint(w, goCode.CommonHeader) fmt.Fprint(w, goCode.OneOffHeader) @@ -152,11 +152,11 @@ func writeGoPathCodeSingleFile(w io.Writer, pathCode *ypathgen.GeneratedPathCode // splitCodeByFileN generates a map, keyed by filename, to a string containing // the code to be output to that filename. It allows division of a -// ygen.GeneratedGoCode struct into a set of source files. It divides the +// gogen.GeneratedCode struct into a set of source files. It divides the // methods, interfaces, and enumeration code snippets into their own files. // Structs are output into files by splitting them evenly among the input split // number. -func splitCodeByFileN(goCode *gogen.GeneratedGoCode, fileN int) (map[string]string, error) { +func splitCodeByFileN(goCode *gogen.GeneratedCode, fileN int) (map[string]string, error) { structN := len(goCode.Structs) if fileN < 1 || fileN > structN { return nil, fmt.Errorf("requested %d files, but must be between 1 and %d (number of schema structs)", fileN, structN) @@ -325,7 +325,7 @@ func main() { } // Perform the code generation. - cg := gogen.NewGoCodeGenerator(&ygen.GeneratorConfig{ + cg := gogen.NewCodeGenerator(&ygen.GeneratorConfig{ ParseOptions: ygen.ParseOpts{ ExcludeModules: modsExcluded, SkipEnumDeduplication: *skipEnumDedup, @@ -367,7 +367,7 @@ func main() { }, ) - generatedGoCode, errs := cg.GenerateGoCode(generateModules, includePaths) + generatedGoCode, errs := cg.Generate(generateModules, includePaths) if errs != nil { log.Exitf("ERROR Generating GoStruct Code: %v\n", errs) } diff --git a/generator/generator_test.go b/generator/generator_test.go index c8342bffb..13b31e595 100644 --- a/generator/generator_test.go +++ b/generator/generator_test.go @@ -29,11 +29,11 @@ import ( func TestWriteGoCode(t *testing.T) { tests := []struct { name string - inGoCode *gogen.GeneratedGoCode + inGoCode *gogen.GeneratedCode wantCode string }{{ name: "single element structs and enums", - inGoCode: &gogen.GeneratedGoCode{ + inGoCode: &gogen.GeneratedCode{ Structs: []gogen.GoStructCodeSnippet{{ StructDef: `structOne`, }}, @@ -46,7 +46,7 @@ enumOne `, }, { name: "multi-element structs and enums", - inGoCode: &gogen.GeneratedGoCode{ + inGoCode: &gogen.GeneratedCode{ Structs: []gogen.GoStructCodeSnippet{{ StructDef: "structOne", }, { @@ -63,7 +63,7 @@ enumTwo `, }, { name: "json string code", - inGoCode: &gogen.GeneratedGoCode{ + inGoCode: &gogen.GeneratedCode{ JSONSchemaCode: "foo", }, wantCode: ` @@ -71,7 +71,7 @@ foo `, }, { name: "enum type map", - inGoCode: &gogen.GeneratedGoCode{ + inGoCode: &gogen.GeneratedCode{ EnumTypeMap: "map", }, wantCode: ` @@ -95,13 +95,13 @@ map func TestSplitCodeByFileN(t *testing.T) { tests := []struct { name string - in *gogen.GeneratedGoCode + in *gogen.GeneratedCode inFileN int want map[string]string wantErrSubstring string }{{ name: "simple struct with all only structs populated", - in: &gogen.GeneratedGoCode{ + in: &gogen.GeneratedCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", Structs: []gogen.GoStructCodeSnippet{{ @@ -122,7 +122,7 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "less than 1 file requested for splitting", - in: &gogen.GeneratedGoCode{ + in: &gogen.GeneratedCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", Structs: []gogen.GoStructCodeSnippet{{ @@ -137,7 +137,7 @@ func TestSplitCodeByFileN(t *testing.T) { wantErrSubstring: "requested 0 files", }, { name: "more than # of structs files requested for splitting", - in: &gogen.GeneratedGoCode{ + in: &gogen.GeneratedCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", Structs: []gogen.GoStructCodeSnippet{{ @@ -152,7 +152,7 @@ func TestSplitCodeByFileN(t *testing.T) { wantErrSubstring: "requested 2 files", }, { name: "two structs with enums populated", - in: &gogen.GeneratedGoCode{ + in: &gogen.GeneratedCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", Structs: []gogen.GoStructCodeSnippet{{ @@ -181,7 +181,7 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "two structs, separated into two files", - in: &gogen.GeneratedGoCode{ + in: &gogen.GeneratedCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", Structs: []gogen.GoStructCodeSnippet{{ @@ -210,7 +210,7 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "five structs, separated into four files", - in: &gogen.GeneratedGoCode{ + in: &gogen.GeneratedCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", Structs: []gogen.GoStructCodeSnippet{{ @@ -253,7 +253,7 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "five structs, separated into three files", - in: &gogen.GeneratedGoCode{ + in: &gogen.GeneratedCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", Structs: []gogen.GoStructCodeSnippet{{ @@ -295,7 +295,7 @@ func TestSplitCodeByFileN(t *testing.T) { }, }, { name: "five structs, separated into two files", - in: &gogen.GeneratedGoCode{ + in: &gogen.GeneratedCode{ CommonHeader: "common_header\n", OneOffHeader: "oneoff_header\n", Structs: []gogen.GoStructCodeSnippet{{ diff --git a/gogen/codegen.go b/gogen/codegen.go index e668c4c6b..cc9e23f55 100644 --- a/gogen/codegen.go +++ b/gogen/codegen.go @@ -12,9 +12,9 @@ import ( "github.com/openconfig/ygot/ygot" ) -// GoCodeGenerator is a structure that is used to pass arguments as to +// CodeGenerator is a structure that is used to pass arguments as to // how the output Go code should be generated. -type GoCodeGenerator struct { +type CodeGenerator struct { // Config stores the configuration parameters used for code generation. Config ygen.GeneratorConfig // GoOptions stores a struct which stores Go code generation specific @@ -102,7 +102,7 @@ type GoOpts struct { AppendEnumSuffixForSimpleUnionEnums bool } -// GeneratedGoCode contains generated code snippets that can be processed by the calling +// GeneratedCode contains generated code snippets that can be processed by the calling // application. The generated code is divided into two types of objects - both represented // as a slice of strings: Structs contains a set of Go structures that have been generated, // and Enums contains the code for generated enumerated types (corresponding to identities, @@ -116,7 +116,7 @@ type GoOpts struct { // The keys of the map are strings corresponding to the name of the generated type, with the // map values being maps of the int64 identifier for each value of the enumeration to the name of // the element, as used in the YANG schema. -type GeneratedGoCode struct { +type GeneratedCode struct { Structs []GoStructCodeSnippet // Structs is the generated set of structs representing containers or lists in the input YANG models. Enums []string // Enums is the generated set of enum definitions corresponding to identities and enumerations in the input YANG models. CommonHeader string // CommonHeader is the header that should be used for all output Go files. @@ -133,16 +133,16 @@ type GeneratedGoCode struct { // - structname - the name of the struct that was generated for the schema element. JSONSchemaCode string // RawJSONSchema stores the JSON document which is serialised and stored in JSONSchemaCode. - // It is populated only if the StoreRawSchema GoCodeGenerator boolean is set to true. + // It is populated only if the StoreRawSchema CodeGenerator boolean is set to true. RawJSONSchema []byte // EnumTypeMap is a Go map that allows YANG schemapaths to be mapped to reflect.Type values. EnumTypeMap string } -// NewGoCodeGenerator returns a new instance of the GoCodeGenerator +// NewCodeGenerator returns a new instance of the CodeGenerator // struct to the calling function. -func NewGoCodeGenerator(c *ygen.GeneratorConfig, goopts *GoOpts) *GoCodeGenerator { - cg := &GoCodeGenerator{} +func NewCodeGenerator(c *ygen.GeneratorConfig, goopts *GoOpts) *CodeGenerator { + cg := &CodeGenerator{} if c != nil { cg.Config = *c @@ -173,19 +173,19 @@ func checkForBinaryKeys(dir *ygen.ParsedDirectory) []error { return errs } -// GenerateGoCode takes a slice of strings containing the path to a set of YANG +// Generate takes a slice of strings containing the path to a set of YANG // files which contain YANG modules, and a second slice of strings which // specifies the set of paths that are to be searched for associated models (e.g., // modules that are included by the specified set of modules, or submodules of those // modules). It extracts the set of modules that are to be generated, and returns -// a GeneratedGoCode struct which contains: +// a GeneratedCode struct which contains: // 1. A struct definition for each container or list that is within the specified // set of models. // 2. Enumerated values which correspond to the set of enumerated entities (leaves // of type enumeration, identities, typedefs that reference an enumeration) // within the specified models. // If errors are encountered during code generation, an error is returned. -func (cg *GoCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*GeneratedGoCode, util.Errors) { +func (cg *CodeGenerator) Generate(yangFiles, includePaths []string) (*GeneratedCode, util.Errors) { opts := ygen.IROptions{ ParseOptions: cg.Config.ParseOptions, TransformationOptions: cg.Config.TransformationOptions, @@ -324,7 +324,7 @@ func (cg *GoCodeGenerator) GenerateGoCode(yangFiles, includePaths []string) (*Ge return nil, codegenErr } - return &GeneratedGoCode{ + return &GeneratedCode{ CommonHeader: commonHeader, OneOffHeader: oneoffHeader, Structs: structSnippets, diff --git a/gogen/codegen_test.go b/gogen/codegen_test.go index 4daae74b7..bdf7d308f 100644 --- a/gogen/codegen_test.go +++ b/gogen/codegen_test.go @@ -39,11 +39,11 @@ type yangTestCase struct { wantSchemaFile string // wantSchemaFile is the path to the schema JSON that the output of the test should be compared to. } -// TestSimpleStructs tests the processModules, GenerateGoCode and writeGoCode +// TestSimpleStructs tests the processModules, Generate and writeGoCode // functions. It takes the set of YANG modules described in the slice of // yangTestCases and generates the struct code for them, comparing the output // to the wantStructsCodeFile. In order to simplify the files that are used, -// the GenerateGoCode structs are concatenated before comparison with the +// the Generate structs are concatenated before comparison with the // expected output. If the generated code matches the expected output, it is // run against the Go parser to ensure that the code is valid Go - this is // expected, but it ensures that the input file does not contain Go which is @@ -866,7 +866,7 @@ func TestSimpleStructs(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - genCode := func() (*GeneratedGoCode, string, map[string]interface{}, error) { + genCode := func() (*GeneratedCode, string, map[string]interface{}, error) { // Set defaults within the supplied configuration for these tests. if tt.inConfig.Caller == "" { // Set the name of the caller explicitly to avoid issues when @@ -876,9 +876,9 @@ func TestSimpleStructs(t *testing.T) { tt.inConfig.StoreRawSchema = true tt.inConfig.ParseOptions.ExcludeModules = tt.inExcludeModules - cg := NewGoCodeGenerator(&tt.inConfig, &tt.inGoOptions) + cg := NewCodeGenerator(&tt.inConfig, &tt.inGoOptions) - gotGeneratedCode, errs := cg.GenerateGoCode(tt.inFiles, tt.inIncludePaths) + gotGeneratedCode, errs := cg.Generate(tt.inFiles, tt.inIncludePaths) var err error if len(errs) > 0 { err = fmt.Errorf("%w", errs) @@ -937,7 +937,7 @@ func TestSimpleStructs(t *testing.T) { if !cmp.Equal(gotJSON, wantJSON) { diff, _ := testutil.GenerateUnifiedDiff(string(wantSchema), string(gotGeneratedCode.RawJSONSchema)) - t.Fatalf("%s: GenerateGoCode(%v, %v), Config: %+v, did not return correct JSON (file: %v), diff: \n%s", tt.name, tt.inFiles, tt.inIncludePaths, tt.inConfig, tt.wantSchemaFile, diff) + t.Fatalf("%s: Generate(%v, %v), Config: %+v, did not return correct JSON (file: %v), diff: \n%s", tt.name, tt.inFiles, tt.inIncludePaths, tt.inConfig, tt.wantSchemaFile, diff) } } @@ -953,7 +953,7 @@ func TestSimpleStructs(t *testing.T) { // two code snippets such that this is simpler to debug // in the test output. diff, _ := testutil.GenerateUnifiedDiff(wantCode, gotCode) - t.Errorf("%s: GenerateGoCode(%v, %v), Config: %+v, did not return correct code (file: %v), diff:\n%s", + t.Errorf("%s: Generate(%v, %v), Config: %+v, did not return correct code (file: %v), diff:\n%s", tt.name, tt.inFiles, tt.inIncludePaths, tt.inConfig, tt.wantStructsCodeFile, diff) } diff --git a/gogen/gogen.go b/gogen/gogen.go index c32d9301d..36cbd14e9 100644 --- a/gogen/gogen.go +++ b/gogen/gogen.go @@ -20,6 +20,7 @@ import ( "io" "sort" "strings" + "text/template" "github.com/openconfig/gnmi/errlist" gpb "github.com/openconfig/gnmi/proto/gnmi" @@ -294,9 +295,15 @@ type generatedDefaultMethod struct { Leaves []*generatedLeafGetter } +// mustMakeTemplate generates a template.Template for a particular named source +// template; with a common set of helper functions. +func mustMakeTemplate(name, src string) *template.Template { + return template.Must(template.New(name).Funcs(igenutil.TemplateHelperFunctions).Parse(src)) +} + var ( // goCommonHeaderTemplate is populated and output at the top of the generated code package - goCommonHeaderTemplate = igenutil.MustMakeTemplate("commonHeader", ` + goCommonHeaderTemplate = mustMakeTemplate("commonHeader", ` {{- /**/ -}} /* Package {{ .PackageName }} is a generated package which contains definitions @@ -335,7 +342,7 @@ import ( // goOneOffHeaderTemplate defines the template for package code that should // be output in only one file. - goOneOffHeaderTemplate = igenutil.MustMakeTemplate("oneoffHeader", ` + goOneOffHeaderTemplate = mustMakeTemplate("oneoffHeader", ` // {{ .BinaryTypeName }} is a type that is used for fields that have a YANG type of // binary. It is used such that binary fields can be distinguished from // leaf-lists of uint8s (which are mapped to []uint8, equivalent to @@ -476,7 +483,7 @@ var ΓModelData = []*gpb.ModelData{ // them. The logic populating the generatedGoStruct handles non-scalar child schema // nodes: leaf-lists are mapped into slices; lists are mapped into a map or slice of // structs; and containers are mapped into structs. - goStructTemplate = igenutil.MustMakeTemplate("struct", ` + goStructTemplate = mustMakeTemplate("struct", ` // {{ .StructName }} represents the {{ .YANGPath }} YANG schema element. type {{ .StructName }} struct { {{- range $idx, $field := .Fields }} @@ -497,7 +504,7 @@ func (*{{ .StructName }}) IsYANGGoStruct() {} // goStructValidatorTemplate takes an input generatedGoStruct, which contains // a definition of a YANG schema node, and generates the Go validation code // from it. - goStructValidatorTemplate = igenutil.MustMakeTemplate("structValidator", ` + goStructValidatorTemplate = mustMakeTemplate("structValidator", ` // Validate validates s against the YANG schema corresponding to its type. func (t *{{ .StructName }}) ΛValidate(opts ...ygot.ValidationOption) error { if err := ytypes.Validate(SchemaTree["{{ .StructName }}"], t, opts...); err != nil { @@ -509,7 +516,7 @@ func (t *{{ .StructName }}) ΛValidate(opts ...ygot.ValidationOption) error { // goStructValidatorProxyTemplate creates a proxy for the ΛValidate function with the // user definable name. - goStructValidatorProxyTemplate = igenutil.MustMakeTemplate("structValidatorProxy", ` + goStructValidatorProxyTemplate = mustMakeTemplate("structValidatorProxy", ` // Validate validates s against the YANG schema corresponding to its type. func (t *{{ .StructName }}) {{ .ValidateProxyFnName }}(opts ...ygot.ValidationOption) error { return t.ΛValidate(opts...) @@ -518,7 +525,7 @@ func (t *{{ .StructName }}) {{ .ValidateProxyFnName }}(opts ...ygot.ValidationOp // goContainerGetterTemplate defines a template that generates a getter function // for the field of a generated struct. It is generated only for YANG containers. - goContainerGetterTemplate = igenutil.MustMakeTemplate("getContainer", ` + goContainerGetterTemplate = mustMakeTemplate("getContainer", ` // Get{{ .Field.Name }} returns the value of the {{ .Field.Name }} struct pointer // from {{ .StructName }}. If the receiver or the field {{ .Field.Name }} is nil, nil // is returned such that the Get* methods can be safely chained. @@ -533,7 +540,7 @@ func (t *{{ .StructName }}) Get{{ .Field.Name }}() {{ .Field.Type }} { // goGetOrCreateStructTemplate is a template that generates a getter // function for a struct field of the receiver struct. The function generated // creates the field if it does not exist. - goGetOrCreateStructTemplate = igenutil.MustMakeTemplate("getOrCreateStruct", ` + goGetOrCreateStructTemplate = mustMakeTemplate("getOrCreateStruct", ` // GetOrCreate{{ .Field.Name }} retrieves the value of the {{ .Field.Name }} field // or returns the existing field if it already exists. func (t *{{ .StructName }}) GetOrCreate{{ .Field.Name }}() {{ .Field.Type }} { @@ -566,7 +573,7 @@ func (t *{{ .StructName }}) GetOrCreate{{ .Field.Name }}() {{ .Field.Type }} { // // This struct is then used as the key of the map representing the list L, in // the generated struct representing the container A. - goListKeyTemplate = igenutil.MustMakeTemplate("listkey", ` + goListKeyTemplate = mustMakeTemplate("listkey", ` // {{ .KeyStructName }} represents the key for list {{ .ListName }} of element {{ .ParentPath }}. type {{ .KeyStructName }} struct { {{- range $idx, $key := .Keys }} @@ -578,7 +585,7 @@ type {{ .KeyStructName }} struct { // goEnumDefinitionTemplate takes an input generatedGoEnumeration struct // and outputs the Go code that is associated with the enumerated type to be // generated. - goEnumDefinitionTemplate = igenutil.MustMakeTemplate("enumDefinition", ` + goEnumDefinitionTemplate = mustMakeTemplate("enumDefinition", ` // E_{{ .EnumerationPrefix }} is a derived int64 type which is used to represent // the enumerated node {{ .EnumerationPrefix }}. An additional value named // {{ .EnumerationPrefix }}_UNSET is added to the enumeration which is used as @@ -612,7 +619,7 @@ const ( // of a struct within a keyed YANG list, and populates the map key, and the // key fields of the list's struct according to the input arguments of the // function. - goNewListMemberTemplate = igenutil.MustMakeTemplate("newListEntry", ` + goNewListMemberTemplate = mustMakeTemplate("newListEntry", ` // New{{ .ListName }} creates a new entry in the {{ .ListName }} list of the // {{ .Receiver}} struct. The keys of the list are populated from the input // arguments. @@ -672,7 +679,7 @@ func (t *{{ .Receiver }}) New{{ .ListName }}( // goListGetterTemplate defines a template for a function that, for a particular // list key, gets an existing map value. - goListGetterTemplate = igenutil.MustMakeTemplate("getList", ` + goListGetterTemplate = mustMakeTemplate("getList", ` // Get{{ .ListName }} retrieves the value with the specified key from // the {{ .ListName }} map field of {{ .Receiver }}. If the receiver is nil, or // the specified key is not present in the list, nil is returned such that Get* @@ -711,7 +718,7 @@ func (t *{{ .Receiver }}) Get{{ .ListName }}( // goGetOrCreateListTemplate defines a template for a function that, for a // particular list key, gets an existing map value, or creates it if it doesn't // exist. - goGetOrCreateListTemplate = igenutil.MustMakeTemplate("getOrCreateList", ` + goGetOrCreateListTemplate = mustMakeTemplate("getOrCreateList", ` // GetOrCreate{{ .ListName }} retrieves the value with the specified keys from // the receiver {{ .Receiver }}. If the entry does not exist, then it is created. // It returns the existing or new list member. @@ -754,7 +761,7 @@ func (t *{{ .Receiver }}) GetOrCreate{{ .ListName }}( // goLeafGetterTemplate defines a template for a function that, for a // particular leaf, generates a getter method. - goLeafGetterTemplate = igenutil.MustMakeTemplate("getLeaf", ` + goLeafGetterTemplate = mustMakeTemplate("getLeaf", ` // Get{{ .Name }} retrieves the value of the leaf {{ .Name }} from the {{ .Receiver }} // struct. If the field is unset but has a default value in the YANG schema, // then the default value will be returned. @@ -778,7 +785,7 @@ func (t *{{ .Receiver }}) Get{{ .Name }}() {{ .Type }} { // goDefaultMethodTemplate is a template for generating a PopulateDefaults method // for a GoStruct that recursively populates default values within the subtree. - goDefaultMethodTemplate = igenutil.MustMakeTemplate("populateDefaults", ` + goDefaultMethodTemplate = mustMakeTemplate("populateDefaults", ` // PopulateDefaults recursively populates unset leaf fields in the {{ .Receiver }} // with default values as specified in the YANG schema, instantiating any nil // container fields. @@ -813,7 +820,7 @@ func (t *{{ .Receiver }}) PopulateDefaults() { // goDeleteListTemplate defines a template for a function that, for a // particular list key, deletes an existing map value. - goDeleteListTemplate = igenutil.MustMakeTemplate("deleteList", ` + goDeleteListTemplate = mustMakeTemplate("deleteList", ` // Delete{{ .ListName }} deletes the value with the specified keys from // the receiver {{ .Receiver }}. If there is no such element, the function // is a no-op. @@ -846,7 +853,7 @@ func (t *{{ .Receiver }}) Delete{{ .ListName }}( // within values by default, we must invert the "IsScalarField" check to // ensure that we dereference elements that are pointers in the generated // code. - goListAppendTemplate = igenutil.MustMakeTemplate("appendList", ` + goListAppendTemplate = mustMakeTemplate("appendList", ` // Append{{ .ListName }} appends the supplied {{ .ListType }} struct to the // list {{ .ListName }} of {{ .Receiver }}. If the key value(s) specified in // the supplied {{ .ListType }} already exist in the list, an error is @@ -910,7 +917,7 @@ func (t *{{ .Receiver }}) Append{{ .ListName }}(v *{{ .ListType }}) error { // goListMemberRenameTemplate provides a template for a function which renames // an entry within a list. It is used to generate functions for each list within // a generated Go struct. - goListMemberRenameTemplate = igenutil.MustMakeTemplate("renameListEntry", ` + goListMemberRenameTemplate = mustMakeTemplate("renameListEntry", ` // Rename{{ .ListName }} renames an entry in the list {{ .ListName }} within // the {{ .Receiver }} struct. The entry with key oldK is renamed to newK updating // the key within the value. @@ -958,7 +965,7 @@ func (t *{{ .Receiver }}) Rename{{ .ListName }}( // goKeyMapTemplate defines the template for a function that is generated for a YANG // list type. It returns a map[string]interface{} keyed by the YANG leaf identifier of each // key leaf, and containing their values within the struct. - goKeyMapTemplate = igenutil.MustMakeTemplate("keyHelper", ` + goKeyMapTemplate = mustMakeTemplate("keyHelper", ` // ΛListKeyMap returns the keys of the {{ .Receiver }} struct, which is a YANG list entry. func (t *{{ .Receiver }}) ΛListKeyMap() (map[string]interface{}, error) { {{- range $key := .Keys -}}{{ if $key.IsPtr }} @@ -980,7 +987,7 @@ func (t *{{ .Receiver }}) ΛListKeyMap() (map[string]interface{}, error) { // goEnumMapTemplate provides a template to output a constant map which // can be used to resolve the string value of any enumeration within the // schema. - goEnumMapTemplate = igenutil.MustMakeTemplate("enumMap", ` + goEnumMapTemplate = mustMakeTemplate("enumMap", ` // ΛEnum is a map, keyed by the name of the type defined for each enum in the // generated Go code, which provides a mapping between the constant int64 value // of each value of the enumeration, and the string that is used to represent it @@ -1004,7 +1011,7 @@ var ΛEnum = map[string]map[int64]ygot.EnumDefinition{ // goEnumTypeMapTemplate provides a template to output a constant map which // can be used to resolve a schemapath to the set of enumerated types that // are valid for the leaf or leaf-list defined at the path specified. - goEnumTypeMapTemplate = igenutil.MustMakeTemplate("enumTypeMap", ` + goEnumTypeMapTemplate = mustMakeTemplate("enumTypeMap", ` // ΛEnumTypes is a map, keyed by a YANG schema path, of the enumerated types that // correspond with the leaf. The type is represented as a reflect.Type. The naming // of the map ensures that there are no clashes with valid YANG identifiers. @@ -1024,7 +1031,7 @@ func initΛEnumTypes(){ // goEnumTypeMapAccessTemplate provides a template to output an accessor // function with a generated struct as receiver, it returns the enum type // map associated with the generated code. - goEnumTypeMapAccessTemplate = igenutil.MustMakeTemplate("enumTypeMapAccessor", ` + goEnumTypeMapAccessTemplate = mustMakeTemplate("enumTypeMapAccessor", ` // ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types // that are included in the generated code. func (t *{{ .StructName }}) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes } @@ -1033,7 +1040,7 @@ func (t *{{ .StructName }}) ΛEnumTypeMap() map[string][]reflect.Type { return // goBelongingModuleTemplate provides a template to output a // function that has a generated struct as receiver, and returns the // name of the module in which namespace the generated struct belongs. - goBelongingModuleTemplate = igenutil.MustMakeTemplate("belongingModuleMethod", ` + goBelongingModuleTemplate = mustMakeTemplate("belongingModuleMethod", ` // ΛBelongingModule returns the name of the module that defines the namespace // of {{ .StructName }}. func (*{{ .StructName }}) ΛBelongingModule() string { @@ -1044,7 +1051,7 @@ func (*{{ .StructName }}) ΛBelongingModule() string { // schemaVarTemplate provides a template to output a constant byte // slice which contains the serialised schema of the YANG modules for // which code generation was performed. - schemaVarTemplate = igenutil.MustMakeTemplate("schemaVar", ` + schemaVarTemplate = mustMakeTemplate("schemaVar", ` var ( // {{ .VarName }} is a byte slice contain a gzip compressed representation of the // YANG schema from which the Go code was generated. When uncompressed the @@ -1062,7 +1069,7 @@ var ( // unionTypeTemplate outputs the type that corresponds to a multi-type union // in the YANG schema. - unionTypeTemplate = igenutil.MustMakeTemplate("unionType", ` + unionTypeTemplate = mustMakeTemplate("unionType", ` // {{ .Name }} is an interface that is implemented by valid types for the union // for the leaf {{ .LeafPath }} within the YANG schema. type {{ .Name }} interface { @@ -1086,7 +1093,7 @@ func (*{{ $intfName }}_{{ $typeName }}) Is_{{ $intfName }}() {} // unionHelperTemplate defines a template that defines a helper method // with a particular receiver type that allows an input type to be converted // to its corresponding type in the union type. - unionHelperTemplate = igenutil.MustMakeTemplate("unionHelper", ` + unionHelperTemplate = mustMakeTemplate("unionHelper", ` {{- $intfName := .Name }} {{- $path := .LeafPath }} // To_{{ .Name }} takes an input interface{} and attempts to convert it to a struct @@ -1112,7 +1119,7 @@ func (t *{{ .ParentReceiver }}) To_{{ .Name }}(i interface{}) ({{ .Name }}, erro // unionTypeSimpleTemplate outputs the type that corresponds to a multi-type union // in the YANG schema. It does so by enhancing generated typedefs. - unionTypeSimpleTemplate = igenutil.MustMakeTemplate("unionTypeSimple", ` + unionTypeSimpleTemplate = mustMakeTemplate("unionTypeSimple", ` // {{ .Name }} is an interface that is implemented by valid types for the union // for the leaf {{ .LeafPath }} within the YANG schema. // Union type can be one of [{{ .SubtypeDocumentation }}]. @@ -1132,7 +1139,7 @@ func ({{ $typeName }}) Documentation_for_{{ $intfName }}() {} // unionHelperSimpleTemplate defines a template that defines a helper method // with a particular receiver type that allows an input type to be converted // to its corresponding type in the union type. - unionHelperSimpleTemplate = igenutil.MustMakeTemplate("unionHelperSimple", ` + unionHelperSimpleTemplate = mustMakeTemplate("unionHelperSimple", ` {{- $intfName := .Name }} {{- $path := .LeafPath }} // To_{{ .Name }} takes an input interface{} and attempts to convert it to a struct @@ -1180,7 +1187,7 @@ func (t *{{ .ParentReceiver }}) To_{{ .Name }}(i interface{}) ({{ .Name }}, erro // The header returned is split into two strings, the common header is a header that // should be used for all files within the output package. The one off header should // be included in only one file of the package. -func writeGoHeader(yangFiles, includePaths []string, cfg *GoCodeGenerator, rootName string, modelData []*gpb.ModelData) (string, string, error) { +func writeGoHeader(yangFiles, includePaths []string, cfg *CodeGenerator, rootName string, modelData []*gpb.ModelData) (string, string, error) { // Determine the running binary's name. if cfg.Config.Caller == "" { cfg.Config.Caller = genutil.CallerName() diff --git a/internal/igenutil/genutil.go b/internal/igenutil/genutil.go index 2e77d2799..8021e0ae0 100644 --- a/internal/igenutil/genutil.go +++ b/internal/igenutil/genutil.go @@ -72,12 +72,6 @@ var ( } ) -// MustMakeTemplate generates a template.Template for a particular named source -// template; with a common set of helper functions. -func MustMakeTemplate(name, src string) *template.Template { - return template.Must(template.New(name).Funcs(TemplateHelperFunctions).Parse(src)) -} - // IsFakeRoot checks whether a given entry is the generated fake root. func IsFakeRoot(e *yang.Entry) bool { return e != nil && e.Node != nil && e.Node.NName() == RootElementNodeName diff --git a/ygen/protogen.go b/ygen/protogen.go index 0e68764c1..5782c464b 100644 --- a/ygen/protogen.go +++ b/ygen/protogen.go @@ -22,6 +22,7 @@ import ( "regexp" "sort" "strings" + "text/template" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" @@ -163,9 +164,15 @@ type proto3Header struct { var disallowedInProtoIDRegexp = regexp.MustCompile(`[^a-zA-Z0-9_]`) +// mustMakeTemplate generates a template.Template for a particular named source +// template; with a common set of helper functions. +func mustMakeTemplate(name, src string) *template.Template { + return template.Must(template.New(name).Funcs(igenutil.TemplateHelperFunctions).Parse(src)) +} + var ( // protoHeaderTemplate is populated and output at the top of the protobuf code output. - protoHeaderTemplate = igenutil.MustMakeTemplate("header", ` + protoHeaderTemplate = mustMakeTemplate("header", ` {{- /**/ -}} // {{ .PackageName }} is generated by {{ .CallerName }} as a protobuf // representation of a YANG schema. @@ -203,7 +210,7 @@ option go_package = "{{ .GoPackageName }}"; // protoMessageTemplate is populated for each entity that is mapped to a message // within the output protobuf. - protoMessageTemplate = igenutil.MustMakeTemplate("msg", ` + protoMessageTemplate = mustMakeTemplate("msg", ` {{ if .PathComment -}} // {{ .Name }} represents the {{ .YANGPath }} YANG schema element. {{ end -}} @@ -246,7 +253,7 @@ message {{ .Name }} { // protoEnumTemplate is the template used to generate enumerations that are // not within a message. Such enums are used where there are referenced YANG // identity nodes, and where there are typedefs which include an enumeration. - protoEnumTemplate = igenutil.MustMakeTemplate("enum", ` + protoEnumTemplate = mustMakeTemplate("enum", ` // {{ .Name }} represents an enumerated type generated for the {{ .Description }}. enum {{ .Name }} { {{- range $i, $val := .Values }} From 5566cc95046bca609a856dcb2ace4e232c73c853 Mon Sep 17 00:00:00 2001 From: Wen Bo Li <50884368+wenovus@users.noreply.github.com> Date: Thu, 9 Jun 2022 09:58:10 -0700 Subject: [PATCH 19/19] Split Proto generation into `protogen` package. (#696) * Split Proto generation into `protogen` package. The change includes the following notable changes: * protogen/codegen.go: Instead of using `ygen.NewYANGCodeGenerator` as the struct for all code generation from the single `ygen` package, `protogen.NewProtoCodeGenerator` is created for Proto generation. `ProtoOpts` is moved to `protogen`. * protogen/genir_test: Since IR generation requires a suitable `LangMapper` implementation, the tests using `ProtoLangMapper` are moved to here. * protogen/protogen.go, protoelements.go: These are moved to this package. * integration_tests/generate_errs_test.go: Moved `TestGenerateErrs` out of ygen. * remove unused code * remove unused variable * add coverpkg * Try again with coverpkg * Try again with coverpkg * Fix syntax error * coverpkg=all * coverpkg=./... * coverpkg filter out irrelevant packages * try to fix coverpkg * try to fix coverpkg * use bash instead of coveralls * Remove 'proto' from various exported names * GenerateProto3 -> Generate * Delete unused variables and rephrase package comments for ygen. (#697) * Delete unused variables and rephrase package comments for ygen. * Fix `IdentityType` classification bug. (#699) * Fix `IdentityType` classification bug. Right now all typedefs are automatically categorized as `DerivedEnumerationType`; this is wrong because identity types could also be in a typedef. Fixed this bug and added tests. * Remove debugging statement * Add coverage for proto generation of typedef identityref * Run go generate ./... for CI check (#703) * Run go generate ./... for CI check * Regenerate exampleoc but don't depend on it for tests * Delete `StoreRawSchema` from `GeneratorConfig` since it's not being used (#705) * Delete `StoreRawSchema` from `GeneratorConfig` since it's not being used I don't see the value of why we need this flag: is it to save memory? For `exampleoc` this variable took 27MB, a negligible amount. * Reorganize Code Generation flags and Delete `ygen.GeneratorConfig` (#706) * ygen now only has `IROptions`. * `CodeGenerator` in gogen/protogen now carry IROptions and their language-specific post-IR generation options. * Code generation specific flags that are not used during IR generation are moved to `gogen` and `protogen`, duplicating if used by both (e.g. CallerName). * `SkipEnumDeduplication` is moved from ParseOptions to TransformationOptions. --- .github/workflows/go.yml | 6 +- Makefile | 32 +- demo/getting_started/interfaces_test.go | 20 +- demo/protobuf_getting_started/demo.go | 2 + demo/protobuf_getting_started/update.sh | 2 - generator/generator.go | 44 +- gogen/codegen.go | 58 +- gogen/codegen_test.go | 1082 +++++++++-------- gogen/genir_test.go | 31 + gogen/goelements.go | 6 +- gogen/gogen.go | 37 +- gogen/gogen_test.go | 28 +- integration_tests/generate_errs_test.go | 79 ++ .../testdata/errors/bad-module.yang | 0 .../testdata/errors/missing-import.yang | 0 .../errors/subdir/module-in-subdir.yang | 0 proto_generator/protogenerator.go | 39 +- protogen/codegen.go | 259 ++++ protogen/codegen_test.go | 523 ++++++++ {ygen => protogen}/genir_test.go | 349 +++--- protogen/helpers.go | 21 + {ygen => protogen}/protoelements.go | 88 +- {ygen => protogen}/protoelements_test.go | 128 +- {ygen => protogen}/protogen.go | 83 +- {ygen => protogen}/protogen_test.go | 775 ++++++------ .../proto/cross-ref-src.formatted-txt | 0 .../testdata/proto/cross-ref-src.yang | 0 .../testdata/proto/cross-ref-target.yang | 0 .../enum-union.compress.enums.formatted-txt | 0 .../proto/enum-union.compress.formatted-txt | 0 ...uded-config-false.compressed.formatted-txt | 0 ...alse.config_false.compressed.formatted-txt | 0 ...ed-config-false.uncompressed.formatted-txt | 0 .../testdata/proto/fakeroot-multimod-one.yang | 0 .../testdata/proto/fakeroot-multimod-two.yang | 0 .../proto/fakeroot-multimod.formatted-txt | 0 ...ed-messages.compressed.enums.formatted-txt | 0 ...s.compressed.nested_messages.formatted-txt | 0 .../proto/nested-messages.enums.formatted-txt | 0 ...ted-messages.nested_messages.formatted-txt | 0 .../nested-messages.openconfig.formatted-txt | 0 .../testdata/proto/nested-messages.yang | 0 .../testdata/proto/proto-anydata-test.yang | 0 .../proto-enums-addid.enums.formatted-txt | 7 + .../proto/proto-enums-addid.formatted-txt | 1 + .../testdata/proto/proto-enums-addid.yang | 0 .../proto/proto-enums.enums.formatted-txt | 6 + .../testdata/proto/proto-enums.formatted-txt | 1 + .../testdata/proto/proto-enums.yang | 11 + ...test-a.compress.parent.child.formatted-txt | 0 ...proto-test-a.compress.parent.formatted-txt | 0 .../proto-test-a.nocompress.formatted-txt | 0 ...st-a.nocompress.parent.child.formatted-txt | 0 ...oto-test-a.nocompress.parent.formatted-txt | 0 .../testdata/proto/proto-test-a.yang | 0 ...proto-test-b.compress.device.formatted-txt | 0 .../proto/proto-test-b.compress.formatted-txt | 0 .../testdata/proto/proto-test-b.yang | 0 .../proto/proto-test-c.enums.formatted-txt | 0 ...-c.proto-test-c.elists.elist.formatted-txt | 0 ...o-test-c.proto-test-c.elists.formatted-txt | 0 ...o-test-c.proto-test-c.entity.formatted-txt | 0 .../proto-test-c.proto-test-c.formatted-txt | 0 .../testdata/proto/proto-test-c.yang | 0 ...to-test-d.uncompressed.enums.formatted-txt | 0 ...-d.uncompressed.proto-test-d.formatted-txt | 0 ...compressed.proto-test-d.test.formatted-txt | 0 .../testdata/proto/proto-test-d.yang | 0 ...to-test-e.uncompressed.enums.formatted-txt | 0 ....proto-test-e.animals.animal.formatted-txt | 0 ...pressed.proto-test-e.animals.formatted-txt | 0 ...compressed.proto-test-e.bars.formatted-txt | 0 ...ressed.proto-test-e.foos.foo.formatted-txt | 0 ...compressed.proto-test-e.foos.formatted-txt | 0 ...-e.uncompressed.proto-test-e.formatted-txt | 0 ...compressed.proto-test-e.test.formatted-txt | 0 .../testdata/proto/proto-test-e.yang | 0 .../testdata/proto/proto-test-f.yang | 0 .../proto-test-g.proto-test-g.formatted-txt | 0 .../testdata/proto/proto-test-g.yang | 0 ...st-key.compressed.openconfig.formatted-txt | 0 ...ed.openconfig.routing_policy.formatted-txt | 0 ...nconfig.proto_union_list_key.formatted-txt | 0 ...nion_list_key.routing_policy.formatted-txt | 0 ..._key.routing_policy.policies.formatted-txt | 0 ...uting_policy.policies.policy.formatted-txt | 0 ...list_key.routing_policy.sets.formatted-txt | 0 .../testdata/proto/proto-union-list-key.yang | 0 ..._key.uncompressed.openconfig.formatted-txt | 0 .../proto/proto_anydata_test.e.formatted-txt | 0 .../proto/proto_anydata_test.formatted-txt | 0 ...ncompressed.proto_test_f.a.c.formatted-txt | 0 ....uncompressed.proto_test_f.a.formatted-txt | 0 ..._f.uncompressed.proto_test_f.formatted-txt | 0 .../proto/union-list-key.enums.formatted-txt | 0 .../proto/union-list-key.formatted-txt | 0 ...nion-list-key.union_list_key.formatted-txt | 0 .../testdata/proto/union-list-key.yang | 0 testdata/modules/openconfig-complex.yang | 10 + ygen/codegen.go | 339 +----- ygen/codegen_test.go | 552 --------- ygen/directory.go | 12 - ygen/enumgen.go | 16 +- ygen/enumgen_test.go | 2 +- ygen/genir.go | 7 +- ygen/genstate_test.go | 20 +- ygen/helpers.go | 36 +- ypathgen/pathgen.go | 6 +- 108 files changed, 2529 insertions(+), 2189 deletions(-) create mode 100644 integration_tests/generate_errs_test.go rename {ygen => integration_tests}/testdata/errors/bad-module.yang (100%) rename {ygen => integration_tests}/testdata/errors/missing-import.yang (100%) rename {ygen => integration_tests}/testdata/errors/subdir/module-in-subdir.yang (100%) create mode 100644 protogen/codegen.go create mode 100644 protogen/codegen_test.go rename {ygen => protogen}/genir_test.go (85%) create mode 100644 protogen/helpers.go rename {ygen => protogen}/protoelements.go (89%) rename {ygen => protogen}/protoelements_test.go (84%) rename {ygen => protogen}/protogen.go (94%) rename {ygen => protogen}/protogen_test.go (75%) rename {ygen => protogen}/testdata/proto/cross-ref-src.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/cross-ref-src.yang (100%) rename {ygen => protogen}/testdata/proto/cross-ref-target.yang (100%) rename {ygen => protogen}/testdata/proto/enum-union.compress.enums.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/enum-union.compress.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/excluded-config-false.compressed.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/excluded-config-false.config_false.compressed.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/excluded-config-false.uncompressed.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/fakeroot-multimod-one.yang (100%) rename {ygen => protogen}/testdata/proto/fakeroot-multimod-two.yang (100%) rename {ygen => protogen}/testdata/proto/fakeroot-multimod.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/nested-messages.compressed.enums.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/nested-messages.compressed.nested_messages.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/nested-messages.enums.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/nested-messages.nested_messages.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/nested-messages.openconfig.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/nested-messages.yang (100%) rename {ygen => protogen}/testdata/proto/proto-anydata-test.yang (100%) rename {ygen => protogen}/testdata/proto/proto-enums-addid.enums.formatted-txt (77%) rename {ygen => protogen}/testdata/proto/proto-enums-addid.formatted-txt (94%) rename {ygen => protogen}/testdata/proto/proto-enums-addid.yang (100%) rename {ygen => protogen}/testdata/proto/proto-enums.enums.formatted-txt (80%) rename {ygen => protogen}/testdata/proto/proto-enums.formatted-txt (93%) rename {ygen => protogen}/testdata/proto/proto-enums.yang (86%) rename {ygen => protogen}/testdata/proto/proto-test-a.compress.parent.child.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-a.compress.parent.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-a.nocompress.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-a.nocompress.parent.child.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-a.nocompress.parent.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-a.yang (100%) rename {ygen => protogen}/testdata/proto/proto-test-b.compress.device.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-b.compress.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-b.yang (100%) rename {ygen => protogen}/testdata/proto/proto-test-c.enums.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-c.proto-test-c.elists.elist.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-c.proto-test-c.elists.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-c.proto-test-c.entity.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-c.proto-test-c.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-c.yang (100%) rename {ygen => protogen}/testdata/proto/proto-test-d.uncompressed.enums.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-d.uncompressed.proto-test-d.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-d.uncompressed.proto-test-d.test.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-d.yang (100%) rename {ygen => protogen}/testdata/proto/proto-test-e.uncompressed.enums.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.animal.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-e.uncompressed.proto-test-e.bars.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.foo.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-e.uncompressed.proto-test-e.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-e.uncompressed.proto-test-e.test.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-e.yang (100%) rename {ygen => protogen}/testdata/proto/proto-test-f.yang (100%) rename {ygen => protogen}/testdata/proto/proto-test-g.proto-test-g.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-test-g.yang (100%) rename {ygen => protogen}/testdata/proto/proto-union-list-key.compressed.openconfig.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-union-list-key.compressed.openconfig.routing_policy.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.policy.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.sets.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto-union-list-key.yang (100%) rename {ygen => protogen}/testdata/proto/proto-union-list_key.uncompressed.openconfig.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto_anydata_test.e.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto_anydata_test.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.c.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/proto_test_f.uncompressed.proto_test_f.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/union-list-key.enums.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/union-list-key.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/union-list-key.union_list_key.formatted-txt (100%) rename {ygen => protogen}/testdata/proto/union-list-key.yang (100%) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index e0470c9b7..a3a8bf26b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -118,6 +118,10 @@ jobs: test-go-build $f $go_file done + - name: Make sure exampleoc doesn't error or panic + run: go generate ./... + working-directory: go/src/github.com/openconfig/ygot + static_analysis: name: Static Analysis runs-on: ubuntu-latest @@ -152,7 +156,7 @@ jobs: # Overwrite results for gogen/protogen since they also cover ygen's # code. go test -covermode count -coverprofile profile.coverprofile -outputdir gogen -coverpkg github.com/openconfig/ygot/gogen,github.com/openconfig/ygot/ygen github.com/openconfig/ygot/gogen - #go test -covermode count -coverprofile profile.coverprofile -outputdir protogen -coverpkg github.com/openconfig/ygot/protogen,github.com/openconfig/ygot/ygen github.com/openconfig/ygot/protogen + go test -covermode count -coverprofile profile.coverprofile -outputdir protogen -coverpkg github.com/openconfig/ygot/protogen,github.com/openconfig/ygot/ygen github.com/openconfig/ygot/protogen echo 'mode: count' > concatenated.coverprofile for p in $pkgs; do diff --git a/Makefile b/Makefile index 7f448505d..a0f4e33c9 100644 --- a/Makefile +++ b/Makefile @@ -1,30 +1,20 @@ # ygot Makefile # -# This makefile is used by Travis CI to run tests against the ygot library. +# This makefile is used by GitHub Actions CI to run tests against the ygot library. # -ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) - test: go test ./... generate: - cd ${ROOT_DIR}/demo/getting_started && SRCDIR=${ROOT_DIR} go generate - cd ${ROOT_DIR}/proto/ywrapper && SRCDIR=${ROOT_DIR} go generate - cd $(ROOT_DIR)/proto/yext && SRCDIR=${ROOT_DIR} go generate - cd $(ROOT_DIR)/demo/uncompressed && SRCDIR=${ROOT_DIR} go generate - cd $(ROOT_DIR)/demo/protobuf_getting_started && SRCDIR=${ROOT_DIR} ./update.sh - cd $(ROOT_DIR)/integration_tests/uncompressed && SRCDIR=${ROOT_DIR} go generate - cd $(ROOT_DIR)/integration_tests/annotations/apb && SRCDIR=${ROOT_DIR} go generate - cd $(ROOT_DIR)/integration_tests/annotations/proto2apb && SRCDIR=${ROOT_DIR} go generate + go generate ./demo/getting_started + go generate ./proto/ywrapper + go generate ./proto/yext + go generate ./demo/uncompressed + go generate ./demo/protobuf_getting_started + go generate ./integration_tests/uncompressed + go generate ./integration_tests/annotations/apb + go generate ./integration_tests/annotations/proto2apb clean: - rm -f ${ROOT_DIR}/demo/getting_started/pkg/ocdemo/oc.go - rm -f ${ROOT_DIR}/demo/uncompressed/pkg/demo/uncompressed.go -deps: - go get -t -d ./ygot - go get -t -d ./ygen - go get -t -d ./generator - go get -t -d ./proto_generator - go get -t -d ./exampleoc - go get -t -d ./ytypes - go get -t -d ./demo/gnmi_telemetry + rm -f demo/getting_started/pkg/ocdemo/oc.go + rm -f demo/uncompressed/pkg/demo/uncompressed.go install: deps generate all: clean deps generate test diff --git a/demo/getting_started/interfaces_test.go b/demo/getting_started/interfaces_test.go index 4822cad9c..dfd05941d 100644 --- a/demo/getting_started/interfaces_test.go +++ b/demo/getting_started/interfaces_test.go @@ -22,13 +22,13 @@ var TestRoot string func TestGenerateCode(t *testing.T) { tests := []struct { name string - inConfig *ygen.GeneratorConfig - inGoOpts *gogen.GoOpts + inIROpts ygen.IROptions + inGoOpts gogen.GoOpts inFiles []string inPaths []string }{{ name: "openconfig interfaces", - inConfig: &ygen.GeneratorConfig{ + inIROpts: ygen.IROptions{ ParseOptions: ygen.ParseOpts{ ExcludeModules: []string{"ietf-interfaces"}, }, @@ -36,9 +36,9 @@ func TestGenerateCode(t *testing.T) { CompressBehaviour: genutil.PreferIntendedConfig, GenerateFakeRoot: true, }, - GenerateJSONSchema: true, }, - inGoOpts: &gogen.GoOpts{ + inGoOpts: gogen.GoOpts{ + GenerateJSONSchema: true, GenerateSimpleUnions: true, }, inFiles: []string{ @@ -48,16 +48,16 @@ func TestGenerateCode(t *testing.T) { inPaths: []string{filepath.Join(TestRoot, "yang")}, }, { name: "openconfig interfaces with no compression", - inConfig: &ygen.GeneratorConfig{ + inIROpts: ygen.IROptions{ ParseOptions: ygen.ParseOpts{ ExcludeModules: []string{"ietf-interfaces"}, }, TransformationOptions: ygen.TransformationOpts{ GenerateFakeRoot: true, }, - GenerateJSONSchema: true, }, - inGoOpts: &gogen.GoOpts{ + inGoOpts: gogen.GoOpts{ + GenerateJSONSchema: true, GenerateSimpleUnions: true, }, inFiles: []string{ @@ -68,10 +68,10 @@ func TestGenerateCode(t *testing.T) { }} for _, tt := range tests { - cg := gogen.NewCodeGenerator(tt.inConfig, tt.inGoOpts) + cg := gogen.New("", tt.inIROpts, tt.inGoOpts) got, err := cg.Generate(tt.inFiles, tt.inPaths) if err != nil { - t.Errorf("%s: Generate(%v, %v): Config: %v, got unexpected error: %v", tt.name, tt.inFiles, tt.inPaths, tt.inConfig, err) + t.Errorf("%s: Generate(%v, %v): Config: %v, got unexpected error: %v", tt.name, tt.inFiles, tt.inPaths, tt.inIROpts, err) continue } diff --git a/demo/protobuf_getting_started/demo.go b/demo/protobuf_getting_started/demo.go index 05d1dbca6..15e101619 100644 --- a/demo/protobuf_getting_started/demo.go +++ b/demo/protobuf_getting_started/demo.go @@ -17,6 +17,8 @@ // to generate a Protobuf form of the OpenConfig RIB model. package main +//go:generate ./update.sh + import ( "fmt" diff --git a/demo/protobuf_getting_started/update.sh b/demo/protobuf_getting_started/update.sh index 3d02941b7..08198c171 100755 --- a/demo/protobuf_getting_started/update.sh +++ b/demo/protobuf_getting_started/update.sh @@ -17,8 +17,6 @@ go run ../../proto_generator/protogenerator.go \ -base_import_path="github.com/openconfig/ygot/demo/protobuf_getting_started/ribproto" \ -go_package_base="github.com/openconfig/ygot/demo/protobuf_getting_started/ribproto" \ -path=yang -output_dir=ribproto \ - -typedef_enum_with_defmod \ - -consistent_union_enum_names \ -enum_package_name=enums -package_name=openconfig \ -exclude_modules=ietf-interfaces \ yang/rib/openconfig-rib-bgp.yang diff --git a/generator/generator.go b/generator/generator.go index 28b6515ed..0d1f90e23 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -325,29 +325,30 @@ func main() { } // Perform the code generation. - cg := gogen.NewCodeGenerator(&ygen.GeneratorConfig{ - ParseOptions: ygen.ParseOpts{ - ExcludeModules: modsExcluded, - SkipEnumDeduplication: *skipEnumDedup, - YANGParseOptions: yang.Options{ - IgnoreSubmoduleCircularDependencies: *ignoreCircDeps, + cg := gogen.New( + "", + ygen.IROptions{ + ParseOptions: ygen.ParseOpts{ + ExcludeModules: modsExcluded, + YANGParseOptions: yang.Options{ + IgnoreSubmoduleCircularDependencies: *ignoreCircDeps, + }, + }, + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: compressBehaviour, + GenerateFakeRoot: *generateFakeRoot, + FakeRootName: *fakeRootName, + SkipEnumDeduplication: *skipEnumDedup, + ShortenEnumLeafNames: *shortenEnumLeafNames, + EnumOrgPrefixesToTrim: enumOrgPrefixesToTrim, + UseDefiningModuleForTypedefEnumNames: *useDefiningModuleForTypedefEnumNames, + EnumerationsUseUnderscores: true, }, }, - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: compressBehaviour, - IgnoreShadowSchemaPaths: *ignoreShadowSchemaPaths, - GenerateFakeRoot: *generateFakeRoot, - FakeRootName: *fakeRootName, - ShortenEnumLeafNames: *shortenEnumLeafNames, - EnumOrgPrefixesToTrim: enumOrgPrefixesToTrim, - UseDefiningModuleForTypedefEnumNames: *useDefiningModuleForTypedefEnumNames, - EnumerationsUseUnderscores: true, - }, - PackageName: *packageName, - GenerateJSONSchema: *generateSchema, - IncludeDescriptions: *includeDescriptions, - }, - &gogen.GoOpts{ + gogen.GoOpts{ + PackageName: *packageName, + GenerateJSONSchema: *generateSchema, + IncludeDescriptions: *includeDescriptions, YgotImportPath: *ygotImportPath, YtypesImportPath: *ytypesImportPath, GoyangImportPath: *goyangImportPath, @@ -364,6 +365,7 @@ func main() { GenerateSimpleUnions: *generateSimpleUnions, IncludeModelData: *includeModelData, AppendEnumSuffixForSimpleUnionEnums: *appendEnumSuffixForSimpleUnionEnums, + IgnoreShadowSchemaPaths: *ignoreShadowSchemaPaths, }, ) diff --git a/gogen/codegen.go b/gogen/codegen.go index cc9e23f55..d14264bc1 100644 --- a/gogen/codegen.go +++ b/gogen/codegen.go @@ -15,19 +15,31 @@ import ( // CodeGenerator is a structure that is used to pass arguments as to // how the output Go code should be generated. type CodeGenerator struct { - // Config stores the configuration parameters used for code generation. - Config ygen.GeneratorConfig + // Caller is the name of the binary calling the generator library, it is + // included in the header of output files for debugging purposes. If a + // string is not specified, the location of the library is utilised. + Caller string + // IROptions stores the configuration parameters used for IR generation. + IROptions ygen.IROptions // GoOptions stores a struct which stores Go code generation specific - // options for the code generaton. + // options for code generaton post IR generation. GoOptions GoOpts } // GoOpts stores Go specific options for the code generation library. type GoOpts struct { + // PackageName is the name that should be used for the generating package. + PackageName string + // GenerateJSONSchema stores a boolean which defines whether to generate + // the JSON corresponding to the YANG schema parsed to generate the + // output code. + GenerateJSONSchema bool + // IncludeDescriptions specifies that YANG entry descriptions are added + // to the JSON schema. Is false by default, to reduce the size of generated schema + IncludeDescriptions bool // SchemaVarName is the name for the variable which stores the compressed // JSON schema in the generated Go code. JSON schema output is only - // produced if the GenerateJSONSchema YANGCodeGenerator field is set to - // true. + // produced if the GenerateJSONSchema field is set to true. SchemaVarName string // GoyangImportPath specifies the path that should be used in the generated // code for importing the goyang/pkg/yang package. @@ -100,6 +112,10 @@ type GoOpts struct { // only applies when useDefiningModuleForTypedefEnumNames is also set // to true. AppendEnumSuffixForSimpleUnionEnums bool + // IgnoreShadowSchemaPaths indicates whether when OpenConfig path + // compression is enabled, that the shadowed paths are to be ignored + // while while unmarshalling. + IgnoreShadowSchemaPaths bool } // GeneratedCode contains generated code snippets that can be processed by the calling @@ -133,25 +149,19 @@ type GeneratedCode struct { // - structname - the name of the struct that was generated for the schema element. JSONSchemaCode string // RawJSONSchema stores the JSON document which is serialised and stored in JSONSchemaCode. - // It is populated only if the StoreRawSchema CodeGenerator boolean is set to true. RawJSONSchema []byte // EnumTypeMap is a Go map that allows YANG schemapaths to be mapped to reflect.Type values. EnumTypeMap string } -// NewCodeGenerator returns a new instance of the CodeGenerator +// New returns a new instance of the CodeGenerator // struct to the calling function. -func NewCodeGenerator(c *ygen.GeneratorConfig, goopts *GoOpts) *CodeGenerator { - cg := &CodeGenerator{} - - if c != nil { - cg.Config = *c - } - if goopts != nil { - cg.GoOptions = *goopts +func New(callerName string, opts ygen.IROptions, goOpts GoOpts) *CodeGenerator { + return &CodeGenerator{ + Caller: callerName, + IROptions: opts, + GoOptions: goOpts, } - - return cg } // checkForBinaryKeys returns a non-empty list of errors if the input directory @@ -187,8 +197,8 @@ func checkForBinaryKeys(dir *ygen.ParsedDirectory) []error { // If errors are encountered during code generation, an error is returned. func (cg *CodeGenerator) Generate(yangFiles, includePaths []string) (*GeneratedCode, util.Errors) { opts := ygen.IROptions{ - ParseOptions: cg.Config.ParseOptions, - TransformationOptions: cg.Config.TransformationOptions, + ParseOptions: cg.IROptions.ParseOptions, + TransformationOptions: cg.IROptions.TransformationOptions, NestedDirectories: false, AbsoluteMapPaths: false, AppendEnumSuffixForSimpleUnionEnums: cg.GoOptions.AppendEnumSuffixForSimpleUnionEnums, @@ -201,8 +211,8 @@ func (cg *CodeGenerator) Generate(yangFiles, includePaths []string) (*GeneratedC } var rootName string - if cg.Config.TransformationOptions.GenerateFakeRoot { - rootName = cg.Config.TransformationOptions.FakeRootName + if cg.IROptions.TransformationOptions.GenerateFakeRoot { + rootName = cg.IROptions.TransformationOptions.FakeRootName if rootName == "" { rootName = igenutil.DefaultRootName } @@ -240,7 +250,7 @@ func (cg *CodeGenerator) Generate(yangFiles, includePaths []string) (*GeneratedC codegenErr = util.AppendErrs(codegenErr, errs) continue } - structOut, errs := writeGoStruct(dir, ir.Directories, generatedUnions, opts.TransformationOptions.IgnoreShadowSchemaPaths, cg.GoOptions, cg.Config.GenerateJSONSchema) + structOut, errs := writeGoStruct(dir, ir.Directories, generatedUnions, cg.GoOptions) if errs != nil { codegenErr = util.AppendErrs(codegenErr, errs) continue @@ -301,9 +311,9 @@ func (cg *CodeGenerator) Generate(yangFiles, includePaths []string) (*GeneratedC var rawSchema []byte var jsonSchema string var enumTypeMapCode string - if cg.Config.GenerateJSONSchema { + if cg.GoOptions.GenerateJSONSchema { var err error - rawSchema, err = ir.SchemaTree(cg.Config.IncludeDescriptions) + rawSchema, err = ir.SchemaTree(cg.GoOptions.IncludeDescriptions) if err != nil { codegenErr = util.AppendErr(codegenErr, fmt.Errorf("error marshalling JSON schema: %v", err)) } diff --git a/gogen/codegen_test.go b/gogen/codegen_test.go index bdf7d308f..d2c273ce4 100644 --- a/gogen/codegen_test.go +++ b/gogen/codegen_test.go @@ -28,15 +28,14 @@ const ( // through Goyang's API, it provides the input set of parameters in a way that // can be reused across tests. type yangTestCase struct { - name string // Name is the identifier for the test. - inFiles []string // inFiles is the set of inputFiles for the test. - inIncludePaths []string // inIncludePaths is the set of paths that should be searched for imports. - inExcludeModules []string // inExcludeModules is the set of modules that should be excluded from code generation. - inConfig ygen.GeneratorConfig // inConfig specifies the configuration that should be used for the generator test case. - inGoOptions GoOpts // inGoOpts specifies the go-specific configuration that should be used for the generator test case. - wantStructsCodeFile string // wantsStructsCodeFile is the path of the generated Go code that the output of the test should be compared to. - wantErrSubstring string // wantErrSubstring specifies whether the test should expect an error. - wantSchemaFile string // wantSchemaFile is the path to the schema JSON that the output of the test should be compared to. + name string // Name is the identifier for the test. + inFiles []string // inFiles is the set of inputFiles for the test. + inIncludePaths []string // inIncludePaths is the set of paths that should be searched for imports. + inExcludeModules []string // inExcludeModules is the set of modules that should be excluded from code generation. + inConfig CodeGenerator // inConfig specifies the configuration that should be used for the generator test case. + wantStructsCodeFile string // wantsStructsCodeFile is the path of the generated Go code that the output of the test should be compared to. + wantErrSubstring string // wantErrSubstring specifies whether the test should expect an error. + wantSchemaFile string // wantSchemaFile is the path to the schema JSON that the output of the test should be compared to. } // TestSimpleStructs tests the processModules, Generate and writeGoCode @@ -52,349 +51,393 @@ func TestSimpleStructs(t *testing.T) { tests := []yangTestCase{{ name: "simple openconfig test, with compression, with (useless) enum org name trimming", inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple.formatted-txt"), }, { name: "simple openconfig test, with excluded state, with compression, with enum org name trimming", inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.ExcludeDerivedState, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.ExcludeDerivedState, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-excludestate.formatted-txt"), }, { name: "simple openconfig test, with no compression", inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-no-compress.formatted-txt"), }, { name: "simple openconfig test, with compression, without shortened enum leaf names, with enum org name trimming", inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple.long-enum-names.trimmed-enum.formatted-txt"), }, { name: "simple openconfig test, with no compression, with enum org name trimming", inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-simple-no-compress.trimmed-enum.formatted-txt"), }, { name: "OpenConfig leaf-list defaults test, with compression", inFiles: []string{filepath.Join(datapath, "openconfig-leaflist-default.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-leaflist-default.formatted-txt"), }, { name: "OpenConfig schema test - with annotations", inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + AddAnnotationFields: true, + AnnotationPrefix: "ᗩ", + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - AddAnnotationFields: true, - AnnotationPrefix: "ᗩ", - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-simple-annotations.formatted-txt"), }, { name: "OpenConfig schema test - list and associated method (rename, new)", inFiles: []string{filepath.Join(datapath, "openconfig-withlist.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-withlist.formatted-txt"), }, { name: "OpenConfig schema test - list and associated method (rename, new) - using operational state", inFiles: []string{filepath.Join(datapath, "openconfig-withlist.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferOperationalState, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferOperationalState, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-withlist-opstate.formatted-txt"), }, { name: "OpenConfig schema test - multi-keyed list key struct name conflict and associated method (rename, new)", inFiles: []string{filepath.Join(datapath, "openconfig-multikey-list-name-conflict.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-multikey-list-name-conflict.formatted-txt"), }, { name: "simple openconfig test, with a list that has an enumeration key", inFiles: []string{filepath.Join(datapath, "openconfig-list-enum-key.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - IgnoreShadowSchemaPaths: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + IgnoreShadowSchemaPaths: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-list-enum-key.formatted-txt"), }, { name: "simple openconfig test, with a list that has an enumeration key, with enum org name trimming", inFiles: []string{filepath.Join(datapath, "openconfig-list-enum-key.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - EnumOrgPrefixesToTrim: []string{"openconfig"}, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + EnumOrgPrefixesToTrim: []string{"openconfig"}, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-list-enum-key.trimmed-enum.formatted-txt"), }, { name: "openconfig test with a identityref union", inFiles: []string{filepath.Join(datapath, "openconfig-unione.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-unione.formatted-txt"), }, { name: "openconfig test with a identityref union (wrapper unions)", inFiles: []string{filepath.Join(datapath, "openconfig-unione.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, }, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-unione.wrapper-unions.formatted-txt"), }, { name: "openconfig tests with fakeroot", inFiles: []string{filepath.Join(datapath, "openconfig-fakeroot.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-fakeroot.formatted-txt"), }, { name: "openconfig noncompressed tests with fakeroot", inFiles: []string{filepath.Join(datapath, "openconfig-fakeroot.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-fakeroot-nc.formatted-txt"), }, { name: "schema test with compression", inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateJSONSchema: true, + GenerateSimpleUnions: true, }, - GenerateJSONSchema: true, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress.formatted-txt"), wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress-schema.json"), }, { name: "schema test without compression", inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateJSONSchema: true, + GenerateSimpleUnions: true, }, - GenerateJSONSchema: true, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress.formatted-txt"), wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress-schema.json"), }, { name: "schema test with fakeroot", inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateJSONSchema: true, + GenerateSimpleUnions: true, }, - GenerateJSONSchema: true, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress-fakeroot.formatted-txt"), wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-compress-fakeroot-schema.json"), }, { name: "schema test with fakeroot and no compression", inFiles: []string{filepath.Join(TestRoot, "testdata/schema/openconfig-options.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateJSONSchema: true, + GenerateSimpleUnions: true, }, - GenerateJSONSchema: true, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress-fakeroot.formatted-txt"), wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-nocompress-fakeroot-schema.json"), }, { name: "schema test with camelcase annotations", inFiles: []string{filepath.Join(datapath, "openconfig-camelcase.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-camelcase-compress.formatted-txt"), }, { name: "structs test with camelcase annotations", inFiles: []string{filepath.Join(datapath, "openconfig-enumcamelcase.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-enumcamelcase-compress.formatted-txt"), }, { @@ -407,55 +450,60 @@ func TestSimpleStructs(t *testing.T) { filepath.Join(datapath, "openconfig-simple-target.yang"), filepath.Join(datapath, "openconfig-simple-augment.yang"), }, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/openconfig-augmented.formatted-txt"), }, { name: "variable and import explicitly specified", inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - FakeRootName: "fakeroot", - EnumerationsUseUnderscores: true, - }, - Caller: "testcase", - StoreRawSchema: true, - GenerateJSONSchema: true, - }, - inGoOptions: GoOpts{ - SchemaVarName: "YANGSchema", - GoyangImportPath: "foo/goyang", - YgotImportPath: "bar/ygot", - YtypesImportPath: "baz/ytypes", - GenerateSimpleUnions: true, + inConfig: CodeGenerator{ + Caller: "testcase", + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + FakeRootName: "fakeroot", + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateJSONSchema: true, + SchemaVarName: "YANGSchema", + GoyangImportPath: "foo/goyang", + YgotImportPath: "bar/ygot", + YtypesImportPath: "baz/ytypes", + GenerateSimpleUnions: true, + }, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-explicit.formatted-txt"), wantSchemaFile: filepath.Join(TestRoot, "testdata/schema/openconfig-options-explicit-schema.json"), }, { name: "module with entities at the root", inFiles: []string{filepath.Join(datapath, "root-entities.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - FakeRootName: "fakeroot", - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, - }, + inConfig: CodeGenerator{ Caller: "testcase", - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + FakeRootName: "fakeroot", + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + }, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/root-entities.formatted-txt"), }, { @@ -466,232 +514,262 @@ func TestSimpleStructs(t *testing.T) { name: "module with excluded modules", inFiles: []string{filepath.Join(datapath, "excluded-module.yang")}, inExcludeModules: []string{"excluded-module-two"}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - FakeRootName: "office", - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + FakeRootName: "office", + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/excluded-module.formatted-txt"), }, { name: "module with excluded config false", inFiles: []string{filepath.Join(datapath, "", "openconfig-config-false.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.UncompressedExcludeDerivedState, - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.UncompressedExcludeDerivedState, + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-config-false-uncompressed.formatted-txt"), }, { name: "module with excluded config false - with compression", inFiles: []string{filepath.Join(datapath, "", "openconfig-config-false.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - CompressBehaviour: genutil.ExcludeDerivedState, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + CompressBehaviour: genutil.ExcludeDerivedState, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-config-false-compressed.formatted-txt"), }, { name: "module with getters, delete and append methods", inFiles: []string{filepath.Join(datapath, "", "openconfig-list-enum-key.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateAppendMethod: true, + GenerateGetters: true, + GenerateDeleteMethod: true, + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateAppendMethod: true, - GenerateGetters: true, - GenerateDeleteMethod: true, - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-list-enum-key.getters-append.formatted-txt"), }, { name: "module with excluded state, with RO list, path compression on", inFiles: []string{filepath.Join(datapath, "", "exclude-state-ro-list.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - CompressBehaviour: genutil.ExcludeDerivedState, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + CompressBehaviour: genutil.ExcludeDerivedState, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "exclude-state-ro-list.formatted-txt"), }, { name: "different union enumeration types", inFiles: []string{filepath.Join(datapath, "", "enum-union.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-union.formatted-txt"), }, { name: "different union enumeration types with consistent naming for union-inlined enums", inFiles: []string{filepath.Join(datapath, "", "enum-union.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + AppendEnumSuffixForSimpleUnionEnums: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - AppendEnumSuffixForSimpleUnionEnums: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-union.consistent.formatted-txt"), }, { name: "different union enumeration types with default enum values", inFiles: []string{filepath.Join(datapath, "", "enum-union-with-enum-defaults.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + AppendEnumSuffixForSimpleUnionEnums: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - AppendEnumSuffixForSimpleUnionEnums: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-union-with-enum-defaults.formatted-txt"), }, { name: "different union enumeration types with default enum values (wrapper union)", inFiles: []string{filepath.Join(datapath, "", "enum-union-with-enum-defaults.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + AppendEnumSuffixForSimpleUnionEnums: true, }, - }, - inGoOptions: GoOpts{ - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - AppendEnumSuffixForSimpleUnionEnums: true, }, wantErrSubstring: "default value not supported for wrapper union values, please generate using simplified union leaves", }, { name: "enumeration behaviour - resolution across submodules and grouping re-use within union", inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.formatted-txt"), }, { name: "enumeration behaviour (wrapper unions) - resolution across submodules and grouping re-use within union", inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateLeafGetters: true, }, - }, - inGoOptions: GoOpts{ - GenerateLeafGetters: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.wrapper-unions.formatted-txt"), }, { name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with enumeration leaf names not shortened", inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.long-enum-names.formatted-txt"), }, { name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with typedef enum names being prefixed by the module of their use/residence rather than of their definition", inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.residing-module-typedef-enum-name.formatted-txt"), }, { name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with typedef enum names being prefixed by the module of their use/residence rather than of their definition, and enumeration leaf names not shortened", inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.long-enum-names.residing-module-typedef-enum-name.formatted-txt"), }, { name: "enumeration behaviour - resolution across submodules and grouping re-use within union, with typedef enum names being prefixed by the module of their use/residence rather than of their definition, and enumeration leaf names not shortened", inFiles: []string{filepath.Join(datapath, "", "enum-module.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + EnumerationsUseUnderscores: true, + }, }, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-module.long-enum-names.residing-module-typedef-enum-name.wrapper-unions.formatted-txt"), @@ -699,167 +777,188 @@ func TestSimpleStructs(t *testing.T) { name: "enumeration behaviour - multiple enumerations within a union", inFiles: []string{filepath.Join(datapath, "", "enum-multi-module.yang")}, inIncludePaths: []string{filepath.Join(datapath, "modules")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateJSONSchema: true, + GenerateSimpleUnions: true, }, - GenerateJSONSchema: true, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-multi-module.formatted-txt"), }, { name: "module with leaf getters", inFiles: []string{filepath.Join(datapath, "", "openconfig-list-enum-key.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - CompressBehaviour: genutil.PreferIntendedConfig, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + CompressBehaviour: genutil.PreferIntendedConfig, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-list-enum-key.leaf-getters.formatted-txt"), }, { name: "uncompressed module with two different enums", inFiles: []string{filepath.Join(datapath, "", "enum-list-uncompressed.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-list-uncompressed.formatted-txt"), }, { name: "uncompressed module with two different enums (wrapper unions)", inFiles: []string{filepath.Join(datapath, "", "enum-list-uncompressed.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, }, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-list-uncompressed.wrapper-unions.formatted-txt"), }, { name: "with model data", inFiles: []string{filepath.Join(datapath, "", "openconfig-versioned-mod.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - CompressBehaviour: genutil.PreferIntendedConfig, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + CompressBehaviour: genutil.PreferIntendedConfig, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + IncludeModelData: true, + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - IncludeModelData: true, - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "openconfig-versioned-mod.formatted-txt"), }, { name: "model with deduplicated enums", inFiles: []string{filepath.Join(datapath, "enum-duplication.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-duplication-dedup.formatted-txt"), }, { name: "model with enums that are in the same grouping duplicated", inFiles: []string{filepath.Join(datapath, "enum-duplication.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + EnumerationsUseUnderscores: true, + SkipEnumDeduplication: true, + }, + ParseOptions: ygen.ParseOpts{}, }, - ParseOptions: ygen.ParseOpts{ - SkipEnumDeduplication: true, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, }, }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata", "structs", "enum-duplication-dup.formatted-txt"), }, { name: "OpenConfig schema test - list with binary key", inFiles: []string{filepath.Join(datapath, "openconfig-binary-list.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, }, wantErrSubstring: "has a binary key", }, { name: "OpenConfig schema test - multi-keyed list with binary key", inFiles: []string{filepath.Join(datapath, "openconfig-binary-multi-list.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, }, wantErrSubstring: "has a binary key", }, { name: "OpenConfig schema test - list with union key containing binary", inFiles: []string{filepath.Join(datapath, "openconfig-union-binary-list.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - ShortenEnumLeafNames: true, - UseDefiningModuleForTypedefEnumNames: true, - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + ShortenEnumLeafNames: true, + UseDefiningModuleForTypedefEnumNames: true, + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateRenameMethod: true, + GenerateSimpleUnions: true, }, - }, - inGoOptions: GoOpts{ - GenerateRenameMethod: true, - GenerateSimpleUnions: true, }, wantErrSubstring: "has a union key containing a binary", }, { name: "module with presence containers", inFiles: []string{filepath.Join(datapath, "presence-container-example.yang")}, - inConfig: ygen.GeneratorConfig{ - TransformationOptions: ygen.TransformationOpts{ - GenerateFakeRoot: true, - FakeRootName: "device", - EnumerationsUseUnderscores: true, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + FakeRootName: "device", + EnumerationsUseUnderscores: true, + }, + }, + GoOptions: GoOpts{ + GenerateSimpleUnions: true, + GenerateLeafGetters: true, + GeneratePopulateDefault: true, + AddYangPresence: true, }, - }, - inGoOptions: GoOpts{ - GenerateSimpleUnions: true, - GenerateLeafGetters: true, - GeneratePopulateDefault: true, - AddYangPresence: true, }, wantStructsCodeFile: filepath.Join(TestRoot, "testdata/structs/presence-container-example.formatted-txt"), }} @@ -873,10 +972,9 @@ func TestSimpleStructs(t *testing.T) { // the unit tests are called by external test entities. tt.inConfig.Caller = "codegen-tests" } - tt.inConfig.StoreRawSchema = true - tt.inConfig.ParseOptions.ExcludeModules = tt.inExcludeModules + tt.inConfig.IROptions.ParseOptions.ExcludeModules = tt.inExcludeModules - cg := NewCodeGenerator(&tt.inConfig, &tt.inGoOptions) + cg := New(tt.inConfig.Caller, tt.inConfig.IROptions, tt.inConfig.GoOptions) gotGeneratedCode, errs := cg.Generate(tt.inFiles, tt.inIncludePaths) var err error @@ -907,7 +1005,7 @@ func TestSimpleStructs(t *testing.T) { fmt.Fprint(&gotCode, gotGeneratedCode.EnumMap) var gotJSON map[string]interface{} - if tt.inConfig.GenerateJSONSchema { + if tt.inConfig.GoOptions.GenerateJSONSchema { // Write the schema byte array out. fmt.Fprint(&gotCode, gotGeneratedCode.JSONSchemaCode) fmt.Fprint(&gotCode, gotGeneratedCode.EnumTypeMap) diff --git a/gogen/genir_test.go b/gogen/genir_test.go index 527644c98..8a27edd84 100644 --- a/gogen/genir_test.go +++ b/gogen/genir_test.go @@ -1447,6 +1447,28 @@ func TestGenerateIR(t *testing.T) { ShadowMappedPaths: [][]string{{"state", "iref"}}, ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, }, + "iref2": { + Name: "Iref2", + YANGDetails: ygen.YANGNodeDetails{ + Name: "iref2", + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/iref2", + SchemaPath: "/model/a/single-key/config/iref2", + ShadowSchemaPath: "/model/a/single-key/state/iref2", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "E_Complex_Program", + IsEnumeratedValue: true, + ZeroValue: "0", + }, + MappedPaths: [][]string{{"config", "iref2"}}, + MappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + ShadowMappedPaths: [][]string{{"state", "iref2"}}, + ShadowMappedPathModules: [][]string{{"openconfig-complex", "openconfig-complex"}}, + }, "key": { Name: "Key", YANGDetails: ygen.YANGNodeDetails{ @@ -1795,6 +1817,15 @@ func TestGenerateIR(t *testing.T) { {Name: "OS", DefiningModule: "openconfig-complex"}, }, }, + "/openconfig-complex/program": { + Name: "Complex_Program", + Kind: ygen.IdentityType, + IdentityBaseName: "SOFTWARE", + TypeName: "program", + ValToYANGDetails: []ygot.EnumDefinition{ + {Name: "OS", DefiningModule: "openconfig-complex"}, + }, + }, "/openconfig-complex/multi-key-config/key2": { Name: "MultiKey_Key2", Kind: ygen.SimpleEnumerationType, diff --git a/gogen/goelements.go b/gogen/goelements.go index ebdcae0a7..9ecfb6def 100644 --- a/gogen/goelements.go +++ b/gogen/goelements.go @@ -255,12 +255,12 @@ func (s *GoLangMapper) FieldName(e *yang.Entry) (string, error) { // LeafType maps the input leaf entry to a MappedType object containing the // type information about the field. func (s *GoLangMapper) LeafType(e *yang.Entry, opts ygen.IROptions) (*ygen.MappedType, error) { - mtype, err := s.yangTypeToGoType(resolveTypeArgs{yangType: e.Type, contextEntry: e}, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.TransformationOptions.EnumOrgPrefixesToTrim) + mtype, err := s.yangTypeToGoType(resolveTypeArgs{yangType: e.Type, contextEntry: e}, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.TransformationOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.TransformationOptions.EnumOrgPrefixesToTrim) if err != nil { return nil, err } - defaultValue, err := generateGoDefaultValue(e, mtype, s, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.TransformationOptions.EnumOrgPrefixesToTrim, s.simpleUnions) + defaultValue, err := generateGoDefaultValue(e, mtype, s, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.TransformationOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.TransformationOptions.EnumOrgPrefixesToTrim, s.simpleUnions) if err != nil { return nil, err } @@ -272,7 +272,7 @@ func (s *GoLangMapper) LeafType(e *yang.Entry, opts ygen.IROptions) (*ygen.Mappe // LeafType maps the input list key entry to a MappedType object containing the // type information about the key field. func (s *GoLangMapper) KeyLeafType(e *yang.Entry, opts ygen.IROptions) (*ygen.MappedType, error) { - return s.yangTypeToGoType(resolveTypeArgs{yangType: e.Type, contextEntry: e}, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.TransformationOptions.EnumOrgPrefixesToTrim) + return s.yangTypeToGoType(resolveTypeArgs{yangType: e.Type, contextEntry: e}, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), opts.TransformationOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.TransformationOptions.EnumOrgPrefixesToTrim) } // PackageName is not used by Go generation. diff --git a/gogen/gogen.go b/gogen/gogen.go index 36cbd14e9..1066e2977 100644 --- a/gogen/gogen.go +++ b/gogen/gogen.go @@ -1189,12 +1189,12 @@ func (t *{{ .ParentReceiver }}) To_{{ .Name }}(i interface{}) ({{ .Name }}, erro // be included in only one file of the package. func writeGoHeader(yangFiles, includePaths []string, cfg *CodeGenerator, rootName string, modelData []*gpb.ModelData) (string, string, error) { // Determine the running binary's name. - if cfg.Config.Caller == "" { - cfg.Config.Caller = genutil.CallerName() + if cfg.Caller == "" { + cfg.Caller = genutil.CallerName() } - if cfg.Config.PackageName == "" { - cfg.Config.PackageName = defaultPackageName + if cfg.GoOptions.PackageName == "" { + cfg.GoOptions.PackageName = defaultPackageName } if cfg.GoOptions.YgotImportPath == "" { @@ -1225,12 +1225,12 @@ func writeGoHeader(yangFiles, includePaths []string, cfg *CodeGenerator, rootNam FakeRootName string // FakeRootName is the name of the fake root struct in the YANG type ModelData []*gpb.ModelData // ModelData contains the gNMI ModelData definition for the input types. }{ - PackageName: cfg.Config.PackageName, + PackageName: cfg.GoOptions.PackageName, YANGFiles: yangFiles, IncludePaths: includePaths, - CompressEnabled: cfg.Config.TransformationOptions.CompressBehaviour.CompressEnabled(), - GeneratingBinary: cfg.Config.Caller, - GenerateSchema: cfg.Config.GenerateJSONSchema, + CompressEnabled: cfg.IROptions.TransformationOptions.CompressBehaviour.CompressEnabled(), + GeneratingBinary: cfg.Caller, + GenerateSchema: cfg.GoOptions.GenerateJSONSchema, GoOptions: cfg.GoOptions, BinaryTypeName: ygot.BinaryTypeName, EmptyTypeName: ygot.EmptyTypeName, @@ -1238,7 +1238,7 @@ func writeGoHeader(yangFiles, includePaths []string, cfg *CodeGenerator, rootNam } s.FakeRootName = "nil" - if cfg.Config.TransformationOptions.GenerateFakeRoot && rootName != "" { + if cfg.IROptions.TransformationOptions.GenerateFakeRoot && rootName != "" { s.FakeRootName = fmt.Sprintf("&%s{}", rootName) } @@ -1281,16 +1281,11 @@ func IsScalarField(field *ygen.NodeDetails) bool { // child container's struct name). // // writeGoStruct takes the following additional arguments: -// - state - the current generator state, as a genState pointer. -// - compressOCPaths - a bool indicating whether OpenConfig path compression is enabled for -// this schema. -// - ignoreShadowSchemaPaths - a bool indicating that when OpenConfig path compression is -// enabled, the shadowed paths are ignored while unmarshalling. -// - generateJSONSchema - a bool indicating whether the generated code should include the -// JSON representation of the YANG schema for this element. +// - targetStruct - the YANG directory (container/list) to be converted to generated code. +// - goStructElements - All existing YANG directories (for looking up children). +// - generatedUnions - Running map of generated unions to avoid generating the +// same union twice. // - goOpts - Go specific code generation options as a GoOpts struct. -// - skipEnumDedup -- a boolean that indicates whether leaves of type enumeration that are -// used in multiple places in the schema tree should share a common underlying type. // // writeGoStruct returns a GoStructCodeSnippet which contains // 1. The generated struct for targetStruct (structDef) @@ -1298,7 +1293,7 @@ func IsScalarField(field *ygen.NodeDetails) bool { // of targetStruct (listKeys). // 3. Methods with the struct corresponding to targetStruct as a receiver, e.g., for each // list a NewListMember() method is generated. -func writeGoStruct(targetStruct *ygen.ParsedDirectory, goStructElements map[string]*ygen.ParsedDirectory, generatedUnions map[string]bool, ignoreShadowSchemaPaths bool, goOpts GoOpts, generateJSONSchema bool) (GoStructCodeSnippet, []error) { +func writeGoStruct(targetStruct *ygen.ParsedDirectory, goStructElements map[string]*ygen.ParsedDirectory, generatedUnions map[string]bool, goOpts GoOpts) (GoStructCodeSnippet, []error) { if targetStruct == nil { return GoStructCodeSnippet{}, []error{fmt.Errorf("cannot create code for nil targetStruct")} } @@ -1547,7 +1542,7 @@ func writeGoStruct(targetStruct *ygen.ParsedDirectory, goStructElements map[stri tagBuf.WriteString(` module:"`) addSchemaPathsToBuffers(field.MappedPathModules, false) - if ignoreShadowSchemaPaths { + if goOpts.IgnoreShadowSchemaPaths { if len(field.ShadowMappedPaths) > 0 { tagBuf.WriteString(` shadow-path:"`) addSchemaPathsToBuffers(field.ShadowMappedPaths, false) @@ -1687,7 +1682,7 @@ func writeGoStruct(targetStruct *ygen.ParsedDirectory, goStructElements map[stri } } - if generateJSONSchema { + if goOpts.GenerateJSONSchema { if err := generateValidator(&methodBuf, structDef, goOpts.ValidateFunctionName); err != nil { errs = append(errs, err) } diff --git a/gogen/gogen_test.go b/gogen/gogen_test.go index 5a60f32d5..3021ec8a3 100644 --- a/gogen/gogen_test.go +++ b/gogen/gogen_test.go @@ -102,6 +102,7 @@ func TestGoCodeStructGeneration(t *testing.T) { BelongingModule: "exmod", }, inGoOpts: GoOpts{ + GenerateJSONSchema: true, ValidateFunctionName: "ValidateProxyFunction", }, want: wantGoStructOut{ @@ -195,7 +196,10 @@ func (*Tstruct) ΛBelongingModule() string { Path: "/root-module/tstruct", BelongingModule: "exmod", }, - inIgnoreShadowSchemaPaths: true, + inGoOpts: GoOpts{ + GenerateJSONSchema: true, + IgnoreShadowSchemaPaths: true, + }, want: wantGoStructOut{ structs: ` // Tstruct represents the /root-module/tstruct YANG schema element. @@ -267,6 +271,9 @@ func (*Tstruct) ΛBelongingModule() string { Path: "/module/input-struct", BelongingModule: "exmod", }, + inGoOpts: GoOpts{ + GenerateJSONSchema: true, + }, want: wantGoStructOut{ structs: ` // InputStruct represents the /module/input-struct YANG schema element. @@ -373,6 +380,9 @@ func (t *InputStruct) To_InputStruct_U1_Union(i interface{}) (InputStruct_U1_Uni BelongingModule: "exmod", }, }, + inGoOpts: GoOpts{ + GenerateJSONSchema: true, + }, want: wantGoStructOut{ structs: ` // InputStruct represents the /root-module/input-struct YANG schema element. @@ -440,7 +450,8 @@ func (*InputStruct) ΛBelongingModule() string { }, }, inGoOpts: GoOpts{ - AddYangPresence: true, + GenerateJSONSchema: true, + AddYangPresence: true, }, want: wantGoStructOut{ structs: ` @@ -557,6 +568,9 @@ func (*InputStruct) ΛBelongingModule() string { BelongingModule: "exmod", }, }, + inGoOpts: GoOpts{ + GenerateJSONSchema: true, + }, want: wantGoStructOut{ structs: ` // QStruct represents the /root-module/q-struct YANG schema element. @@ -657,6 +671,7 @@ func (*QStruct) ΛBelongingModule() string { }, }, inGoOpts: GoOpts{ + GenerateJSONSchema: true, GenerateRenameMethod: true, }, want: wantGoStructOut{ @@ -898,6 +913,7 @@ func (*Tstruct) ΛBelongingModule() string { }, }, inGoOpts: GoOpts{ + GenerateJSONSchema: true, GenerateRenameMethod: true, }, want: wantGoStructOut{ @@ -1023,6 +1039,7 @@ func (*Tstruct) ΛBelongingModule() string { BelongingModule: "exmod", }, inGoOpts: GoOpts{ + GenerateJSONSchema: true, AddAnnotationFields: true, AnnotationPrefix: "Ω", }, @@ -1156,6 +1173,7 @@ func (*Tstruct) ΛBelongingModule() string { }, }, inGoOpts: GoOpts{ + GenerateJSONSchema: true, GenerateAppendMethod: true, GenerateGetters: true, GenerateDeleteMethod: true, @@ -1385,6 +1403,7 @@ func (*Tstruct) ΛBelongingModule() string { }, }, inGoOpts: GoOpts{ + GenerateJSONSchema: true, GenerateAppendMethod: true, GenerateGetters: true, GenerateDeleteMethod: true, @@ -1567,6 +1586,7 @@ func (*Tstruct) ΛBelongingModule() string { }, }, inGoOpts: GoOpts{ + GenerateJSONSchema: true, GenerateGetters: true, GeneratePopulateDefault: true, }, @@ -1665,6 +1685,7 @@ func (*InputStruct) ΛBelongingModule() string { BelongingModule: "m1", }, inGoOpts: GoOpts{ + GenerateJSONSchema: true, GenerateLeafGetters: true, GeneratePopulateDefault: true, }, @@ -1758,6 +1779,7 @@ func (*Container) ΛBelongingModule() string { BelongingModule: "m1", }, inGoOpts: GoOpts{ + GenerateJSONSchema: true, GenerateLeafGetters: true, GeneratePopulateDefault: true, }, @@ -1834,7 +1856,7 @@ func (*Container) ΛBelongingModule() string { tt.inOtherStructMap[tt.inStructToMap.Path] = tt.inStructToMap // Always generate the JSON schema for this test. generatedUnions := map[string]bool{} - got, errs := writeGoStruct(tt.inStructToMap, tt.inOtherStructMap, generatedUnions, tt.inIgnoreShadowSchemaPaths, tt.inGoOpts, true) + got, errs := writeGoStruct(tt.inStructToMap, tt.inOtherStructMap, generatedUnions, tt.inGoOpts) if len(errs) != 0 && !tt.want.wantErr { t.Fatalf("%s writeGoStruct(targetStruct: %v): received unexpected errors: %v", diff --git a/integration_tests/generate_errs_test.go b/integration_tests/generate_errs_test.go new file mode 100644 index 000000000..fd90a587d --- /dev/null +++ b/integration_tests/generate_errs_test.go @@ -0,0 +1,79 @@ +package integration_tests + +import ( + "path/filepath" + "testing" + + "github.com/openconfig/gnmi/errdiff" + "github.com/openconfig/ygot/gogen" + "github.com/openconfig/ygot/protogen" + "github.com/openconfig/ygot/ygen" +) + +func TestGenerateErrs(t *testing.T) { + tests := []struct { + name string + inFiles []string + inPath []string + inIROptions ygen.IROptions + wantGoOK bool + wantGoErrSubstring string + wantProtoOK bool + wantProtoErrSubstring string + wantSameErrSubstring bool + }{{ + name: "missing YANG file", + inFiles: []string{filepath.Join("testdata", "errors", "doesnt-exist.yang")}, + wantGoErrSubstring: "no such file", + wantSameErrSubstring: true, + }, { + name: "bad YANG file", + inFiles: []string{filepath.Join("testdata", "errors", "bad-module.yang")}, + wantGoErrSubstring: "syntax error", + wantSameErrSubstring: true, + }, { + name: "missing import due to path", + inFiles: []string{filepath.Join("testdata", "errors", "missing-import.yang")}, + wantGoErrSubstring: "no such module", + wantSameErrSubstring: true, + }, { + name: "import satisfied due to path", + inFiles: []string{filepath.Join("testdata", "errors", "missing-import.yang")}, + inPath: []string{filepath.Join("testdata", "errors", "subdir")}, + wantGoOK: true, + wantProtoOK: true, + }} + + for _, tt := range tests { + gcg := gogen.New("", tt.inIROptions, gogen.GoOpts{}) + + _, goErr := gcg.Generate(tt.inFiles, tt.inPath) + switch { + case tt.wantGoOK && goErr != nil: + t.Errorf("%s: gcg.GenerateGoCode(%v, %v): got unexpected error, got: %v, want: nil", tt.name, tt.inFiles, tt.inPath, goErr) + case tt.wantGoOK: + default: + if diff := errdiff.Substring(goErr, tt.wantGoErrSubstring); diff != "" { + t.Errorf("%s: gcg.GenerateGoCode(%v, %v): %v", tt.name, tt.inFiles, tt.inPath, diff) + } + } + + pcg := protogen.New("", tt.inIROptions, protogen.ProtoOpts{}) + + if tt.wantSameErrSubstring { + tt.wantProtoErrSubstring = tt.wantGoErrSubstring + } + + _, protoErr := pcg.Generate(tt.inFiles, tt.inPath) + switch { + case tt.wantProtoOK && protoErr != nil: + t.Errorf("%s: pcg.Generate(%v, %v): got unexpected error, got: %v, want: nil", tt.name, tt.inFiles, tt.inPath, protoErr) + case tt.wantProtoOK: + default: + if diff := errdiff.Substring(protoErr, tt.wantProtoErrSubstring); diff != "" { + t.Errorf("%s: pcg.Generate(%v, %v): %v", tt.name, tt.inFiles, tt.inPath, diff) + } + } + + } +} diff --git a/ygen/testdata/errors/bad-module.yang b/integration_tests/testdata/errors/bad-module.yang similarity index 100% rename from ygen/testdata/errors/bad-module.yang rename to integration_tests/testdata/errors/bad-module.yang diff --git a/ygen/testdata/errors/missing-import.yang b/integration_tests/testdata/errors/missing-import.yang similarity index 100% rename from ygen/testdata/errors/missing-import.yang rename to integration_tests/testdata/errors/missing-import.yang diff --git a/ygen/testdata/errors/subdir/module-in-subdir.yang b/integration_tests/testdata/errors/subdir/module-in-subdir.yang similarity index 100% rename from ygen/testdata/errors/subdir/module-in-subdir.yang rename to integration_tests/testdata/errors/subdir/module-in-subdir.yang diff --git a/proto_generator/protogenerator.go b/proto_generator/protogenerator.go index 2b6c01af7..687542da5 100644 --- a/proto_generator/protogenerator.go +++ b/proto_generator/protogenerator.go @@ -28,6 +28,7 @@ import ( log "github.com/golang/glog" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/protogen" "github.com/openconfig/ygot/ygen" ) @@ -40,8 +41,8 @@ var ( outputDir = flag.String("output_dir", "", "The path to which files should be output, hierarchical folders are created for the generated messages.") ignoreCircDeps = flag.Bool("ignore_circdeps", false, "If set to true, circular dependencies between submodules are ignored.") baseImportPath = flag.String("base_import_path", "", "The base import path that should be used for this package, for example a URL to the GitHub repo that the protobuf messages are stored in.") - ywrapperPath = flag.String("ywrapper_path", ygen.DefaultYwrapperPath, "The path to the ywrapper.proto file, excluding the file name. Used to import the ywrapper protobuf that specifies the wrapper messages for scalar protobuf types.") - yextPath = flag.String("yext_path", ygen.DefaultYextPath, "The path to the yext.proto file, excluding the file name. Used to import the yext protobuf that specifies YANG-specific field options for protobuf.") + ywrapperPath = flag.String("ywrapper_path", protogen.DefaultYwrapperPath, "The path to the ywrapper.proto file, excluding the file name. Used to import the ywrapper protobuf that specifies the wrapper messages for scalar protobuf types.") + yextPath = flag.String("yext_path", protogen.DefaultYextPath, "The path to the yext.proto file, excluding the file name. Used to import the yext protobuf that specifies YANG-specific field options for protobuf.") generateFakeRoot = flag.Bool("generate_fakeroot", false, "If set to true, a fake element at the root of the data tree is generated. The fake root's name can be controlled with the fakeroot_name flag.") fakeRootName = flag.String("fakeroot_name", "Device", "The name of the fake root entity.") annotateSchemaPaths = flag.Bool("add_schemapaths", true, "If set to true, the schema path of each YANG entity is added as a protobuf field option") @@ -100,22 +101,24 @@ func main() { } // Perform the code generation. - cg := ygen.NewYANGCodeGenerator(&ygen.GeneratorConfig{ - ParseOptions: ygen.ParseOpts{ - ExcludeModules: modsExcluded, - SkipEnumDeduplication: *skipEnumDedup, - YANGParseOptions: yang.Options{ - IgnoreSubmoduleCircularDependencies: *ignoreCircDeps, + cg := protogen.New( + *callerName, + ygen.IROptions{ + ParseOptions: ygen.ParseOpts{ + ExcludeModules: modsExcluded, + YANGParseOptions: yang.Options{ + IgnoreSubmoduleCircularDependencies: *ignoreCircDeps, + }, + }, + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: compressBehaviour, + GenerateFakeRoot: *generateFakeRoot, + FakeRootName: *fakeRootName, + SkipEnumDeduplication: *skipEnumDedup, }, }, - TransformationOptions: ygen.TransformationOpts{ - CompressBehaviour: compressBehaviour, - GenerateFakeRoot: *generateFakeRoot, - FakeRootName: *fakeRootName, - }, - PackageName: *packageName, - Caller: *callerName, - ProtoOptions: ygen.ProtoOpts{ + protogen.ProtoOpts{ + PackageName: *packageName, BaseImportPath: *baseImportPath, YwrapperPath: *ywrapperPath, YextPath: *yextPath, @@ -125,9 +128,9 @@ func main() { EnumPackageName: *enumPackageName, GoPackageBase: *goPackageBase, }, - }) + ) - generatedProtoCode, errs := cg.GenerateProto3(generateModules, includePaths) + generatedProtoCode, errs := cg.Generate(generateModules, includePaths) if errs != nil { log.Exitf("%v\n", errs) } diff --git a/protogen/codegen.go b/protogen/codegen.go new file mode 100644 index 000000000..0f2310702 --- /dev/null +++ b/protogen/codegen.go @@ -0,0 +1,259 @@ +package protogen + +import ( + "fmt" + "sort" + "strings" + + "github.com/openconfig/ygot/util" + "github.com/openconfig/ygot/ygen" +) + +// CodeGenerator is a structure that is used to pass arguments as to +// how the output protobuf code should be generated. +type CodeGenerator struct { + // Caller is the name of the binary calling the generator library, it is + // included in the header of output files for debugging purposes. If a + // string is not specified, the location of the library is utilised. + Caller string + // IROptions stores the configuration parameters used for IR generation. + IROptions ygen.IROptions + // ProtoOptions stores a struct which contains Protobuf specific + // options for code generation post IR generation. + ProtoOptions ProtoOpts +} + +// ProtoOpts stores Protobuf specific options for the code generation library. +type ProtoOpts struct { + // PackageName is the name that should be used for the generating package. + PackageName string + // BaseImportPath stores the root URL or path for imports that are + // relative within the imported protobufs. + BaseImportPath string + // EnumPackageName stores the package name that should be used + // for the package that defines enumerated types that are used + // in multiple parts of the schema (identityrefs, and enumerations) + // that fall within type definitions. + EnumPackageName string + // YwrapperPath is the path to the ywrapper.proto file that stores + // the definition of the wrapper messages used to ensure that unset + // fields can be distinguished from those that are set to their + // default value. The path excluds the filename. + YwrapperPath string + // YextPath is the path to the yext.proto file that stores the + // definition of the extension messages that are used to annotat the + // generated protobuf messages. + YextPath string + // AnnotateSchemaPaths specifies whether the extensions defined in + // yext.proto should be used to annotate schema paths into the output + // protobuf file. See + // https://github.com/openconfig/ygot/blob/master/docs/yang-to-protobuf-transformations-spec.md#annotation-of-schema-paths + AnnotateSchemaPaths bool + // AnnotateEnumNames specifies whether the extensions defined in + // yext.proto should be used to annotate enum values with their + // original YANG names in the output protobuf file. + // See https://github.com/openconfig/ygot/blob/master/docs/yang-to-protobuf-transformations-spec.md#annotation-of-enums + AnnotateEnumNames bool + // NestedMessages indicates whether nested messages should be + // output for the protobuf schema. If false, a separate package + // is generated per package. + NestedMessages bool + // GoPackageBase specifies the base of the names that are used in + // the go_package file option for generated protobufs. Additional + // package identifiers are appended to the go_package - such that + // the format /// is used. + GoPackageBase string +} + +// New returns a new instance of the CodeGenerator +// struct to the calling function. +func New(callerName string, opts ygen.IROptions, protoOpts ProtoOpts) *CodeGenerator { + return &CodeGenerator{ + Caller: callerName, + IROptions: opts, + ProtoOptions: protoOpts, + } +} + +// GeneratedCode stores a set of generated Protobuf packages. +type GeneratedCode struct { + // Packages stores a map, keyed by the Protobuf package name, and containing the contents of the protobuf3 + // messages defined within the package. The calling application can write out the defined packages to the + // files expected by the protoc tool. + Packages map[string]Proto3Package +} + +// Proto3Package stores the code for a generated protobuf3 package. +type Proto3Package struct { + FilePath []string // FilePath is the path to the file that this package should be written to. + Header string // Header is the header text to be used in the package. + Messages []string // Messages is a slice of strings containing the set of messages that are within the generated package. + Enums []string // Enums is a slice of string containing the generated set of enumerations within the package. + UsesYwrapperImport bool // UsesYwrapperImport indicates whether the ywrapper proto package is used within the generated package. + UsesYextImport bool // UsesYextImport indicates whether the yext proto package is used within the generated package. +} + +// Generate generates Protobuf 3 code for the input set of YANG files. +// The YANG schemas for which protobufs are to be created is supplied as the +// yangFiles argument, with included modules being searched for in includePaths. +// It returns a GeneratedCode struct containing the messages that are to be +// output, along with any associated values (e.g., enumerations). +func (cg *CodeGenerator) Generate(yangFiles, includePaths []string) (*GeneratedCode, util.Errors) { + basePackageName := cg.ProtoOptions.PackageName + if basePackageName == "" { + basePackageName = DefaultBasePackageName + } + enumPackageName := cg.ProtoOptions.EnumPackageName + if enumPackageName == "" { + enumPackageName = DefaultEnumPackageName + } + ywrapperPath := cg.ProtoOptions.YwrapperPath + if ywrapperPath == "" { + ywrapperPath = DefaultYwrapperPath + } + yextPath := cg.ProtoOptions.YextPath + if yextPath == "" { + yextPath = DefaultYextPath + } + + // This flag is always true for proto generation. + cg.IROptions.TransformationOptions.UseDefiningModuleForTypedefEnumNames = true + opts := ygen.IROptions{ + ParseOptions: cg.IROptions.ParseOptions, + TransformationOptions: cg.IROptions.TransformationOptions, + NestedDirectories: cg.ProtoOptions.NestedMessages, + AbsoluteMapPaths: true, + AppendEnumSuffixForSimpleUnionEnums: true, + } + + ir, err := ygen.GenerateIR(yangFiles, includePaths, NewProtoLangMapper(basePackageName, enumPackageName), opts) + if err != nil { + return nil, util.NewErrs(err) + } + + protoEnums, err := writeProtoEnums(ir.Enums, cg.ProtoOptions.AnnotateEnumNames) + if err != nil { + return nil, util.NewErrs(err) + } + + genProto := &GeneratedCode{ + Packages: map[string]Proto3Package{}, + } + + // yerr stores errors encountered during code generation. + var yerr util.Errors + + // pkgImports lists the imports that are required for the package that is being + // written out. + pkgImports := map[string]map[string]interface{}{} + + // Only create the enums package if there are enums that are within the schema. + if len(protoEnums) > 0 { + // Sort the set of enumerations so that they are deterministically output. + sort.Strings(protoEnums) + fp := []string{basePackageName, enumPackageName, fmt.Sprintf("%s.proto", enumPackageName)} + genProto.Packages[fmt.Sprintf("%s.%s", basePackageName, enumPackageName)] = Proto3Package{ + FilePath: fp, + Enums: protoEnums, + UsesYextImport: cg.ProtoOptions.AnnotateEnumNames, + } + } + + // Ensure that the slice of messages returned is in a deterministic order by + // sorting the message paths. We use the path rather than the name as the + // proto message name may not be unique. + for _, directoryPath := range ir.OrderedDirectoryPaths() { + m := ir.Directories[directoryPath] + + genMsg, errs := writeProto3Msg(m, ir, &protoMsgConfig{ + compressPaths: cg.IROptions.TransformationOptions.CompressBehaviour.CompressEnabled(), + basePackageName: basePackageName, + enumPackageName: enumPackageName, + baseImportPath: cg.ProtoOptions.BaseImportPath, + annotateSchemaPaths: cg.ProtoOptions.AnnotateSchemaPaths, + annotateEnumNames: cg.ProtoOptions.AnnotateEnumNames, + nestedMessages: cg.ProtoOptions.NestedMessages, + }) + + if errs != nil { + yerr = util.AppendErrs(yerr, errs) + continue + } + + // Check whether any messages were required for this schema element, writeProto3Msg can + // return nil if nested messages were being produced, and the message was encapsulated + // in another message. + if genMsg == nil { + continue + } + + if genMsg.PackageName == "" { + genMsg.PackageName = basePackageName + } else { + genMsg.PackageName = fmt.Sprintf("%s.%s", basePackageName, genMsg.PackageName) + } + + if pkgImports[genMsg.PackageName] == nil { + pkgImports[genMsg.PackageName] = map[string]interface{}{} + } + addNewKeys(pkgImports[genMsg.PackageName], genMsg.RequiredImports) + + // If the package does not already exist within the generated proto3 + // output, then create it within the package map. This allows different + // entries in the msgNames set to fall within the same package. + tp, ok := genProto.Packages[genMsg.PackageName] + if !ok { + genProto.Packages[genMsg.PackageName] = Proto3Package{ + FilePath: protoPackageToFilePath(genMsg.PackageName), + Messages: []string{}, + } + tp = genProto.Packages[genMsg.PackageName] + } + tp.Messages = append(tp.Messages, genMsg.MessageCode) + if genMsg.UsesYwrapperImport { + tp.UsesYwrapperImport = true + } + if genMsg.UsesYextImport { + tp.UsesYextImport = true + } + genProto.Packages[genMsg.PackageName] = tp + } + + for n, pkg := range genProto.Packages { + var gpn string + if cg.ProtoOptions.GoPackageBase != "" { + gpn = fmt.Sprintf("%s/%s", cg.ProtoOptions.GoPackageBase, strings.ReplaceAll(n, ".", "/")) + } + ywrapperPath := ywrapperPath + if !pkg.UsesYwrapperImport { + ywrapperPath = "" + } + yextPath := yextPath + if !pkg.UsesYextImport { + yextPath = "" + } + h, err := writeProto3Header(proto3Header{ + PackageName: n, + Imports: stringKeys(pkgImports[n]), + SourceYANGFiles: yangFiles, + SourceYANGIncludePaths: includePaths, + CompressPaths: cg.IROptions.TransformationOptions.CompressBehaviour.CompressEnabled(), + CallerName: cg.Caller, + YwrapperPath: ywrapperPath, + YextPath: yextPath, + GoPackageName: gpn, + }) + if err != nil { + yerr = util.AppendErrs(yerr, []error{err}) + continue + } + pkg.Header = h + genProto.Packages[n] = pkg + } + + if yerr != nil { + return nil, yerr + } + + return genProto, nil +} diff --git a/protogen/codegen_test.go b/protogen/codegen_test.go new file mode 100644 index 000000000..ce003b462 --- /dev/null +++ b/protogen/codegen_test.go @@ -0,0 +1,523 @@ +package protogen + +import ( + "bytes" + "fmt" + "io/ioutil" + "path/filepath" + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/testutil" + "github.com/openconfig/ygot/ygen" +) + +const ( + // TestRoot is the root of the test directory such that this is not + // repeated when referencing files. + TestRoot string = "" + // deflakeRuns specifies the number of runs of code generation that + // should be performed to check for flakes. + deflakeRuns int = 10 +) + +func TestGenerateProto3(t *testing.T) { + tests := []struct { + name string + inFiles []string + inIncludePaths []string + inConfig CodeGenerator + // wantOutputFiles is a map keyed on protobuf package name with a path + // to the file that is expected for each package. + wantOutputFiles map[string]string + wantErr bool + }{{ + name: "simple protobuf test with compression", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + }, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.compress.parent.formatted-txt"), + "openconfig.parent": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.compress.parent.child.formatted-txt"), + }, + }, { + name: "simple protobuf test without compression", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.yang")}, + wantOutputFiles: map[string]string{ + "openconfig.proto_test_a": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.nocompress.formatted-txt"), + "openconfig.proto_test_a.parent": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.nocompress.parent.formatted-txt"), + "openconfig.proto_test_a.parent.child": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.nocompress.parent.child.formatted-txt"), + }, + }, { + name: "enumeration under unions test with compression", + inFiles: []string{filepath.Join(datapath, "enum-union.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + UseDefiningModuleForTypedefEnumNames: true, + }, + }, + ProtoOptions: ProtoOpts{ + AnnotateEnumNames: true, + NestedMessages: true, + GoPackageBase: "github.com/foo/bar", + }, + }, + wantOutputFiles: map[string]string{ + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "enum-union.compress.formatted-txt"), + "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "enum-union.compress.enums.formatted-txt"), + }, + }, { + name: "yang schema with a list", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-b.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + }, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "proto-test-b.compress.formatted-txt"), + "openconfig.device": filepath.Join(TestRoot, "testdata", "proto", "proto-test-b.compress.device.formatted-txt"), + }, + }, { + name: "yang schema with simple enumerations", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{}, + ProtoOptions: ProtoOpts{ + GoPackageBase: "github.com/foo/baz", + }, + }, + wantOutputFiles: map[string]string{ + "openconfig.proto_test_c": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.proto-test-c.formatted-txt"), + "openconfig.proto_test_c.entity": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.proto-test-c.entity.formatted-txt"), + "openconfig.proto_test_c.elists": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.proto-test-c.elists.formatted-txt"), + "openconfig.proto_test_c.elists.elist": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.proto-test-c.elists.elist.formatted-txt"), + "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.enums.formatted-txt"), + }, + }, { + name: "yang schema with identityref and enumerated typedef, compression off", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-d.yang")}, + wantOutputFiles: map[string]string{ + "openconfig.proto_test_d": filepath.Join(TestRoot, "testdata", "proto", "proto-test-d.uncompressed.proto-test-d.formatted-txt"), + "openconfig.proto_test_d.test": filepath.Join(TestRoot, "testdata", "proto", "proto-test-d.uncompressed.proto-test-d.test.formatted-txt"), + "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-test-d.uncompressed.enums.formatted-txt"), + }, + }, { + name: "yang schema with unions", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + UseDefiningModuleForTypedefEnumNames: true, + }, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig.proto_test_e": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.formatted-txt"), + "openconfig.proto_test_e.test": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.test.formatted-txt"), + "openconfig.proto_test_e.foos": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.foos.formatted-txt"), + "openconfig.proto_test_e.foos.foo": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.foos.foo.formatted-txt"), + "openconfig.proto_test_e.bars": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.bars.formatted-txt"), + "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.enums.formatted-txt"), + "openconfig.proto_test_e.animals": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.animals.formatted-txt"), + "openconfig.proto_test_e.animals.animal": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.animals.animal.formatted-txt"), + }, + }, { + name: "yang schema with anydata", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-anydata-test.yang")}, + wantOutputFiles: map[string]string{ + "openconfig.proto_anydata_test": filepath.Join(TestRoot, "testdata", "proto", "proto_anydata_test.formatted-txt"), + "openconfig.proto_anydata_test.e": filepath.Join(TestRoot, "testdata", "proto", "proto_anydata_test.e.formatted-txt"), + }, + }, { + name: "yang schema with path annotations", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-f.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{}, + ProtoOptions: ProtoOpts{ + AnnotateSchemaPaths: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig.proto_test_f": filepath.Join(TestRoot, "testdata", "proto", "proto_test_f.uncompressed.proto_test_f.formatted-txt"), + "openconfig.proto_test_f.a": filepath.Join(TestRoot, "testdata", "proto", "proto_test_f.uncompressed.proto_test_f.a.formatted-txt"), + "openconfig.proto_test_f.a.c": filepath.Join(TestRoot, "testdata", "proto", "proto_test_f.uncompressed.proto_test_f.a.c.formatted-txt"), + }, + }, { + name: "yang schema with leafrefs that point to the same path", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-g.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{}, + ProtoOptions: ProtoOpts{ + GoPackageBase: "github.com/foo/baz", + NestedMessages: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig.proto_test_g": filepath.Join(TestRoot, "testdata", "proto", "proto-test-g.proto-test-g.formatted-txt"), + }, + }, { + name: "yang schema with fake root, path compression and union list key", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + }, + }, + ProtoOptions: ProtoOpts{ + AnnotateSchemaPaths: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.compressed.openconfig.formatted-txt"), + "openconfig.routing_policy": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.compressed.openconfig.routing_policy.formatted-txt"), + }, + }, { + name: "yang schema with fakeroot, and union list key", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + }, + }, + ProtoOptions: ProtoOpts{ + AnnotateSchemaPaths: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list_key.uncompressed.openconfig.formatted-txt"), + "openconfig.proto_union_list_key": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.formatted-txt"), + "openconfig.proto_union_list_key.routing_policy": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.formatted-txt"), + "openconfig.proto_union_list_key.routing_policy.policies": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.formatted-txt"), + "openconfig.proto_union_list_key.routing_policy.policies.policy": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.policy.formatted-txt"), + "openconfig.proto_union_list_key.routing_policy.sets": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.sets.formatted-txt"), + }, + }, { + name: "enums: yang schema with various types of enums with underscores", + inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-enums.yang")}, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + UseDefiningModuleForTypedefEnumNames: true, + }, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-enums.enums.formatted-txt"), + "openconfig.proto_enums": filepath.Join(TestRoot, "testdata", "proto", "proto-enums.formatted-txt"), + }, + }, { + name: "enums: yang schema with identity that adds to previous module", + inFiles: []string{ + filepath.Join(TestRoot, "testdata", "proto", "proto-enums.yang"), + filepath.Join(TestRoot, "testdata", "proto", "proto-enums-addid.yang"), + }, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + UseDefiningModuleForTypedefEnumNames: true, + }, + }, + ProtoOptions: ProtoOpts{ + AnnotateEnumNames: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-enums-addid.enums.formatted-txt"), + "openconfig.proto_enums": filepath.Join(TestRoot, "testdata", "proto", "proto-enums-addid.formatted-txt"), + }, + }, { + name: "yang schema with nested messages requested - uncompressed with fakeroot", + inFiles: []string{ + filepath.Join(TestRoot, "testdata", "proto", "nested-messages.yang"), + }, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + UseDefiningModuleForTypedefEnumNames: true, + }, + }, + ProtoOptions: ProtoOpts{ + AnnotateEnumNames: true, + AnnotateSchemaPaths: true, + NestedMessages: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.openconfig.formatted-txt"), + "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.enums.formatted-txt"), + "openconfig.nested_messages": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.nested_messages.formatted-txt"), + }, + }, { + name: "yang schema with nested messages - compressed with fakeroot", + inFiles: []string{ + filepath.Join(TestRoot, "testdata", "proto", "nested-messages.yang"), + }, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + CompressBehaviour: genutil.PreferIntendedConfig, + GenerateFakeRoot: true, + UseDefiningModuleForTypedefEnumNames: true, + }, + }, + ProtoOptions: ProtoOpts{ + AnnotateEnumNames: true, + AnnotateSchemaPaths: true, + NestedMessages: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.compressed.enums.formatted-txt"), + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.compressed.nested_messages.formatted-txt"), + }, + }, { + name: "yang schema with a leafref key to a union with enumeration", + inFiles: []string{ + filepath.Join(TestRoot, "testdata", "proto", "union-list-key.yang"), + }, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + UseDefiningModuleForTypedefEnumNames: true, + }, + }, + ProtoOptions: ProtoOpts{ + AnnotateEnumNames: true, + AnnotateSchemaPaths: true, + NestedMessages: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "union-list-key.enums.formatted-txt"), + "openconfig.union_list_key": filepath.Join(TestRoot, "testdata", "proto", "union-list-key.union_list_key.formatted-txt"), + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "union-list-key.formatted-txt"), + }, + }, { + name: "protobuf generation with excluded read only fields - compressed", + inFiles: []string{ + filepath.Join(datapath, "openconfig-config-false.yang"), + }, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + CompressBehaviour: genutil.UncompressedExcludeDerivedState, + }, + }, + ProtoOptions: ProtoOpts{ + AnnotateEnumNames: true, + AnnotateSchemaPaths: true, + NestedMessages: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "excluded-config-false.compressed.formatted-txt"), + "openconfig.openconfig_config_false": filepath.Join(TestRoot, "testdata", "proto", "excluded-config-false.config_false.compressed.formatted-txt"), + }, + }, { + name: "protobuf generation with excluded read only fields - compressed", + inFiles: []string{ + filepath.Join(datapath, "openconfig-config-false.yang"), + }, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + CompressBehaviour: genutil.ExcludeDerivedState, + }, + }, + ProtoOptions: ProtoOpts{ + AnnotateEnumNames: true, + AnnotateSchemaPaths: true, + NestedMessages: true, + GoPackageBase: "github.com/openconfig/a/package", + }, + }, + wantOutputFiles: map[string]string{ + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "excluded-config-false.uncompressed.formatted-txt"), + }, + }, { + name: "protobuf generation with leafref to a module excluded by the test", + inFiles: []string{ + filepath.Join(TestRoot, "testdata", "proto", "cross-ref-target.yang"), + filepath.Join(TestRoot, "testdata", "proto", "cross-ref-src.yang"), + }, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + ParseOptions: ygen.ParseOpts{ + ExcludeModules: []string{"cross-ref-target"}, + }, + }, + ProtoOptions: ProtoOpts{ + NestedMessages: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig.cross_ref_src": filepath.Join(TestRoot, "testdata", "proto", "cross-ref-src.formatted-txt"), + }, + }, { + name: "multimod with fakeroot and nested", + inFiles: []string{ + filepath.Join(TestRoot, "testdata", "proto", "fakeroot-multimod-one.yang"), + filepath.Join(TestRoot, "testdata", "proto", "fakeroot-multimod-two.yang"), + }, + inConfig: CodeGenerator{ + IROptions: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ + GenerateFakeRoot: true, + CompressBehaviour: genutil.PreferIntendedConfig, + }, + }, + ProtoOptions: ProtoOpts{ + NestedMessages: true, + AnnotateEnumNames: true, + AnnotateSchemaPaths: true, + }, + }, + wantOutputFiles: map[string]string{ + "openconfig": filepath.Join(TestRoot, "testdata", "proto", "fakeroot-multimod.formatted-txt"), + }, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + sortedPkgNames := func(pkgs map[string]string) []string { + wantPkgs := []string{} + for k := range tt.wantOutputFiles { + wantPkgs = append(wantPkgs, k) + } + sort.Strings(wantPkgs) + return wantPkgs + } + + genCode := func() *GeneratedCode { + if tt.inConfig.Caller == "" { + // Override the caller if it is not set, to ensure that test + // output is deterministic. + tt.inConfig.Caller = "codegen-tests" + } + + cg := New(tt.inConfig.Caller, tt.inConfig.IROptions, tt.inConfig.ProtoOptions) + gotProto, err := cg.Generate(tt.inFiles, tt.inIncludePaths) + if (err != nil) != tt.wantErr { + t.Fatalf("cg.Generate(%v, %v), config: %v: got unexpected error: %v", tt.inFiles, tt.inIncludePaths, tt.inConfig, err) + } + + if tt.wantErr || err != nil { + return nil + } + + return gotProto + } + + gotProto := genCode() + + allCode := bytes.Buffer{} + + seenPkg := map[string]bool{} + for n := range gotProto.Packages { + seenPkg[n] = false + } + + protoPkgs := func(m map[string]Proto3Package) []string { + a := []string{} + for k := range m { + a = append(a, k) + } + return a + } + + wantPkgs := sortedPkgNames(tt.wantOutputFiles) + for _, pkg := range wantPkgs { + wantFile := tt.wantOutputFiles[pkg] + wantCodeBytes, err := ioutil.ReadFile(wantFile) + if err != nil { + t.Errorf("%s: ioutil.ReadFile(%v): could not read file for package %s", tt.name, wantFile, pkg) + return + } + + gotPkg, ok := gotProto.Packages[pkg] + if !ok { + t.Fatalf("%s: cg.Generate(%v, %v): did not find expected package %s in output, got: %#v, want key: %v", tt.name, tt.inFiles, tt.inIncludePaths, pkg, protoPkgs(gotProto.Packages), pkg) + } + + // Mark this package as having been seen. + seenPkg[pkg] = true + + // Write the returned struct out to a buffer to compare with the + // testdata file. + var gotCodeBuf bytes.Buffer + fmt.Fprintf(&gotCodeBuf, gotPkg.Header) + + for _, gotMsg := range gotPkg.Messages { + fmt.Fprintf(&gotCodeBuf, "%s\n", gotMsg) + } + + for _, gotEnum := range gotPkg.Enums { + fmt.Fprintf(&gotCodeBuf, "%s", gotEnum) + } + + wantCode := string(wantCodeBytes) + + allCode.WriteString(gotCodeBuf.String()) + + if diff := cmp.Diff(gotCodeBuf.String(), wantCode); diff != "" { + if diffl, _ := testutil.GenerateUnifiedDiff(wantCode, gotCodeBuf.String()); diffl != "" { + diff = diffl + } + t.Errorf("%s: cg.Generate(%v, %v) for package %s, did not get expected code (code file: %v), diff(-want, +got):\n%s", tt.name, tt.inFiles, tt.inIncludePaths, pkg, wantFile, diff) + } + } + + for pkg, seen := range seenPkg { + if !seen { + t.Errorf("%s: cg.Generate(%v, %v) did not test received package %v", tt.name, tt.inFiles, tt.inIncludePaths, pkg) + } + } + + for i := 0; i < deflakeRuns; i++ { + got := genCode() + var gotCodeBuf bytes.Buffer + + wantPkgs := sortedPkgNames(tt.wantOutputFiles) + for _, pkg := range wantPkgs { + gotPkg, ok := got.Packages[pkg] + if !ok { + t.Fatalf("%s: cg.Generate(%v, %v): did not find expected package %s in output, got: %#v, want key: %v", tt.name, tt.inFiles, tt.inIncludePaths, pkg, protoPkgs(gotProto.Packages), pkg) + } + fmt.Fprintf(&gotCodeBuf, gotPkg.Header) + for _, gotMsg := range gotPkg.Messages { + fmt.Fprintf(&gotCodeBuf, "%s\n", gotMsg) + } + for _, gotEnum := range gotPkg.Enums { + fmt.Fprintf(&gotCodeBuf, "%s", gotEnum) + } + } + + if diff := cmp.Diff(gotCodeBuf.String(), allCode.String()); diff != "" { + diff, _ = testutil.GenerateUnifiedDiff(allCode.String(), gotCodeBuf.String()) + t.Fatalf("flaky code generation iter: %d, diff(-want, +got):\n%s", i, diff) + } + } + }) + } +} diff --git a/ygen/genir_test.go b/protogen/genir_test.go similarity index 85% rename from ygen/genir_test.go rename to protogen/genir_test.go index 9da740031..8a97a529e 100644 --- a/ygen/genir_test.go +++ b/protogen/genir_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ygen +package protogen import ( "path/filepath" @@ -23,26 +23,30 @@ import ( "github.com/openconfig/gnmi/errdiff" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/ygen" "github.com/openconfig/ygot/ygot" "google.golang.org/protobuf/testing/protocmp" ) -func protoIR(nestedDirectories bool) *IR { +// datapath is the path to common YANG test modules. +const datapath = "../testdata/modules" + +func protoIR(nestedDirectories bool) *ygen.IR { packageName := "model" if nestedDirectories { packageName = "" } - return &IR{ - Directories: map[string]*ParsedDirectory{ + return &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/device": { Name: "Device", - Type: Container, + Type: ygen.Container, Path: "/device", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "model": { Name: "model", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "model", Defaults: nil, BelongingModule: "openconfig-complex", @@ -53,7 +57,7 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: ContainerNode, + Type: ygen.ContainerNode, LangType: nil, MappedPaths: [][]string{{"", "model"}}, MappedPathModules: [][]string{{"", "openconfig-complex"}}, @@ -62,7 +66,7 @@ func protoIR(nestedDirectories bool) *IR { }, "example-presence": { Name: "example_presence", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "example-presence", Defaults: nil, BelongingModule: "openconfig-complex", @@ -74,7 +78,7 @@ func protoIR(nestedDirectories bool) *IR { PresenceStatement: ygot.String("This is an example presence container"), Description: "", }, - Type: ContainerNode, + Type: ygen.ContainerNode, LangType: nil, MappedPaths: [][]string{{"", "example-presence"}}, MappedPathModules: [][]string{{"", "openconfig-complex"}}, @@ -87,9 +91,9 @@ func protoIR(nestedDirectories bool) *IR { }, "/openconfig-complex/example-presence": { Name: "ExamplePresence", - Type: Container, + Type: ygen.Container, Path: "/openconfig-complex/example-presence", - Fields: map[string]*NodeDetails{}, + Fields: map[string]*ygen.NodeDetails{}, PackageName: "", ListKeys: nil, IsFakeRoot: false, @@ -99,12 +103,12 @@ func protoIR(nestedDirectories bool) *IR { }, "/openconfig-complex/model": { Name: "Model", - Type: Container, + Type: ygen.Container, Path: "/openconfig-complex/model", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "anydata-leaf": { Name: "anydata_leaf", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "anydata-leaf", Defaults: nil, BelongingModule: "openconfig-complex", @@ -115,7 +119,7 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "some anydata leaf", }, - Type: AnyDataNode, + Type: ygen.AnyDataNode, LangType: nil, MappedPaths: [][]string{{"", "model", "anydata-leaf"}}, MappedPathModules: [][]string{{"", "openconfig-complex", "openconfig-complex"}}, @@ -124,7 +128,7 @@ func protoIR(nestedDirectories bool) *IR { }, "dateref": { Name: "dateref", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "dateref", Defaults: nil, BelongingModule: "openconfig-complex", @@ -135,8 +139,8 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "/openconfig-complex/model/a/single-key/config/dates", Description: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.UintValue", ZeroValue: "", DefaultValue: nil, @@ -148,7 +152,7 @@ func protoIR(nestedDirectories bool) *IR { }, "multi-key": { Name: "multi_key", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "multi-key", Defaults: nil, BelongingModule: "openconfig-complex", @@ -159,7 +163,7 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: ListNode, + Type: ygen.ListNode, LangType: nil, MappedPaths: [][]string{{"", "model", "b", "multi-key"}}, MappedPathModules: [][]string{{"", "openconfig-complex", "openconfig-complex", "openconfig-complex"}}, @@ -168,7 +172,7 @@ func protoIR(nestedDirectories bool) *IR { }, "single-key": { Name: "single_key", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "single-key", Defaults: nil, BelongingModule: "openconfig-complex", @@ -179,7 +183,7 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: ListNode, + Type: ygen.ListNode, LangType: nil, MappedPaths: [][]string{{"", "model", "a", "single-key"}}, MappedPathModules: [][]string{{"", "openconfig-complex", "openconfig-complex", "openconfig-complex"}}, @@ -188,7 +192,7 @@ func protoIR(nestedDirectories bool) *IR { }, "unkeyed-list": { Name: "unkeyed_list", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "unkeyed-list", BelongingModule: "openconfig-complex", RootElementModule: "openconfig-complex", @@ -196,7 +200,7 @@ func protoIR(nestedDirectories bool) *IR { Path: "/openconfig-complex/model/c/unkeyed-list", SchemaPath: "/model/c/unkeyed-list", }, - Type: ListNode, + Type: ygen.ListNode, MappedPaths: [][]string{{"", "model", "c", "unkeyed-list"}}, MappedPathModules: [][]string{{"", "openconfig-complex", "openconfig-complex", "openconfig-complex"}}, }, @@ -209,12 +213,12 @@ func protoIR(nestedDirectories bool) *IR { }, "/openconfig-complex/model/a/single-key": { Name: "SingleKey", - Type: List, + Type: ygen.List, Path: "/openconfig-complex/model/a/single-key", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "dates": { Name: "dates", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "dates", Defaults: []string{"5"}, BelongingModule: "openconfig-complex", @@ -226,8 +230,8 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafListNode, - LangType: &MappedType{ + Type: ygen.LeafListNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.UintValue", ZeroValue: "", DefaultValue: nil, @@ -239,7 +243,7 @@ func protoIR(nestedDirectories bool) *IR { }, "dates-with-defaults": { Name: "dates_with_defaults", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "dates-with-defaults", Defaults: []string{"1", "2"}, BelongingModule: "openconfig-complex", @@ -251,8 +255,8 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafListNode, - LangType: &MappedType{ + Type: ygen.LeafListNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.UintValue", ZeroValue: "", DefaultValue: nil, @@ -264,7 +268,7 @@ func protoIR(nestedDirectories bool) *IR { }, "iref": { Name: "iref", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "iref", Defaults: nil, BelongingModule: "openconfig-complex", @@ -276,8 +280,8 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "openconfig.enums.ComplexSOFTWARE", UnionTypes: nil, IsEnumeratedValue: true, @@ -304,9 +308,51 @@ func protoIR(nestedDirectories bool) *IR { }, }, }, + "iref2": { + Name: "iref2", + YANGDetails: ygen.YANGNodeDetails{ + Name: "iref2", + Defaults: nil, + BelongingModule: "openconfig-complex", + RootElementModule: "openconfig-complex", + DefiningModule: "openconfig-complex", + Path: "/openconfig-complex/model/a/single-key/config/iref2", + SchemaPath: "/model/a/single-key/config/iref2", + ShadowSchemaPath: "/model/a/single-key/state/iref2", + LeafrefTargetPath: "", + Description: "", + }, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + NativeType: "openconfig.enums.ComplexProgram", + UnionTypes: nil, + IsEnumeratedValue: true, + EnumeratedYANGTypeKey: "/openconfig-complex/program", + ZeroValue: "", + DefaultValue: nil, + }, + MappedPaths: [][]string{ + {"", "model", "a", "single-key", "config", "iref2"}, + }, + MappedPathModules: [][]string{ + { + "", "openconfig-complex", "openconfig-complex", "openconfig-complex", + "openconfig-complex", "openconfig-complex", + }, + }, + ShadowMappedPaths: [][]string{ + {"", "model", "a", "single-key", "state", "iref2"}, + }, + ShadowMappedPathModules: [][]string{ + { + "", "openconfig-complex", "openconfig-complex", "openconfig-complex", + "openconfig-complex", "openconfig-complex", + }, + }, + }, "key": { Name: "key", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "key", Defaults: nil, BelongingModule: "openconfig-complex", @@ -318,10 +364,10 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "", - UnionTypes: map[string]MappedUnionSubtype{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "openconfig.enums.ComplexWeekendDays": { Index: 0, EnumeratedYANGTypeKey: "/openconfig-complex/weekend-days", @@ -373,7 +419,7 @@ func protoIR(nestedDirectories bool) *IR { }, "leaf-default-override": { Name: "leaf_default_override", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "leaf-default-override", Defaults: []string{"3"}, BelongingModule: "openconfig-complex", @@ -385,10 +431,10 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "", - UnionTypes: map[string]MappedUnionSubtype{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "openconfig.enums.ComplexCycloneScalesEnum": { Index: 0, EnumeratedYANGTypeKey: "/openconfig-complex/cyclone-scales", @@ -424,7 +470,7 @@ func protoIR(nestedDirectories bool) *IR { }, "simple-union-enum": { Name: "simple_union_enum", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "simple-union-enum", Defaults: []string{"TWO"}, BelongingModule: "openconfig-complex", @@ -436,10 +482,10 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "", - UnionTypes: map[string]MappedUnionSubtype{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "SimpleUnionEnumEnum": { Index: 0, EnumeratedYANGTypeKey: "/openconfig-complex/single-key-config/simple-union-enum", @@ -475,7 +521,7 @@ func protoIR(nestedDirectories bool) *IR { }, "singleton-union-enum": { Name: "singleton_union_enum", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "singleton-union-enum", Defaults: []string{"DEUX"}, BelongingModule: "openconfig-complex", @@ -487,8 +533,8 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "SingletonUnionEnumEnum", UnionTypes: nil, IsEnumeratedValue: true, @@ -517,7 +563,7 @@ func protoIR(nestedDirectories bool) *IR { }, "typedef-enum": { Name: "typedef_enum", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "typedef-enum", Defaults: []string{"SATURDAY"}, BelongingModule: "openconfig-complex", @@ -529,8 +575,8 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "openconfig.enums.ComplexWeekendDays", UnionTypes: nil, IsEnumeratedValue: true, @@ -559,7 +605,7 @@ func protoIR(nestedDirectories bool) *IR { }, "typedef-union-enum": { Name: "typedef_union_enum", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "typedef-union-enum", Defaults: []string{"SUPER"}, BelongingModule: "openconfig-complex", @@ -571,10 +617,10 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "", - UnionTypes: map[string]MappedUnionSubtype{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ // protoLangMapper sorts by name instead of YANG order. "openconfig.enums.ComplexCycloneScalesEnum": { Index: 0, @@ -610,12 +656,12 @@ func protoIR(nestedDirectories bool) *IR { }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "key": { Name: "key", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "", - UnionTypes: map[string]MappedUnionSubtype{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "openconfig.enums.ComplexWeekendDays": { Index: 0, EnumeratedYANGTypeKey: "/openconfig-complex/weekend-days", @@ -638,12 +684,12 @@ func protoIR(nestedDirectories bool) *IR { }, "/openconfig-complex/model/b/multi-key": { Name: "MultiKey", - Type: List, + Type: ygen.List, Path: "/openconfig-complex/model/b/multi-key", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "key1": { Name: "key1", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "key1", Defaults: nil, BelongingModule: "openconfig-complex", @@ -655,8 +701,8 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafNode, - LangType: &MappedType{NativeType: "ywrapper.UintValue"}, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{NativeType: "ywrapper.UintValue"}, MappedPaths: [][]string{ {"", "model", "b", "multi-key", "config", "key1"}, {"", "model", "b", "multi-key", "key1"}, @@ -694,7 +740,7 @@ func protoIR(nestedDirectories bool) *IR { }, "key2": { Name: "key2", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "key2", Defaults: nil, BelongingModule: "openconfig-complex", @@ -706,8 +752,8 @@ func protoIR(nestedDirectories bool) *IR { LeafrefTargetPath: "", Description: "", }, - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "Key2", IsEnumeratedValue: true, EnumeratedYANGTypeKey: "/openconfig-complex/multi-key-config/key2", @@ -749,14 +795,14 @@ func protoIR(nestedDirectories bool) *IR { }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "key1": { Name: "key1", - LangType: &MappedType{NativeType: "uint64", ZeroValue: ""}, + LangType: &ygen.MappedType{NativeType: "uint64", ZeroValue: ""}, }, "key2": { Name: "key2", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "Key2", IsEnumeratedValue: true, EnumeratedYANGTypeKey: "/openconfig-complex/multi-key-config/key2", @@ -773,12 +819,12 @@ func protoIR(nestedDirectories bool) *IR { }, "/openconfig-complex/model/c/unkeyed-list": { Name: "UnkeyedList", - Type: List, + Type: ygen.List, Path: "/openconfig-complex/model/c/unkeyed-list", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "field": { Name: "field", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "field", BelongingModule: "openconfig-complex", RootElementModule: "openconfig-complex", @@ -786,8 +832,8 @@ func protoIR(nestedDirectories bool) *IR { Path: "/openconfig-complex/model/c/unkeyed-list/field", SchemaPath: "/model/c/unkeyed-list/field", }, - Type: LeafNode, - LangType: &MappedType{NativeType: "ywrapper.BytesValue"}, + Type: ygen.LeafNode, + LangType: &ygen.MappedType{NativeType: "ywrapper.BytesValue"}, MappedPaths: [][]string{{"", "model", "c", "unkeyed-list", "field"}}, MappedPathModules: [][]string{{"", "openconfig-complex", "openconfig-complex", "openconfig-complex", "openconfig-complex"}}, }, @@ -799,10 +845,10 @@ func protoIR(nestedDirectories bool) *IR { ConfigFalse: true, }, }, - Enums: map[string]*EnumeratedYANGType{ + Enums: map[string]*ygen.EnumeratedYANGType{ "/openconfig-complex/cyclone-scales": { Name: "ComplexCycloneScalesEnum", - Kind: DerivedUnionEnumerationType, + Kind: ygen.DerivedUnionEnumerationType, TypeName: "cyclone-scales", ValToYANGDetails: []ygot.EnumDefinition{ { @@ -819,16 +865,25 @@ func protoIR(nestedDirectories bool) *IR { }, "/openconfig-complex/SOFTWARE": { Name: "ComplexSOFTWARE", - Kind: IdentityType, + Kind: ygen.IdentityType, IdentityBaseName: "SOFTWARE", TypeName: "identityref", ValToYANGDetails: []ygot.EnumDefinition{ {Name: "OS", DefiningModule: "openconfig-complex"}, }, }, + "/openconfig-complex/program": { + Name: "ComplexProgram", + Kind: ygen.IdentityType, + IdentityBaseName: "SOFTWARE", + TypeName: "program", + ValToYANGDetails: []ygot.EnumDefinition{ + {Name: "OS", DefiningModule: "openconfig-complex"}, + }, + }, "/openconfig-complex/multi-key-config/key2": { Name: "MultiKeyKey2", - Kind: SimpleEnumerationType, + Kind: ygen.SimpleEnumerationType, TypeName: "enumeration", ValToYANGDetails: []ygot.EnumDefinition{ { @@ -845,7 +900,7 @@ func protoIR(nestedDirectories bool) *IR { }, "/openconfig-complex/weekend-days": { Name: "ComplexWeekendDays", - Kind: DerivedEnumerationType, + Kind: ygen.DerivedEnumerationType, TypeName: "weekend-days", ValToYANGDetails: []ygot.EnumDefinition{ { @@ -862,7 +917,7 @@ func protoIR(nestedDirectories bool) *IR { }, "/openconfig-complex/single-key-config/simple-union-enum": { Name: "SingleKeySimpleUnionEnumEnum", - Kind: UnionEnumerationType, + Kind: ygen.UnionEnumerationType, TypeName: "union", ValToYANGDetails: []ygot.EnumDefinition{ { @@ -884,7 +939,7 @@ func protoIR(nestedDirectories bool) *IR { }, "/openconfig-complex/single-key-config/singleton-union-enum": { Name: "SingleKeySingletonUnionEnumEnum", - Kind: UnionEnumerationType, + Kind: ygen.UnionEnumerationType, TypeName: "union", ValToYANGDetails: []ygot.EnumDefinition{ { @@ -915,20 +970,20 @@ func TestGenerateIR(t *testing.T) { inYANGFiles []string inIncludePaths []string inExcludeModules []string - inLangMapper LangMapper - inOpts IROptions - wantIR *IR + inLangMapper ygen.LangMapper + inOpts ygen.IROptions + wantIR *ygen.IR wantErrSubstring string }{{ desc: "complex openconfig test with compression using ProtoLangMapper with nested directories", inYANGFiles: []string{filepath.Join(datapath, "openconfig-complex.yang")}, - inLangMapper: func() LangMapper { + inLangMapper: func() ygen.LangMapper { return NewProtoLangMapper(DefaultBasePackageName, DefaultEnumPackageName) }(), - inOpts: IROptions{ + inOpts: ygen.IROptions{ NestedDirectories: true, AbsoluteMapPaths: true, - TransformationOptions: TransformationOpts{ + TransformationOptions: ygen.TransformationOpts{ CompressBehaviour: genutil.PreferIntendedConfig, ShortenEnumLeafNames: true, EnumOrgPrefixesToTrim: []string{"openconfig"}, @@ -942,13 +997,13 @@ func TestGenerateIR(t *testing.T) { }, { desc: "complex openconfig test with compression using ProtoLangMapper", inYANGFiles: []string{filepath.Join(datapath, "openconfig-complex.yang")}, - inLangMapper: func() LangMapper { + inLangMapper: func() ygen.LangMapper { return NewProtoLangMapper(DefaultBasePackageName, DefaultEnumPackageName) }(), - inOpts: IROptions{ + inOpts: ygen.IROptions{ NestedDirectories: false, AbsoluteMapPaths: true, - TransformationOptions: TransformationOpts{ + TransformationOptions: ygen.TransformationOpts{ CompressBehaviour: genutil.PreferIntendedConfig, ShortenEnumLeafNames: true, EnumOrgPrefixesToTrim: []string{"openconfig"}, @@ -965,11 +1020,11 @@ func TestGenerateIR(t *testing.T) { filepath.Join(datapath, "openconfig-simple.yang"), filepath.Join(datapath, "openconfig-simple-augment2.yang"), }, - inLangMapper: func() LangMapper { + inLangMapper: func() ygen.LangMapper { return NewProtoLangMapper(DefaultBasePackageName, DefaultEnumPackageName) }(), - inOpts: IROptions{ - TransformationOptions: TransformationOpts{ + inOpts: ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ CompressBehaviour: genutil.Uncompressed, ShortenEnumLeafNames: true, EnumOrgPrefixesToTrim: []string{"openconfig"}, @@ -979,16 +1034,16 @@ func TestGenerateIR(t *testing.T) { }, AppendEnumSuffixForSimpleUnionEnums: true, }, - wantIR: &IR{ - Directories: map[string]*ParsedDirectory{ + wantIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/device": { Name: "Device", - Type: Container, + Type: ygen.Container, Path: "/device", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "parent": { Name: "parent", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "parent", Defaults: nil, BelongingModule: "openconfig-simple", @@ -999,7 +1054,7 @@ func TestGenerateIR(t *testing.T) { LeafrefTargetPath: "", Description: "I am a parent container\nthat has 4 children.", }, - Type: ContainerNode, + Type: ygen.ContainerNode, MappedPaths: [][]string{{"parent"}}, MappedPathModules: [][]string{{"openconfig-simple"}}, ShadowMappedPaths: nil, @@ -1007,7 +1062,7 @@ func TestGenerateIR(t *testing.T) { }, "remote-container": { Name: "remote_container", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "remote-container", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1018,7 +1073,7 @@ func TestGenerateIR(t *testing.T) { LeafrefTargetPath: "", Description: "", }, - Type: ContainerNode, + Type: ygen.ContainerNode, MappedPaths: [][]string{{"remote-container"}}, MappedPathModules: [][]string{{"openconfig-simple"}}, ShadowMappedPaths: nil, @@ -1029,12 +1084,12 @@ func TestGenerateIR(t *testing.T) { }, "/openconfig-simple/parent": { Name: "Parent", - Type: Container, + Type: ygen.Container, Path: "/openconfig-simple/parent", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "child": { Name: "child", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "child", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1045,7 +1100,7 @@ func TestGenerateIR(t *testing.T) { LeafrefTargetPath: "", Description: "", }, - Type: ContainerNode, + Type: ygen.ContainerNode, LangType: nil, MappedPaths: [][]string{{"child"}}, MappedPathModules: [][]string{{"openconfig-simple"}}, @@ -1060,12 +1115,12 @@ func TestGenerateIR(t *testing.T) { }, "/openconfig-simple/parent/child": { Name: "Child", - Type: Container, + Type: ygen.Container, Path: "/openconfig-simple/parent/child", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "config": { Name: "config", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "config", BelongingModule: "openconfig-simple", RootElementModule: "openconfig-simple", @@ -1081,7 +1136,7 @@ func TestGenerateIR(t *testing.T) { }, "state": { Name: "state", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "state", BelongingModule: "openconfig-simple", RootElementModule: "openconfig-simple", @@ -1104,12 +1159,12 @@ func TestGenerateIR(t *testing.T) { }, "/openconfig-simple/parent/child/config": { Name: "Config", - Type: Container, + Type: ygen.Container, Path: "/openconfig-simple/parent/child/config", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "four": { Name: "four", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "four", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1121,7 +1176,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "ywrapper.BytesValue", UnionTypes: nil, IsEnumeratedValue: false, @@ -1135,7 +1190,7 @@ func TestGenerateIR(t *testing.T) { }, "one": { Name: "one", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "one", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1147,7 +1202,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", UnionTypes: nil, IsEnumeratedValue: false, @@ -1161,7 +1216,7 @@ func TestGenerateIR(t *testing.T) { }, "three": { Name: "three", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "three", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1173,7 +1228,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "Three", UnionTypes: nil, IsEnumeratedValue: true, @@ -1194,12 +1249,12 @@ func TestGenerateIR(t *testing.T) { }, "/openconfig-simple/parent/child/state": { Name: "State", - Type: Container, + Type: ygen.Container, Path: "/openconfig-simple/parent/child/state", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "four": { Name: "four", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "four", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1211,7 +1266,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "ywrapper.BytesValue", UnionTypes: nil, IsEnumeratedValue: false, @@ -1225,7 +1280,7 @@ func TestGenerateIR(t *testing.T) { }, "one": { Name: "one", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "one", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1237,7 +1292,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", UnionTypes: nil, IsEnumeratedValue: false, @@ -1251,7 +1306,7 @@ func TestGenerateIR(t *testing.T) { }, "three": { Name: "three", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "three", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1263,7 +1318,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "Three", UnionTypes: nil, IsEnumeratedValue: true, @@ -1278,7 +1333,7 @@ func TestGenerateIR(t *testing.T) { }, "two": { Name: "two", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "two", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1290,7 +1345,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", UnionTypes: nil, IsEnumeratedValue: false, @@ -1304,7 +1359,7 @@ func TestGenerateIR(t *testing.T) { }, "zero": { Name: "zero", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "zero", Defaults: nil, BelongingModule: "openconfig-simple-augment2", @@ -1316,7 +1371,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", UnionTypes: nil, IsEnumeratedValue: false, @@ -1338,12 +1393,12 @@ func TestGenerateIR(t *testing.T) { }, "/openconfig-simple/remote-container": { Name: "RemoteContainer", - Type: Container, + Type: ygen.Container, Path: "/openconfig-simple/remote-container", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "config": { Name: "config", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "config", BelongingModule: "openconfig-simple", RootElementModule: "openconfig-simple", @@ -1359,7 +1414,7 @@ func TestGenerateIR(t *testing.T) { }, "state": { Name: "state", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "state", BelongingModule: "openconfig-simple", RootElementModule: "openconfig-simple", @@ -1382,12 +1437,12 @@ func TestGenerateIR(t *testing.T) { }, "/openconfig-simple/remote-container/config": { Name: "Config", - Type: Container, + Type: ygen.Container, Path: "/openconfig-simple/remote-container/config", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "a-leaf": { Name: "a_leaf", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "a-leaf", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1399,7 +1454,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", UnionTypes: nil, IsEnumeratedValue: false, @@ -1419,12 +1474,12 @@ func TestGenerateIR(t *testing.T) { }, "/openconfig-simple/remote-container/state": { Name: "State", - Type: Container, + Type: ygen.Container, Path: "/openconfig-simple/remote-container/state", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "a-leaf": { Name: "a_leaf", - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "a-leaf", Defaults: nil, BelongingModule: "openconfig-simple", @@ -1436,7 +1491,7 @@ func TestGenerateIR(t *testing.T) { Description: "", }, Type: 3, - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", UnionTypes: nil, IsEnumeratedValue: false, @@ -1456,7 +1511,7 @@ func TestGenerateIR(t *testing.T) { ConfigFalse: true, }, }, - Enums: map[string]*EnumeratedYANGType{ + Enums: map[string]*ygen.EnumeratedYANGType{ "/openconfig-simple/parent-config/three": { Name: "Simple_Parent_Child_Config_Three", Kind: 1, @@ -1477,11 +1532,11 @@ func TestGenerateIR(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { tt.inOpts.ParseOptions.ExcludeModules = tt.inExcludeModules - got, err := GenerateIR(tt.inYANGFiles, tt.inIncludePaths, tt.inLangMapper, tt.inOpts) + got, err := ygen.GenerateIR(tt.inYANGFiles, tt.inIncludePaths, tt.inLangMapper, tt.inOpts) if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" { t.Fatalf("did not get expected error, %s", diff) } - if diff := cmp.Diff(got, tt.wantIR, cmpopts.IgnoreUnexported(IR{}, ParsedDirectory{}, EnumeratedYANGType{}), protocmp.Transform()); diff != "" { + if diff := cmp.Diff(got, tt.wantIR, cmpopts.IgnoreUnexported(ygen.IR{}, ygen.ParsedDirectory{}, ygen.EnumeratedYANGType{}), protocmp.Transform()); diff != "" { t.Fatalf("did not get expected IR, diff(-got,+want):\n%s", diff) } }) diff --git a/protogen/helpers.go b/protogen/helpers.go new file mode 100644 index 000000000..7c23cbfcb --- /dev/null +++ b/protogen/helpers.go @@ -0,0 +1,21 @@ +package protogen + +// addNewKeys appends entries from the newKeys string slice to the +// existing map if the entry is not an existing key. The existing +// map is modified in place. +func addNewKeys(existing map[string]interface{}, newKeys []string) { + for _, n := range newKeys { + if _, ok := existing[n]; !ok { + existing[n] = true + } + } +} + +// stringKeys returns the keys of the supplied map as a slice of strings. +func stringKeys(m map[string]interface{}) []string { + var ss []string + for k := range m { + ss = append(ss, k) + } + return ss +} diff --git a/ygen/protoelements.go b/protogen/protoelements.go similarity index 89% rename from ygen/protoelements.go rename to protogen/protoelements.go index 895f9e971..4bce3a97f 100644 --- a/ygen/protoelements.go +++ b/protogen/protoelements.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ygen +package protogen import ( "fmt" @@ -23,10 +23,11 @@ import ( "github.com/openconfig/ygot/genutil" "github.com/openconfig/ygot/internal/igenutil" "github.com/openconfig/ygot/util" + "github.com/openconfig/ygot/ygen" ) // Ensure at compile time that the ProtoLangMapper implements the LangMapper interface. -var _ LangMapper = &ProtoLangMapper{} +var _ ygen.LangMapper = &ProtoLangMapper{} // ProtoLangMapper contains the functionality and state for generating proto // names for the generated code. @@ -34,7 +35,7 @@ type ProtoLangMapper struct { // LangMapperBase being embedded is a requirement for ProtoLangMapper // to implement the LangMapper interface, and also gives it access to // built-in methods. - LangMapperBase + ygen.LangMapperBase // definedGlobals specifies the global proto names used during code // generation to avoid conflicts. @@ -64,7 +65,7 @@ type ProtoLangMapper struct { // UnimplementedLangMapperExt ensures GoLangMapper implements the // LangMapperExt interface for forwards compatibility. - UnimplementedLangMapperExt + ygen.UnimplementedLangMapperExt } // NewProtoLangMapper creates a new ProtoLangMapper instance, initialised with the @@ -84,6 +85,7 @@ func NewProtoLangMapper(basePackageName, enumPackageName string) *ProtoLangMappe // function which allows extra context to be handed on. This provides the ability // to use not only the YangType but also the yang.Entry that the type was part of // to resolve the possible type name. +// TODO(wenbli): Move this to igenutil to reduce duplication. type resolveTypeArgs struct { // yangType is a pointer to the yang.YangType that is to be mapped. yangType *yang.YangType @@ -111,9 +113,9 @@ func (s *ProtoLangMapper) FieldName(e *yang.Entry) (string, error) { return safeProtoIdentifierName(e.Name), nil } -// LeafType maps the input leaf entry to a MappedType object containing the +// LeafType maps the input leaf entry to a ygen.MappedType object containing the // type information about the field. -func (s *ProtoLangMapper) LeafType(e *yang.Entry, opts IROptions) (*MappedType, error) { +func (s *ProtoLangMapper) LeafType(e *yang.Entry, opts ygen.IROptions) (*ygen.MappedType, error) { protoType, err := s.yangTypeToProtoType(resolveTypeArgs{ yangType: e.Type, contextEntry: e, @@ -128,9 +130,9 @@ func (s *ProtoLangMapper) LeafType(e *yang.Entry, opts IROptions) (*MappedType, return protoType, err } -// LeafType maps the input list key entry to a MappedType object containing the +// LeafType maps the input list key entry to a ygen.MappedType object containing the // type information about the key field. -func (s *ProtoLangMapper) KeyLeafType(e *yang.Entry, opts IROptions) (*MappedType, error) { +func (s *ProtoLangMapper) KeyLeafType(e *yang.Entry, opts ygen.IROptions) (*ygen.MappedType, error) { scalarType, err := s.yangTypeToProtoScalarType(resolveTypeArgs{ yangType: e.Type, contextEntry: e, @@ -227,7 +229,7 @@ type resolveProtoTypeArgs struct { // to be represented by. The types that are used in the protobuf are wrapper // types as described in the YANG to Protobuf translation specification. // If the input type is not a Yenum, an error is returned. -func yangEnumTypeToProtoType(args resolveTypeArgs) (*MappedType, error) { +func yangEnumTypeToProtoType(args resolveTypeArgs) (*ygen.MappedType, error) { if args.yangType.Kind != yang.Yenum { return nil, fmt.Errorf("input type to yangEnumTypeToProtoType is not a Yenum: %s", args.contextEntry.Path()) } @@ -247,9 +249,9 @@ func yangEnumTypeToProtoType(args resolveTypeArgs) (*MappedType, error) { return nil, err } if definingType.Kind == yang.Yunion { - typeName += enumeratedUnionSuffix + typeName += ygen.EnumeratedUnionSuffix } - return &MappedType{ + return &ygen.MappedType{ NativeType: typeName, IsEnumeratedValue: true, }, nil @@ -265,7 +267,7 @@ func yangEnumTypeToProtoType(args resolveTypeArgs) (*MappedType, error) { // // See https://github.com/openconfig/ygot/blob/master/docs/yang-to-protobuf-transformations-spec.md // for additional details as to the transformation from YANG to Protobuf. -func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { +func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts ygen.IROptions) (*ygen.MappedType, error) { // Handle typedef cases. typedefName, key, isTypedef, err := s.EnumeratedTypedefTypeName(args.yangType, args.contextEntry, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { @@ -274,7 +276,7 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv // typedefName is set to non-empty-string when this was a valid enumeration // within a typedef. if isTypedef { - return &MappedType{ + return &ygen.MappedType{ NativeType: typedefName, IsEnumeratedValue: true, EnumeratedYANGTypeKey: key, @@ -283,17 +285,17 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv switch args.yangType.Kind { case yang.Yint8, yang.Yint16, yang.Yint32, yang.Yint64: - return &MappedType{NativeType: ywrapperAccessor + "IntValue"}, nil + return &ygen.MappedType{NativeType: ywrapperAccessor + "IntValue"}, nil case yang.Yuint8, yang.Yuint16, yang.Yuint32, yang.Yuint64: - return &MappedType{NativeType: ywrapperAccessor + "UintValue"}, nil + return &ygen.MappedType{NativeType: ywrapperAccessor + "UintValue"}, nil case yang.Ybinary: - return &MappedType{NativeType: ywrapperAccessor + "BytesValue"}, nil + return &ygen.MappedType{NativeType: ywrapperAccessor + "BytesValue"}, nil case yang.Ybool, yang.Yempty: - return &MappedType{NativeType: ywrapperAccessor + "BoolValue"}, nil + return &ygen.MappedType{NativeType: ywrapperAccessor + "BoolValue"}, nil case yang.Ystring: - return &MappedType{NativeType: ywrapperAccessor + "StringValue"}, nil + return &ygen.MappedType{NativeType: ywrapperAccessor + "StringValue"}, nil case yang.Ydecimal64: - return &MappedType{NativeType: ywrapperAccessor + "Decimal64Value"}, nil + return &ygen.MappedType{NativeType: ywrapperAccessor + "Decimal64Value"}, nil case yang.Yleafref: // We look up the leafref in the schema tree to be able to // determine what type to map to. @@ -307,7 +309,7 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv if err != nil { return nil, err } - _, key, err := s.EnumName(args.contextEntry, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, false, opts.TransformationOptions.EnumOrgPrefixesToTrim) + _, key, err := s.EnumName(args.contextEntry, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.TransformationOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, false, opts.TransformationOptions.EnumOrgPrefixesToTrim) if err != nil { return nil, err } @@ -323,7 +325,7 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv if err != nil { return nil, err } - return &MappedType{ + return &ygen.MappedType{ NativeType: n, IsEnumeratedValue: true, EnumeratedYANGTypeKey: key, @@ -344,7 +346,7 @@ func (s *ProtoLangMapper) yangTypeToProtoType(args resolveTypeArgs, pargs resolv // yangTypeToProtoScalarType takes an input resolveTypeArgs and returns the protobuf // in-built type that is used to represent it. It is used within list keys where the // value cannot be nil/unset. -func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { +func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts ygen.IROptions) (*ygen.MappedType, error) { // Handle typedef cases. typedefName, key, isTypedef, err := s.EnumeratedTypedefTypeName(args.yangType, args.contextEntry, fmt.Sprintf("%s.%s.", pargs.basePackageName, pargs.enumPackageName), true, true) if err != nil { @@ -353,7 +355,7 @@ func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs // typedefName is set to non-empty-string when this was a valid enumeration // within a typedef. if isTypedef { - return &MappedType{ + return &ygen.MappedType{ NativeType: typedefName, IsEnumeratedValue: true, EnumeratedYANGTypeKey: key, @@ -361,19 +363,19 @@ func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs } switch args.yangType.Kind { case yang.Yint8, yang.Yint16, yang.Yint32, yang.Yint64: - return &MappedType{NativeType: "sint64"}, nil + return &ygen.MappedType{NativeType: "sint64"}, nil case yang.Yuint8, yang.Yuint16, yang.Yuint32, yang.Yuint64: - return &MappedType{NativeType: "uint64"}, nil + return &ygen.MappedType{NativeType: "uint64"}, nil case yang.Ybinary: - return &MappedType{NativeType: "bytes"}, nil + return &ygen.MappedType{NativeType: "bytes"}, nil case yang.Ybool, yang.Yempty: - return &MappedType{NativeType: "bool"}, nil + return &ygen.MappedType{NativeType: "bool"}, nil case yang.Ystring: - return &MappedType{NativeType: "string"}, nil + return &ygen.MappedType{NativeType: "string"}, nil case yang.Ydecimal64: // Decimal64 continues to be a message even when we are mapping scalars // as there is not an equivalent Protobuf type. - return &MappedType{NativeType: ywrapperAccessor + "Decimal64Value"}, nil + return &ygen.MappedType{NativeType: ywrapperAccessor + "Decimal64Value"}, nil case yang.Yleafref: target, err := s.ResolveLeafrefTarget(args.yangType.Path, args.contextEntry) if err != nil { @@ -385,7 +387,7 @@ func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs if err != nil { return nil, err } - _, key, err := s.EnumName(args.contextEntry, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, false, opts.TransformationOptions.EnumOrgPrefixesToTrim) + _, key, err := s.EnumName(args.contextEntry, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.TransformationOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, false, opts.TransformationOptions.EnumOrgPrefixesToTrim) if err != nil { return nil, err } @@ -399,7 +401,7 @@ func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs if err != nil { return nil, err } - return &MappedType{ + return &ygen.MappedType{ NativeType: n, IsEnumeratedValue: true, EnumeratedYANGTypeKey: key, @@ -416,7 +418,7 @@ func (s *ProtoLangMapper) yangTypeToProtoScalarType(args resolveTypeArgs, pargs type unionSubtypeInfo struct { yangType *yang.YangType - mtype *MappedType + mtype *ygen.MappedType } // protoUnionType resolves the types that are included within the YangType in resolveTypeArgs into the @@ -438,8 +440,8 @@ type unionSubtypeInfo struct { // int32 a_int32 = NN; // } // -// The MappedType's UnionTypes can be output through a template into the oneof. -func (s *ProtoLangMapper) protoUnionType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts IROptions) (*MappedType, error) { +// The ygen.MappedType's UnionTypes can be output through a template into the oneof. +func (s *ProtoLangMapper) protoUnionType(args resolveTypeArgs, pargs resolveProtoTypeArgs, opts ygen.IROptions) (*ygen.MappedType, error) { unionTypes := make(map[string]unionSubtypeInfo) if errs := s.protoUnionSubTypes(args.yangType, args.contextEntry, unionTypes, pargs, opts); errs != nil { return nil, fmt.Errorf("errors mapping element: %v", errs) @@ -451,14 +453,14 @@ func (s *ProtoLangMapper) protoUnionType(args resolveTypeArgs, pargs resolveProt // Handle the case whereby there is an identityref and we simply // want to return the type that has been resolved. if t.yangType.Kind == yang.Yidentityref || t.yangType.Kind == yang.Yenum { - return &MappedType{ + return &ygen.MappedType{ NativeType: st, IsEnumeratedValue: true, EnumeratedYANGTypeKey: t.mtype.EnumeratedYANGTypeKey, }, nil } - var n *MappedType + var n *ygen.MappedType var err error // Resolve the type of the single type within the union according to whether // we want scalar types or not. This is used in contexts where there may @@ -483,11 +485,11 @@ func (s *ProtoLangMapper) protoUnionType(args resolveTypeArgs, pargs resolveProt } } - mtype := &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{}, + mtype := &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{}, } - // Rewrite the map to be the expected format for the MappedType return value, + // Rewrite the map to be the expected format for the ygen.MappedType return value, // we sort the keys into alphabetical order to avoid test flakes. keys := []string{} for k := range unionTypes { @@ -496,7 +498,7 @@ func (s *ProtoLangMapper) protoUnionType(args resolveTypeArgs, pargs resolveProt sort.Strings(keys) for _, k := range keys { - mtype.UnionTypes[k] = MappedUnionSubtype{ + mtype.UnionTypes[k] = ygen.MappedUnionSubtype{ Index: len(mtype.UnionTypes), EnumeratedYANGTypeKey: unionTypes[k].mtype.EnumeratedYANGTypeKey, } @@ -510,7 +512,7 @@ func (s *ProtoLangMapper) protoUnionType(args resolveTypeArgs, pargs resolveProt // with is required for mapping. The currentType map is updated as an in-out argument. The basePackageName and enumPackageName // are used to map enumerated typedefs and identityrefs to the correct type. It returns a slice of errors if they occur // mapping subtypes. -func (s *ProtoLangMapper) protoUnionSubTypes(subtype *yang.YangType, ctx *yang.Entry, currentTypes map[string]unionSubtypeInfo, pargs resolveProtoTypeArgs, opts IROptions) []error { +func (s *ProtoLangMapper) protoUnionSubTypes(subtype *yang.YangType, ctx *yang.Entry, currentTypes map[string]unionSubtypeInfo, pargs resolveProtoTypeArgs, opts ygen.IROptions) []error { var errs []error if util.IsUnionType(subtype) { for _, st := range subtype.Type { @@ -519,7 +521,7 @@ func (s *ProtoLangMapper) protoUnionSubTypes(subtype *yang.YangType, ctx *yang.E return errs } - var mtype *MappedType + var mtype *ygen.MappedType switch subtype.Kind { case yang.Yidentityref: n, key, err := s.protoIdentityName(pargs, subtype.IdentityBase) @@ -528,7 +530,7 @@ func (s *ProtoLangMapper) protoUnionSubTypes(subtype *yang.YangType, ctx *yang.E } // Handle the case that the context entry is not the correct entry to deal with. This occurs when the subtype is // an identityref. - mtype = &MappedType{ + mtype = &ygen.MappedType{ NativeType: n, IsEnumeratedValue: true, EnumeratedYANGTypeKey: key, diff --git a/ygen/protoelements_test.go b/protogen/protoelements_test.go similarity index 84% rename from ygen/protoelements_test.go rename to protogen/protoelements_test.go index 2bd865b4a..7e593d4eb 100644 --- a/ygen/protoelements_test.go +++ b/protogen/protoelements_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ygen +package protogen import ( "testing" @@ -21,16 +21,64 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" + "github.com/openconfig/ygot/internal/igenutil" + "github.com/openconfig/ygot/ygen" ) +// enumMapFromEntries recursively finds enumerated values from a slice of +// entries and returns an enumMap. The input enumMap is intended for +// findEnumSet. +func enumMapFromEntries(entries []*yang.Entry) map[string]*yang.Entry { + enumMap := map[string]*yang.Entry{} + for _, e := range entries { + addEnumsToEnumMap(e, enumMap) + } + return enumMap +} + +// enumMapFromEntries recursively finds enumerated values from a slice of +// resolveTypeArgs and returns an enumMap. The input enumMap is intended for +// findEnumSet. +func enumMapFromArgs(args []resolveTypeArgs) map[string]*yang.Entry { + enumMap := map[string]*yang.Entry{} + for _, a := range args { + addEnumsToEnumMap(a.contextEntry, enumMap) + } + return enumMap +} + +// enumMapFromEntries recursively finds enumerated values from an entry and +// returns an enumMap. The input enumMap is intended for findEnumSet. +func enumMapFromEntry(entry *yang.Entry) map[string]*yang.Entry { + enumMap := map[string]*yang.Entry{} + addEnumsToEnumMap(entry, enumMap) + return enumMap +} + +// addEnumsToEnumMap recursively finds enumerated values and adds them to the +// input enumMap. The input enumMap is intended for findEnumSet, so that tests +// that need generated enumerated names have an easy time generating them, and +// subsequently adding them to their generated state during setup. +func addEnumsToEnumMap(entry *yang.Entry, enumMap map[string]*yang.Entry) { + if entry == nil { + return + } + if e := igenutil.MappableLeaf(entry); e != nil { + enumMap[entry.Path()] = e + } + for _, e := range entry.Dir { + addEnumsToEnumMap(e, enumMap) + } +} + func TestYangTypeToProtoType(t *testing.T) { tests := []struct { name string in []resolveTypeArgs inResolveProtoTypeArgs *resolveProtoTypeArgs inEntries []*yang.Entry - wantWrapper *MappedType - wantScalar *MappedType + wantWrapper *ygen.MappedType + wantScalar *ygen.MappedType wantSame bool wantErr bool }{{ @@ -41,8 +89,8 @@ func TestYangTypeToProtoType(t *testing.T) { {yangType: &yang.YangType{Kind: yang.Yint32}}, {yangType: &yang.YangType{Kind: yang.Yint64}}, }, - wantWrapper: &MappedType{NativeType: "ywrapper.IntValue"}, - wantScalar: &MappedType{NativeType: "sint64"}, + wantWrapper: &ygen.MappedType{NativeType: "ywrapper.IntValue"}, + wantScalar: &ygen.MappedType{NativeType: "sint64"}, }, { name: "unsigned integer types", in: []resolveTypeArgs{ @@ -51,16 +99,16 @@ func TestYangTypeToProtoType(t *testing.T) { {yangType: &yang.YangType{Kind: yang.Yuint32}}, {yangType: &yang.YangType{Kind: yang.Yuint64}}, }, - wantWrapper: &MappedType{NativeType: "ywrapper.UintValue"}, - wantScalar: &MappedType{NativeType: "uint64"}, + wantWrapper: &ygen.MappedType{NativeType: "ywrapper.UintValue"}, + wantScalar: &ygen.MappedType{NativeType: "uint64"}, }, { name: "bool types", in: []resolveTypeArgs{ {yangType: &yang.YangType{Kind: yang.Ybool}}, {yangType: &yang.YangType{Kind: yang.Yempty}}, }, - wantWrapper: &MappedType{NativeType: "ywrapper.BoolValue"}, - wantScalar: &MappedType{NativeType: "bool"}, + wantWrapper: &ygen.MappedType{NativeType: "ywrapper.BoolValue"}, + wantScalar: &ygen.MappedType{NativeType: "bool"}, }, { name: "missing leafref path", in: []resolveTypeArgs{ @@ -85,17 +133,17 @@ func TestYangTypeToProtoType(t *testing.T) { }, { name: "string", in: []resolveTypeArgs{{yangType: &yang.YangType{Kind: yang.Ystring}}}, - wantWrapper: &MappedType{NativeType: "ywrapper.StringValue"}, - wantScalar: &MappedType{NativeType: "string"}, + wantWrapper: &ygen.MappedType{NativeType: "ywrapper.StringValue"}, + wantScalar: &ygen.MappedType{NativeType: "string"}, }, { name: "binary", in: []resolveTypeArgs{{yangType: &yang.YangType{Kind: yang.Ybinary}}}, - wantWrapper: &MappedType{NativeType: "ywrapper.BytesValue"}, - wantScalar: &MappedType{NativeType: "bytes"}, + wantWrapper: &ygen.MappedType{NativeType: "ywrapper.BytesValue"}, + wantScalar: &ygen.MappedType{NativeType: "bytes"}, }, { name: "decimal64", in: []resolveTypeArgs{{yangType: &yang.YangType{Kind: yang.Ydecimal64}}}, - wantWrapper: &MappedType{NativeType: "ywrapper.Decimal64Value"}, + wantWrapper: &ygen.MappedType{NativeType: "ywrapper.Decimal64Value"}, wantSame: true, }, { name: "unmapped types", @@ -116,8 +164,8 @@ func TestYangTypeToProtoType(t *testing.T) { }, }, }, - wantWrapper: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + wantWrapper: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -138,7 +186,7 @@ func TestYangTypeToProtoType(t *testing.T) { }, }, }}, - wantWrapper: &MappedType{NativeType: "ywrapper.StringValue"}, + wantWrapper: &ygen.MappedType{NativeType: "ywrapper.StringValue"}, wantSame: true, }, { name: "union of string, unsupported instance identifier", @@ -174,7 +222,7 @@ func TestYangTypeToProtoType(t *testing.T) { }, }, }}, - wantWrapper: &MappedType{ + wantWrapper: &ygen.MappedType{ NativeType: "UnionLeafEnum", IsEnumeratedValue: true, }, @@ -200,7 +248,7 @@ func TestYangTypeToProtoType(t *testing.T) { }, }, }}, - wantWrapper: &MappedType{ + wantWrapper: &ygen.MappedType{ NativeType: "UnionLeafEnum", IsEnumeratedValue: true, }, @@ -227,7 +275,7 @@ func TestYangTypeToProtoType(t *testing.T) { Parent: &yang.Entry{Name: "base-module"}, }, }}, - wantWrapper: &MappedType{ + wantWrapper: &ygen.MappedType{ NativeType: "basePackage.enumPackage.BaseModuleDerivedIdentityref", IsEnumeratedValue: true, }, @@ -265,7 +313,7 @@ func TestYangTypeToProtoType(t *testing.T) { }, }, }}, - wantWrapper: &MappedType{ + wantWrapper: &ygen.MappedType{ NativeType: "basePackage.enumPackage.BaseModuleBaseIdentity", IsEnumeratedValue: true, }, @@ -297,7 +345,7 @@ func TestYangTypeToProtoType(t *testing.T) { Parent: &yang.Entry{Name: "base-module"}, }, }}, - wantWrapper: &MappedType{ + wantWrapper: &ygen.MappedType{ NativeType: "EnumerationLeaf", IsEnumeratedValue: true, }, @@ -324,7 +372,7 @@ func TestYangTypeToProtoType(t *testing.T) { Parent: &yang.Entry{Name: "base-module"}, }, }}, - wantWrapper: &MappedType{NativeType: "basePackage.enumPackage.BaseModuleDerivedEnumeration", IsEnumeratedValue: true}, + wantWrapper: &ygen.MappedType{NativeType: "basePackage.enumPackage.BaseModuleDerivedEnumeration", IsEnumeratedValue: true}, wantSame: true, }, { name: "identityref", @@ -349,7 +397,7 @@ func TestYangTypeToProtoType(t *testing.T) { Parent: &yang.Entry{Name: "test-module"}, }, }}, - wantWrapper: &MappedType{NativeType: "basePackage.enumPackage.TestModuleBaseIdentity", IsEnumeratedValue: true}, + wantWrapper: &ygen.MappedType{NativeType: "basePackage.enumPackage.TestModuleBaseIdentity", IsEnumeratedValue: true}, wantSame: true, }, { name: "identityref with underscore in identity name", @@ -378,7 +426,7 @@ func TestYangTypeToProtoType(t *testing.T) { Parent: &yang.Entry{Name: "test-module"}, }, }}, - wantWrapper: &MappedType{NativeType: "basePackage.enumPackage.TestModuleBASEIDENTITY", IsEnumeratedValue: true}, + wantWrapper: &ygen.MappedType{NativeType: "basePackage.enumPackage.TestModuleBASEIDENTITY", IsEnumeratedValue: true}, wantSame: true, }, { name: "single type union with scalars requested", @@ -401,7 +449,7 @@ func TestYangTypeToProtoType(t *testing.T) { enumPackageName: "enumPackage", scalarTypeInSingleTypeUnion: true, }, - wantWrapper: &MappedType{NativeType: "string"}, + wantWrapper: &ygen.MappedType{NativeType: "string"}, wantSame: true, }, { name: "leafref with bad path", @@ -446,8 +494,8 @@ func TestYangTypeToProtoType(t *testing.T) { }, }, }, - wantWrapper: &MappedType{NativeType: "ywrapper.StringValue"}, - wantScalar: &MappedType{NativeType: "string"}, + wantWrapper: &ygen.MappedType{NativeType: "ywrapper.StringValue"}, + wantScalar: &ygen.MappedType{NativeType: "string"}, }, { name: "leafref to leafref", in: []resolveTypeArgs{{ @@ -505,7 +553,7 @@ func TestYangTypeToProtoType(t *testing.T) { }, }, }, - wantWrapper: &MappedType{NativeType: "basePackage.enumPackage.EnumModule", IsEnumeratedValue: true}, + wantWrapper: &ygen.MappedType{NativeType: "basePackage.enumPackage.EnumModule", IsEnumeratedValue: true}, wantSame: true, }, { name: "leafref to union", @@ -564,8 +612,8 @@ func TestYangTypeToProtoType(t *testing.T) { }, }, }, - wantWrapper: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + wantWrapper: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "bool": { Index: 0, }, @@ -618,10 +666,9 @@ func TestYangTypeToProtoType(t *testing.T) { } for _, st := range tt.in { - gotWrapper, err := s.yangTypeToProtoType(st, rpt, IROptions{ - TransformationOptions: TransformationOpts{ + gotWrapper, err := s.yangTypeToProtoType(st, rpt, ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ CompressBehaviour: genutil.Uncompressed, - IgnoreShadowSchemaPaths: false, GenerateFakeRoot: true, ExcludeState: false, ShortenEnumLeafNames: false, @@ -638,16 +685,15 @@ func TestYangTypeToProtoType(t *testing.T) { continue } - // NOTE: We ignore testing "MappedType.EnumeratedYANGTypeKey" because it is a reference value, + // NOTE: We ignore testing "ygen.MappedType.EnumeratedYANGTypeKey" because it is a reference value, // and is best tested in an integration test where we can ensure that this value actually points to an enum value in the enum map. - if diff := cmp.Diff(gotWrapper, tt.wantWrapper, cmpopts.IgnoreFields(MappedType{}, "EnumeratedYANGTypeKey")); diff != "" { + if diff := cmp.Diff(gotWrapper, tt.wantWrapper, cmpopts.IgnoreFields(ygen.MappedType{}, "EnumeratedYANGTypeKey")); diff != "" { t.Errorf("%s: yangTypeToProtoType(%v): did not get correct type, diff(-got,+want):\n%s", tt.name, tt.in, diff) } - gotScalar, err := s.yangTypeToProtoScalarType(st, rpt, IROptions{ - TransformationOptions: TransformationOpts{ + gotScalar, err := s.yangTypeToProtoScalarType(st, rpt, ygen.IROptions{ + TransformationOptions: ygen.TransformationOpts{ CompressBehaviour: genutil.Uncompressed, - IgnoreShadowSchemaPaths: false, GenerateFakeRoot: true, ExcludeState: false, ShortenEnumLeafNames: false, @@ -667,9 +713,9 @@ func TestYangTypeToProtoType(t *testing.T) { if tt.wantSame { wantScalar = tt.wantWrapper } - // NOTE: We ignore testing "MappedType.EnumeratedYANGTypeKey" because it is a reference value, + // NOTE: We ignore testing "ygen.MappedType.EnumeratedYANGTypeKey" because it is a reference value, // and is best tested in an integration test where we can ensure that this value actually points to an enum value in the enum map. - if diff := cmp.Diff(gotScalar, wantScalar, cmpopts.IgnoreFields(MappedType{}, "EnumeratedYANGTypeKey")); diff != "" { + if diff := cmp.Diff(gotScalar, wantScalar, cmpopts.IgnoreFields(ygen.MappedType{}, "EnumeratedYANGTypeKey")); diff != "" { t.Errorf("%s: yangTypeToProtoScalarType(%v): did not get correct type, diff(-got,+want):\n%s", tt.name, tt.in, diff) } } diff --git a/ygen/protogen.go b/protogen/protogen.go similarity index 94% rename from ygen/protogen.go rename to protogen/protogen.go index 5782c464b..54580a4d9 100644 --- a/ygen/protogen.go +++ b/protogen/protogen.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ygen +// Package protogen contains code for generating proto code given YANG input. +package protogen import ( "bytes" @@ -28,6 +29,7 @@ import ( "github.com/openconfig/ygot/genutil" "github.com/openconfig/ygot/internal/igenutil" "github.com/openconfig/ygot/util" + "github.com/openconfig/ygot/ygen" ) // Constants defining the defaults for Protobuf package generation. These constants @@ -315,7 +317,7 @@ type protoMsgConfig struct { // It returns a generatedProto3Message pointer which includes the definition of the proto3 message, particularly the // name of the package it is within, the code for the message, and any imports for packages that are referenced by // the message. -func writeProto3Msg(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig) (*generatedProto3Message, util.Errors) { +func writeProto3Msg(msg *ygen.ParsedDirectory, ir *ygen.IR, cfg *protoMsgConfig) (*generatedProto3Message, util.Errors) { if cfg.nestedMessages { if !outputNestedMessage(msg, cfg.compressPaths) { return nil, nil @@ -325,22 +327,33 @@ func writeProto3Msg(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig) (*generat return writeProto3MsgSingleMsg(msg, ir, cfg) } +// isChildOfModule determines whether the Directory represents a container +// or list member that is the direct child of a module entry. +func isChildOfModule(y *ygen.ParsedDirectory) bool { + if y.IsFakeRoot || len(strings.Split(y.Path, "/")) == 3 { + // If the message has a path length of 3, then it is a top-level entity + // within a module, since the path is in the format []{"", , }. + return true + } + return false +} + // outputNestedMessage determines whether the message represented by the supplied // Directory is a message that should be output when nested messages are being // created. The compressPaths argument specifies whether path compression is enabled. // Valid messages are those that are direct children of a module, or become a direct // child when path compression is enabled (i.e., lists that have their parent // surrounding container removed). -func outputNestedMessage(msg *ParsedDirectory, compressPaths bool) bool { +func outputNestedMessage(msg *ygen.ParsedDirectory, compressPaths bool) bool { // If path compression is enabled, and this entry is a list, then its top-level // parent will have been removed, therefore this is a valid message. The path // is 4 elements long since it is of the form // []string{"", module-name, surrounding-container, list-name}. - if compressPaths && msg.Type == List && len(strings.Split(msg.Path, "/")) == 4 { + if compressPaths && msg.Type == ygen.List && len(strings.Split(msg.Path, "/")) == 4 { return true } - return msg.isChildOfModule() + return isChildOfModule(msg) } // writeProto3MsgNested returns a nested set of protobuf messages for the message @@ -351,7 +364,7 @@ func outputNestedMessage(msg *ParsedDirectory, compressPaths bool) bool { // - protogen: the current code generation state. // - cfg: the configuration for the current code generation. // It returns a generated protobuf3 message. -func writeProto3MsgNested(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig) (*generatedProto3Message, util.Errors) { +func writeProto3MsgNested(msg *ygen.ParsedDirectory, ir *ygen.IR, cfg *protoMsgConfig) (*generatedProto3Message, util.Errors) { var gerrs util.Errors var childMsgs []*generatedProto3Message if !msg.IsFakeRoot { @@ -430,7 +443,7 @@ func writeProto3MsgNested(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig) (*g // writeProto3MsgSingleMsg generates a protobuf message definition. It takes the // arguments of writeProto3Message, outputting an individual message that outputs // a package definition and a single protobuf message. -func writeProto3MsgSingleMsg(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig) (*generatedProto3Message, util.Errors) { +func writeProto3MsgSingleMsg(msg *ygen.ParsedDirectory, ir *ygen.IR, cfg *protoMsgConfig) (*generatedProto3Message, util.Errors) { msgDefs, errs := genProto3Msg(msg, ir, cfg, msg.PackageName, nil) if errs != nil { return nil, errs @@ -523,7 +536,7 @@ func genProto3MsgCode(cfg *protoMsgConfig, pkg string, msgDefs []*protoMsg, path // as a protoMsgConfig struct. The parentPkg argument specifies the name of the parent // package for the protobuf message(s) that are being generated, such that relative // paths can be used in the messages. -func genProto3Msg(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig, parentPkg string, childMsgs []*generatedProto3Message) ([]*protoMsg, util.Errors) { +func genProto3Msg(msg *ygen.ParsedDirectory, ir *ygen.IR, cfg *protoMsgConfig, parentPkg string, childMsgs []*generatedProto3Message) ([]*protoMsg, util.Errors) { var errs util.Errors var msgDefs []*protoMsg @@ -574,7 +587,7 @@ func genProto3Msg(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig, parentPkg s parentPkg: parentPkg, } switch field.Type { - case ListNode: + case ygen.ListNode: keyMsg, listImports, listErrs := addProtoListField(fieldDef, msgDef, defArgs) if listErrs != nil { errs = append(errs, listErrs...) @@ -584,14 +597,14 @@ func genProto3Msg(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig, parentPkg s if keyMsg != nil { msgDefs = append(msgDefs, keyMsg) } - case ContainerNode: + case ygen.ContainerNode: cImports, err := addProtoContainerField(fieldDef, defArgs) if err != nil { errs = append(errs, err) continue } addNewKeys(imports, cImports) - case LeafNode, LeafListNode: + case ygen.LeafNode, ygen.LeafListNode: repeatedMsg, lImports, lErrs := addProtoLeafOrLeafListField(fieldDef, msgDef, defArgs) if lErrs != nil { errs = append(errs, lErrs...) @@ -601,7 +614,7 @@ func genProto3Msg(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig, parentPkg s if repeatedMsg != nil { msgDefs = append(msgDefs, repeatedMsg) } - case AnyDataNode: + case ygen.AnyDataNode: fieldDef.Type = protoAnyType imports[protoAnyPackage] = true default: @@ -633,12 +646,12 @@ func genProto3Msg(msg *ParsedDirectory, ir *IR, cfg *protoMsgConfig, parentPkg s type protoDefinitionArgs struct { // field contains the node details for which the proto output is being // defined, in the case that the definition is for an individual entry. - field *NodeDetails - // directory is the ParsedDirectory for which the proto output is being + field *ygen.NodeDetails + // directory is the ygen.ParsedDirectory for which the proto output is being // defined, in the case that the definition is for an directory entry. - directory *ParsedDirectory + directory *ygen.ParsedDirectory // ir is the entirety of the IR as input to the code generation. - ir *IR + ir *ygen.IR // definedFieldNames specifies the field names that have been defined in the context. definedFieldNames map[string]bool // cfg contains configuration options for proto generation. @@ -760,7 +773,7 @@ func addProtoLeafOrLeafListField(fieldDef *protoMsgField, msgDef *protoMsg, args imports = append(imports, importPath(args.cfg.baseImportPath, args.cfg.basePackageName, args.cfg.enumPackageName)) } - if args.field.Type == LeafListNode { + if args.field.Type == ygen.LeafListNode { fieldDef.IsRepeated = true } return repeatedMsg, imports, nil @@ -771,7 +784,7 @@ func addProtoLeafOrLeafListField(fieldDef *protoMsgField, msgDef *protoMsg, args // the annotateEnumNames bool is set, then the original enum value label is // stored in the definition. Since leaves that are of type enumeration are // output directly within a Protobuf message, these are skipped. -func writeProtoEnums(enums map[string]*EnumeratedYANGType, annotateEnumNames bool) ([]string, error) { +func writeProtoEnums(enums map[string]*ygen.EnumeratedYANGType, annotateEnumNames bool) ([]string, error) { var errs util.Errors var genEnums []string for _, enum := range enums { @@ -779,10 +792,10 @@ func writeProtoEnums(enums map[string]*EnumeratedYANGType, annotateEnumNames boo p := &protoEnum{Name: enum.Name} switch enum.Kind { - case SimpleEnumerationType, UnionEnumerationType: + case ygen.SimpleEnumerationType, ygen.UnionEnumerationType: // Skip simple enumerations and those within unions. continue - case IdentityType: + case ygen.IdentityType: // For an identityref the values are based on // the name of the identities that correspond with the base, and the value // is gleaned from the YANG schema. @@ -804,7 +817,7 @@ func writeProtoEnums(enums map[string]*EnumeratedYANGType, annotateEnumNames boo p.Values = values p.ValuePrefix = strings.ToUpper(enum.Name) p.Description = fmt.Sprintf("YANG identity %s", enum.IdentityBaseName) - case DerivedEnumerationType, DerivedUnionEnumerationType: + case ygen.DerivedEnumerationType, ygen.DerivedUnionEnumerationType: ge, err := genProtoEnum(enum, annotateEnumNames, true) if err != nil { errs = append(errs, err) @@ -837,7 +850,7 @@ func writeProtoEnums(enums map[string]*EnumeratedYANGType, annotateEnumNames boo // and returns a protoMsgEnum that contains its definition within the proto // schema. If the annotateEnumNames bool is set, then the original YANG name // is stored with each enum value. -func genProtoEnum(enum *EnumeratedYANGType, annotateEnumNames, isLeafOrTypedef bool) (*protoMsgEnum, error) { +func genProtoEnum(enum *ygen.EnumeratedYANGType, annotateEnumNames, isLeafOrTypedef bool) (*protoMsgEnum, error) { eval := map[int64]protoEnumValue{} eval[0] = protoEnumValue{ProtoLabel: protoEnumZeroName} @@ -934,16 +947,16 @@ func protoLeafDefinition(leafName string, args *protoDefinitionArgs) (*protoDefi enums: map[string]*protoMsgEnum{}, } - var enum *EnumeratedYANGType + var enum *ygen.EnumeratedYANGType if protoType.IsEnumeratedValue { enum = args.ir.Enums[protoType.EnumeratedYANGTypeKey] } switch { - case protoType.IsEnumeratedValue && enum.Kind == SimpleEnumerationType: + case protoType.IsEnumeratedValue && enum.Kind == ygen.SimpleEnumerationType: // For fields that are simple enumerations within a message, then we embed an enumeration // within the Protobuf message. - e, err := genProtoEnum(enum, args.cfg.annotateEnumNames, args.field.Type == LeafNode) + e, err := genProtoEnum(enum, args.cfg.annotateEnumNames, args.field.Type == ygen.LeafNode) if err != nil { return nil, err } @@ -1006,7 +1019,7 @@ func safeProtoIdentifierName(name string) string { } // protoTagForEntry returns a protobuf tag value for the entry e. -func protoTagForEntry(n YANGNodeDetails) (uint32, error) { +func protoTagForEntry(n ygen.YANGNodeDetails) (uint32, error) { return fieldTag(n.Path) } @@ -1070,12 +1083,12 @@ func genListKeyProto(listPackage string, listName string, args *protoDefinitionA Tag: ctag, } - var enum *EnumeratedYANGType + var enum *ygen.EnumeratedYANGType if scalarType.IsEnumeratedValue { enum = args.ir.Enums[scalarType.EnumeratedYANGTypeKey] } switch { - case scalarType.IsEnumeratedValue && enum.Kind == IdentityType: + case scalarType.IsEnumeratedValue && enum.Kind == ygen.IdentityType: km.Imports = append(km.Imports, importPath(args.cfg.baseImportPath, args.cfg.basePackageName, args.cfg.enumPackageName)) fd.Type = scalarType.NativeType case scalarType.IsEnumeratedValue: @@ -1153,7 +1166,7 @@ func genListKeyProto(listPackage string, listName string, args *protoDefinitionA // enumInProtoUnionField parses an enum that is within a union and returns the generated // enumeration that should be included within a protobuf message for it. If annotateEnumNames // is set to true, the enumerated value's original names are stored. -func enumInProtoUnionField(name string, field *NodeDetails, Enums map[string]*EnumeratedYANGType, annotateEnumNames bool) (map[string]*protoMsgEnum, error) { +func enumInProtoUnionField(name string, field *ygen.NodeDetails, Enums map[string]*ygen.EnumeratedYANGType, annotateEnumNames bool) (map[string]*protoMsgEnum, error) { enums := map[string]*protoMsgEnum{} for genName, subtype := range field.LangType.UnionTypes { if subtype.EnumeratedYANGTypeKey == "" { @@ -1164,8 +1177,8 @@ func enumInProtoUnionField(name string, field *NodeDetails, Enums map[string]*En return nil, fmt.Errorf("enumerated type within union %s not found in IR, field path: %s", genName, field.YANGDetails.Path) } switch enum.Kind { - case SimpleEnumerationType, UnionEnumerationType: - protoEnum, err := genProtoEnum(enum, annotateEnumNames, field.Type == LeafNode) + case ygen.SimpleEnumerationType, ygen.UnionEnumerationType: + protoEnum, err := genProtoEnum(enum, annotateEnumNames, field.Type == ygen.LeafNode) if err != nil { return nil, err } @@ -1186,11 +1199,11 @@ type protoUnionField struct { } // unionFieldToOneOf takes an input name, a yang.Entry containing a field -// definition, a path argument used to compute the field tag numbers, and a MappedType +// definition, a path argument used to compute the field tag numbers, and a ygen.MappedType // containing the proto type that the entry has been mapped to, and returns a definition of a union // field within the protobuf message. If the annotateEnumNames boolean is set, then any enumerated types // within the union have their original names within the YANG schema appended. -func unionFieldToOneOf(fieldName string, field *NodeDetails, path string, mtype *MappedType, Enums map[string]*EnumeratedYANGType, annotateEnumNames bool) (*protoUnionField, error) { +func unionFieldToOneOf(fieldName string, field *ygen.NodeDetails, path string, mtype *ygen.MappedType, Enums map[string]*ygen.EnumeratedYANGType, annotateEnumNames bool) (*protoUnionField, error) { enums, err := enumInProtoUnionField(fieldName, field, Enums, annotateEnumNames) if err != nil { return nil, err @@ -1230,7 +1243,7 @@ func unionFieldToOneOf(fieldName string, field *NodeDetails, path string, mtype oofs = append(oofs, st) } - if field.Type == LeafListNode { + if field.Type == ygen.LeafListNode { // In this case, we cannot return a oneof, since it is not possible to have a repeated // oneof, therefore we return a message that contains the protoMsgFields that are defined // above. @@ -1267,7 +1280,7 @@ func protoPackageToFilePath(pkg string) []string { // protoSchemaPathAnnotation takes a protobuf message and field, and returns the protobuf // field option definitions required to annotate it with its schema path(s). -func protoSchemaPathAnnotation(msg *ParsedDirectory, fieldName string, compressPaths bool) (*protoOption, error) { +func protoSchemaPathAnnotation(msg *ygen.ParsedDirectory, fieldName string, compressPaths bool) (*protoOption, error) { // protobuf paths are always absolute. smapp := msg.Fields[fieldName].MappedPaths var b bytes.Buffer diff --git a/ygen/protogen_test.go b/protogen/protogen_test.go similarity index 75% rename from ygen/protogen_test.go rename to protogen/protogen_test.go index 60a90155c..5f1b8ebb7 100644 --- a/ygen/protogen_test.go +++ b/protogen/protogen_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ygen +package protogen import ( "sort" @@ -22,6 +22,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/testutil" + "github.com/openconfig/ygot/ygen" "github.com/openconfig/ygot/ygot" "google.golang.org/protobuf/testing/protocmp" ) @@ -62,8 +63,8 @@ func TestGenProto3Msg(t *testing.T) { tests := []struct { name string - inMsg *ParsedDirectory - inIR *IR + inMsg *ygen.ParsedDirectory + inIR *ygen.IR inCompressPaths bool inBasePackage string inEnumPackage string @@ -75,28 +76,28 @@ func TestGenProto3Msg(t *testing.T) { wantErr bool }{{ name: "simple message with only scalar fields", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "field-one": { Name: "field_one", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "field-one", Path: "/field-one", }, }, "field-two": { Name: "field_two", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.IntValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "field-two", Path: "/field-two", }, @@ -123,17 +124,17 @@ func TestGenProto3Msg(t *testing.T) { }, }, { name: "simple message with child messages, ensure no difference in logic", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "field-one": { Name: "field_one", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "field-one", Path: "/field-one", }, @@ -161,15 +162,15 @@ func TestGenProto3Msg(t *testing.T) { }, }, { name: "simple message with union leaf and leaf-list", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "field-one": { Name: "field_one", - Type: LeafNode, - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -178,16 +179,16 @@ func TestGenProto3Msg(t *testing.T) { }, }, }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "field-one", Path: "/field-one", }, }, "field-two": { Name: "field_two", - Type: LeafListNode, - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + Type: ygen.LeafListNode, + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "sint64": { Index: 0, }, @@ -197,7 +198,7 @@ func TestGenProto3Msg(t *testing.T) { }, }, }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "field-two", Path: "/parent/field-two", }, @@ -205,11 +206,11 @@ func TestGenProto3Msg(t *testing.T) { }, Path: "/root/message-name", }, - inIR: &IR{ - Enums: map[string]*EnumeratedYANGType{ + inIR: &ygen.IR{ + Enums: map[string]*ygen.EnumeratedYANGType{ "/root/derived-enum": { Name: "BaseDerivedEnum", - Kind: DerivedEnumerationType, + Kind: ygen.DerivedEnumerationType, TypeName: "derived-enum", ValToYANGDetails: []ygot.EnumDefinition{ { @@ -265,25 +266,25 @@ func TestGenProto3Msg(t *testing.T) { }, }, { name: "simple message with leaf-list and a message child, compression on", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "AMessage", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "leaf-list": { Name: "leaf_list", - Type: LeafListNode, - LangType: &MappedType{ + Type: ygen.LeafListNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "leaf-list", Path: "/leaf-list", }, }, "container-child": { Name: "container_child", - Type: ContainerNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ContainerNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "container-child", Path: "/root/a-message/container-child", }, @@ -291,11 +292,11 @@ func TestGenProto3Msg(t *testing.T) { }, Path: "/root/a-message", }, - inIR: &IR{ - Directories: map[string]*ParsedDirectory{ + inIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/root/a-message/container-child": { Name: "ContainerChild", - Type: Container, + Type: ygen.Container, Path: "/root/a-message/container-child", PackageName: "a_message", }, @@ -323,25 +324,25 @@ func TestGenProto3Msg(t *testing.T) { }, }, { name: "simple message with leaf-list and a message child, compression off", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "AMessage", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "leaf-list": { Name: "leaf_list", - Type: LeafListNode, - LangType: &MappedType{ + Type: ygen.LeafListNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "leaf-list", Path: "/leaf-list", }, }, "container-child": { Name: "container_child", - Type: ContainerNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ContainerNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "container-child", Path: "/root/a-message/container-child", }, @@ -349,11 +350,11 @@ func TestGenProto3Msg(t *testing.T) { }, Path: "/root/a-message", }, - inIR: &IR{ - Directories: map[string]*ParsedDirectory{ + inIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/root/a-message/container-child": { Name: "ContainerChild", - Type: Container, + Type: ygen.Container, Path: "/root/a-message/container-child", PackageName: "root.a_message", }, @@ -380,14 +381,14 @@ func TestGenProto3Msg(t *testing.T) { }, }, { name: "message with list", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "AMessageWithAList", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Path: "/a-message-with-a-list/list", }, @@ -395,27 +396,27 @@ func TestGenProto3Msg(t *testing.T) { }, Path: "/a-message-with-a-list/list", }, - inIR: &IR{ - Directories: map[string]*ParsedDirectory{ + inIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/a-message-with-a-list/list": { - Name: "List", - Type: List, + Name: "ygen.List", + Type: ygen.List, Path: "/a-message-with-a-list/list", PackageName: "a_message_with_a_list", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "key": { Name: "key", - Type: LeafNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.LeafNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "key", Path: "/key", }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "key": { Name: "key", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", }, }, @@ -431,13 +432,13 @@ func TestGenProto3Msg(t *testing.T) { YANGPath: "/a-message-with-a-list/list", Fields: []*protoMsgField{{ Name: "list", - Type: "ListKey", + Type: "ygen.ListKey", Tag: 200573382, IsRepeated: true, }}, }, - "ListKey": { - Name: "ListKey", + "ygen.ListKey": { + Name: "ygen.ListKey", YANGPath: "/a-message-with-a-list/list", Fields: []*protoMsgField{{ Tag: 1, @@ -447,21 +448,21 @@ func TestGenProto3Msg(t *testing.T) { }, { Tag: 2, Name: "list", - Type: "a_message_with_a_list.List", + Type: "a_message_with_a_list.ygen.List", }}, Imports: []string{"base/a_message_with_a_list/a_message_with_a_list.proto"}, }, }, }, { name: "message with list, where the key has the same name as list", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "AMessageWithAList", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Path: "/a-message-with-a-list/list", }, @@ -469,27 +470,27 @@ func TestGenProto3Msg(t *testing.T) { }, Path: "/a-message-with-a-list/list", }, - inIR: &IR{ - Directories: map[string]*ParsedDirectory{ + inIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/a-message-with-a-list/list": { - Name: "List", - Type: List, + Name: "ygen.List", + Type: ygen.List, Path: "/a-message-with-a-list/list", PackageName: "a_message_with_a_list", - Fields: map[string]*NodeDetails{ + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "list", - Type: LeafNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.LeafNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Path: "/list", }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "list": { Name: "list", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", }, }, @@ -505,13 +506,13 @@ func TestGenProto3Msg(t *testing.T) { YANGPath: "/a-message-with-a-list/list", Fields: []*protoMsgField{{ Name: "list", - Type: "ListKey", + Type: "ygen.ListKey", Tag: 200573382, IsRepeated: true, }}, }, - "ListKey": { - Name: "ListKey", + "ygen.ListKey": { + Name: "ygen.ListKey", YANGPath: "/a-message-with-a-list/list", Fields: []*protoMsgField{{ Tag: 1, @@ -521,21 +522,21 @@ func TestGenProto3Msg(t *testing.T) { }, { Tag: 2, Name: "list", - Type: "a_message_with_a_list.List", + Type: "a_message_with_a_list.ygen.List", }}, Imports: []string{"base/a_message_with_a_list/a_message_with_a_list.proto"}, }, }, }, { name: "message with missing directory", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "Foo", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "bar": { Name: "bar", - Type: ContainerNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ContainerNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "bar", Path: "/bar", }, @@ -543,32 +544,32 @@ func TestGenProto3Msg(t *testing.T) { }, Path: "/foo", }, - inIR: &IR{ - Directories: map[string]*ParsedDirectory{}, + inIR: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{}, }, wantErr: true, }, { name: "message with any anydata field", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "MessageWithAnydata", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "any-data": { Name: "any_data", - Type: AnyDataNode, + Type: ygen.AnyDataNode, LangType: nil, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "any-data", Path: "/any-data", }, }, "leaf": { Name: "leaf", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "leaf", Path: "/leaf", }, @@ -596,17 +597,17 @@ func TestGenProto3Msg(t *testing.T) { }, }, { name: "message with annotate schema paths enabled", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "MessageWithAnnotations", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "leaf": { Name: "leaf", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "leaf", Path: "/one/two/leaf", }, @@ -729,13 +730,13 @@ func TestWriteProtoMsg(t *testing.T) { tests := []struct { name string // inMsg should be used if msg is the same between compressed and uncompressed. - inMsg *ParsedDirectory - inMsgCompress *ParsedDirectory - inMsgUncompress *ParsedDirectory - inIR *IR - // inIR should be used if IR is the same between compressed and uncompressed. - inIRCompress *IR - inIRUncompress *IR + inMsg *ygen.ParsedDirectory + inMsgCompress *ygen.ParsedDirectory + inMsgUncompress *ygen.ParsedDirectory + inIR *ygen.IR + // inIR should be used if ygen.IR is the same between compressed and uncompressed. + inIRCompress *ygen.IR + inIRUncompress *ygen.IR inBasePackageName string inEnumPackageName string inBaseImportPath string @@ -746,17 +747,17 @@ func TestWriteProtoMsg(t *testing.T) { wantUncompressErr bool }{{ name: "simple message with scalar fields", - inMsgCompress: &ParsedDirectory{ + inMsgCompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "field-one": { Name: "field_one", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "field-one", Path: "/field-one", }, @@ -765,17 +766,17 @@ func TestWriteProtoMsg(t *testing.T) { PackageName: "container", Path: "/module/container/message-name", }, - inMsgUncompress: &ParsedDirectory{ + inMsgUncompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "field-one": { Name: "field_one", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "field-one", Path: "/field-one", }, @@ -804,14 +805,14 @@ message MessageName { }, }, { name: "simple message with other messages embedded", - inMsgCompress: &ParsedDirectory{ + inMsgCompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "child": { Name: "child", - Type: ContainerNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ContainerNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "child", Path: "/module/message-name/child", }, @@ -820,14 +821,14 @@ message MessageName { Path: "/module/message-name", PackageName: "", }, - inMsgUncompress: &ParsedDirectory{ + inMsgUncompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "child": { Name: "child", - Type: ContainerNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ContainerNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "child", Path: "/module/message-name/child", }, @@ -836,21 +837,21 @@ message MessageName { Path: "/module/message-name", PackageName: "module", }, - inIRCompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRCompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/message-name/child": { Name: "Child", - Type: Container, + Type: ygen.Container, Path: "/module/message-name/child", PackageName: "message_name", }, }, }, - inIRUncompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRUncompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/message-name/child": { Name: "Child", - Type: Container, + Type: ygen.Container, Path: "/module/message-name/child", PackageName: "module.message_name", }, @@ -878,14 +879,14 @@ message MessageName { }, }, { name: "simple message with other messages embedded - with nested messages", - inMsgCompress: &ParsedDirectory{ + inMsgCompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "child": { Name: "child", - Type: ContainerNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ContainerNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "child", Path: "/module/message-name/child", }, @@ -894,19 +895,19 @@ message MessageName { Path: "/module/message-name", PackageName: "", }, - inIRCompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRCompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/message-name/child": { Name: "Child", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "leaf": { Name: "leaf", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "leaf", Path: "/leaf", }, @@ -917,14 +918,14 @@ message MessageName { }, }, }, - inMsgUncompress: &ParsedDirectory{ + inMsgUncompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "child": { Name: "child", - Type: ContainerNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ContainerNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "child", Path: "/module/message-name/child", }, @@ -933,19 +934,19 @@ message MessageName { Path: "/module/message-name", PackageName: "module", }, - inIRUncompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRUncompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/message-name/child": { Name: "Child", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "leaf": { Name: "leaf", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "leaf", Path: "/leaf", }, @@ -981,19 +982,19 @@ message MessageName { }, }, { name: "simple message with an enumeration leaf", - inMsgCompress: &ParsedDirectory{ + inMsgCompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "enum": { Name: "enum", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "Enum", IsEnumeratedValue: true, EnumeratedYANGTypeKey: "/module/message-name/enum", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "enum", Path: "/module/message-name/enum", }, @@ -1002,19 +1003,19 @@ message MessageName { Path: "/module/message-name", PackageName: "", }, - inMsgUncompress: &ParsedDirectory{ + inMsgUncompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "enum": { Name: "enum", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "Enum", IsEnumeratedValue: true, EnumeratedYANGTypeKey: "/module/message-name/enum", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "enum", Path: "/module/message-name/enum", }, @@ -1023,11 +1024,11 @@ message MessageName { Path: "/module/message-name", PackageName: "module", }, - inIR: &IR{ - Enums: map[string]*EnumeratedYANGType{ + inIR: &ygen.IR{ + Enums: map[string]*ygen.EnumeratedYANGType{ "/module/message-name/enum": { Name: "ModuleMessageNameEnum", - Kind: SimpleEnumerationType, + Kind: ygen.SimpleEnumerationType, TypeName: "enumeration", ValToYANGDetails: []ygot.EnumDefinition{ { @@ -1072,14 +1073,14 @@ message MessageName { }, }, { name: "simple message with a list", - inMsgUncompress: &ParsedDirectory{ + inMsgUncompress: &ygen.ParsedDirectory{ Name: "AMessage", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Path: "/module/a-message/surrounding-container/list", }, @@ -1088,25 +1089,25 @@ message MessageName { Path: "/module/a-message", PackageName: "module", }, - inIRUncompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRUncompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/a-message/surrounding-container/list": { - Name: "List", - Type: List, - Fields: map[string]*NodeDetails{ + Name: "ygen.List", + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyfield": { Name: "keyfield", - Type: LeafNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.LeafNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "keyfield", Path: "/keyfield", }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "keyfield": { Name: "keyfield", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", }, }, @@ -1116,14 +1117,14 @@ message MessageName { }, }, }, - inMsgCompress: &ParsedDirectory{ + inMsgCompress: &ygen.ParsedDirectory{ Name: "AMessage", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Path: "/module/a-message/surrounding-container/list", }, @@ -1132,25 +1133,25 @@ message MessageName { Path: "/module/a-message", PackageName: "", }, - inIRCompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRCompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/a-message/surrounding-container/list": { - Name: "List", - Type: List, - Fields: map[string]*NodeDetails{ + Name: "ygen.List", + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyfield": { Name: "keyfield", - Type: LeafNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.LeafNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "keyfield", Path: "/keyfield", }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "keyfield": { Name: "keyfield", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", }, }, @@ -1165,43 +1166,43 @@ message MessageName { wantCompress: &generatedProto3Message{ PackageName: "", MessageCode: ` -// ListKey represents the /module/a-message/surrounding-container/list YANG schema element. -message ListKey { +// ygen.ListKey represents the /module/a-message/surrounding-container/list YANG schema element. +message ygen.ListKey { string keyfield = 1; - a_message.List list = 2; + a_message.ygen.List list = 2; } // AMessage represents the /module/a-message YANG schema element. message AMessage { - repeated ListKey list = 486198550; + repeated ygen.ListKey list = 486198550; }`, RequiredImports: []string{"base/a_message/a_message.proto"}, }, wantUncompress: &generatedProto3Message{ PackageName: "module", MessageCode: ` -// ListKey represents the /module/a-message/surrounding-container/list YANG schema element. -message ListKey { +// ygen.ListKey represents the /module/a-message/surrounding-container/list YANG schema element. +message ygen.ListKey { string keyfield = 1; - a_message.surrounding_container.List list = 2; + a_message.surrounding_container.ygen.List list = 2; } // AMessage represents the /module/a-message YANG schema element. message AMessage { - repeated ListKey list = 486198550; + repeated ygen.ListKey list = 486198550; }`, RequiredImports: []string{"base/module/a_message/surrounding_container/surrounding_container.proto"}, }, }, { name: "simple message with a list - nested messages", - inMsgUncompress: &ParsedDirectory{ + inMsgUncompress: &ygen.ParsedDirectory{ Name: "AMessage", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Path: "/module/a-message/surrounding-container/list", }, @@ -1210,28 +1211,28 @@ message AMessage { Path: "/module/a-message", PackageName: "module", }, - inIRUncompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRUncompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/a-message/surrounding-container/list": { - Name: "List", - Type: List, - Fields: map[string]*NodeDetails{ + Name: "ygen.List", + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyfield": { Name: "keyfield", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyfield", Path: "/keyfield", }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "keyfield": { Name: "keyfield", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", }, }, @@ -1241,14 +1242,14 @@ message AMessage { }, }, }, - inMsgCompress: &ParsedDirectory{ + inMsgCompress: &ygen.ParsedDirectory{ Name: "AMessage", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Path: "/module/a-message/surrounding-container/list", }, @@ -1257,28 +1258,28 @@ message AMessage { Path: "/module/a-message", PackageName: "", }, - inIRCompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRCompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/a-message/surrounding-container/list": { - Name: "List", - Type: List, - Fields: map[string]*NodeDetails{ + Name: "ygen.List", + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyfield": { Name: "keyfield", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyfield", Path: "/keyfield", }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "keyfield": { Name: "keyfield", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", }, }, @@ -1295,38 +1296,38 @@ message AMessage { PackageName: "", MessageCode: ` message AMessage { - message List { + message ygen.List { } - message ListKey { + message ygen.ListKey { string keyfield = 1; - List list = 2; + ygen.List list = 2; } - repeated ListKey list = 486198550; + repeated ygen.ListKey list = 486198550; }`, }, wantUncompress: &generatedProto3Message{ PackageName: "module", MessageCode: ` message AMessage { - message List { + message ygen.List { } - message ListKey { + message ygen.ListKey { string keyfield = 1; - List list = 2; + ygen.List list = 2; } - repeated ListKey list = 486198550; + repeated ygen.ListKey list = 486198550; }`, }, }, { name: "simple message with unkeyed list - nested messages", - inMsgUncompress: &ParsedDirectory{ + inMsgUncompress: &ygen.ParsedDirectory{ Name: "AMessage", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Path: "/module/a-message/surrounding-container/list", }, @@ -1335,19 +1336,19 @@ message AMessage { Path: "/module/a-message", PackageName: "module", }, - inIRUncompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRUncompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/a-message/surrounding-container/list": { - Name: "List", - Type: List, - Fields: map[string]*NodeDetails{ + Name: "ygen.List", + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyfield": { Name: "keyfield", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyfield", Path: "/keyfield", }, @@ -1358,14 +1359,14 @@ message AMessage { }, }, }, - inMsgCompress: &ParsedDirectory{ + inMsgCompress: &ygen.ParsedDirectory{ Name: "AMessage", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "list": { Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "list", Path: "/module/a-message/surrounding-container/list", }, @@ -1374,19 +1375,19 @@ message AMessage { Path: "/module/a-message", PackageName: "", }, - inIRCompress: &IR{ - Directories: map[string]*ParsedDirectory{ + inIRCompress: &ygen.IR{ + Directories: map[string]*ygen.ParsedDirectory{ "/module/a-message/surrounding-container/list": { - Name: "List", - Type: List, - Fields: map[string]*NodeDetails{ + Name: "ygen.List", + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "keyfield": { Name: "keyfield", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "ywrapper.StringValue", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "keyfield", Path: "/keyfield", }, @@ -1404,27 +1405,27 @@ message AMessage { PackageName: "", MessageCode: ` message AMessage { - message List { + message ygen.List { ywrapper.StringValue keyfield = 411968747; } - repeated List list = 486198550; + repeated ygen.List list = 486198550; }`, }, wantUncompress: &generatedProto3Message{ PackageName: "module", MessageCode: ` message AMessage { - message List { + message ygen.List { ywrapper.StringValue keyfield = 411968747; } - repeated List list = 486198550; + repeated ygen.List list = 486198550; }`, }, }, { name: "message skipped due to path length", - inMsg: &ParsedDirectory{ + inMsg: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, + Type: ygen.Container, Path: "one/two", }, inBasePackageName: "base", @@ -1434,19 +1435,19 @@ message AMessage { wantUncompress: nil, }, { name: "simple message with an identityref leaf", - inMsgUncompress: &ParsedDirectory{ + inMsgUncompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "identityref": { Name: "identityref", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "base.enums.TestModuleFooIdentity", IsEnumeratedValue: true, EnumeratedYANGTypeKey: "/module/foo-identity", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "identityref", Path: "/module/message-name/identityref", }, @@ -1455,19 +1456,19 @@ message AMessage { PackageName: "module", Path: "/module-name/message-name", }, - inMsgCompress: &ParsedDirectory{ + inMsgCompress: &ygen.ParsedDirectory{ Name: "MessageName", - Type: Container, - Fields: map[string]*NodeDetails{ + Type: ygen.Container, + Fields: map[string]*ygen.NodeDetails{ "identityref": { Name: "identityref", - Type: LeafNode, - LangType: &MappedType{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ NativeType: "base.enums.TestModuleFooIdentity", IsEnumeratedValue: true, EnumeratedYANGTypeKey: "/module/foo-identity", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Name: "identityref", Path: "/module/message-name/identityref", }, @@ -1476,11 +1477,11 @@ message AMessage { PackageName: "", Path: "/module-name/message-name", }, - inIR: &IR{ - Enums: map[string]*EnumeratedYANGType{ + inIR: &ygen.IR{ + Enums: map[string]*ygen.EnumeratedYANGType{ "/module/foo-identity": { Name: "TestModuleFooIdentity", - Kind: IdentityType, + Kind: ygen.IdentityType, TypeName: "identityref", ValToYANGDetails: []ygot.EnumDefinition{ { @@ -1590,31 +1591,31 @@ func TestGenListKeyProto(t *testing.T) { inListPackage: "pkg", inListName: "list", inArgs: &protoDefinitionArgs{ - field: &NodeDetails{ + field: &ygen.NodeDetails{ Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Path: "/list", }, }, - directory: &ParsedDirectory{ - Name: "List", - Type: List, - Fields: map[string]*NodeDetails{ + directory: &ygen.ParsedDirectory{ + Name: "ygen.List", + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "key": { Name: "key", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Path: "/list/key", }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "key": { Name: "key", - LangType: &MappedType{ + LangType: &ygen.MappedType{ NativeType: "string", }, }, @@ -1645,21 +1646,21 @@ func TestGenListKeyProto(t *testing.T) { inListPackage: "pkg", inListName: "list", inArgs: &protoDefinitionArgs{ - field: &NodeDetails{ + field: &ygen.NodeDetails{ Name: "list", - Type: ListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.ListNode, + YANGDetails: ygen.YANGNodeDetails{ Path: "/list", }, }, - directory: &ParsedDirectory{ - Name: "List", - Type: List, - Fields: map[string]*NodeDetails{ + directory: &ygen.ParsedDirectory{ + Name: "ygen.List", + Type: ygen.List, + Fields: map[string]*ygen.NodeDetails{ "key": { Name: "key", - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -1668,16 +1669,16 @@ func TestGenListKeyProto(t *testing.T) { }, }, }, - YANGDetails: YANGNodeDetails{ + YANGDetails: ygen.YANGNodeDetails{ Path: "/key", }, }, }, - ListKeys: map[string]*ListKey{ + ListKeys: map[string]*ygen.ListKey{ "key": { Name: "key", - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -1689,7 +1690,7 @@ func TestGenListKeyProto(t *testing.T) { }, }, }, - ir: &IR{}, + ir: &ygen.IR{}, cfg: &protoMsgConfig{ compressPaths: false, basePackageName: "base", @@ -1751,16 +1752,16 @@ func TestWriteProtoEnums(t *testing.T) { tests := []struct { name string - inEnums map[string]*EnumeratedYANGType + inEnums map[string]*ygen.EnumeratedYANGType inAnnotateEnumNames bool wantEnums []string wantErr bool }{{ name: "skipped enumeration type", - inEnums: map[string]*EnumeratedYANGType{ + inEnums: map[string]*ygen.EnumeratedYANGType{ "/field-name|enum": { Name: "SomeEnumType", - Kind: SimpleEnumerationType, + Kind: ygen.SimpleEnumerationType, TypeName: "enumeration", ValToYANGDetails: []ygot.EnumDefinition{{ Name: "SPEED_2.5G", @@ -1774,10 +1775,10 @@ func TestWriteProtoEnums(t *testing.T) { wantEnums: []string{}, }, { name: "enum for identityref", - inEnums: map[string]*EnumeratedYANGType{ + inEnums: map[string]*ygen.EnumeratedYANGType{ "/field-name|enum": { Name: "EnumeratedValue", - Kind: IdentityType, + Kind: ygen.IdentityType, IdentityBaseName: "IdentityValue", ValToYANGDetails: []ygot.EnumDefinition{{ Name: "VALUE_A", @@ -1800,10 +1801,10 @@ enum EnumeratedValue { }, }, { name: "enum for typedef enumeration", - inEnums: map[string]*EnumeratedYANGType{ + inEnums: map[string]*ygen.EnumeratedYANGType{ "e": { Name: "EnumName", - Kind: DerivedEnumerationType, + Kind: ygen.DerivedEnumerationType, TypeName: "typedef", ValToYANGDetails: []ygot.EnumDefinition{{ Name: "SPEED_2.5G", @@ -1815,7 +1816,7 @@ enum EnumeratedValue { }, "f": { Name: "SecondEnum", - Kind: DerivedEnumerationType, + Kind: ygen.DerivedEnumerationType, TypeName: "derived", ValToYANGDetails: []ygot.EnumDefinition{{ Name: "VALUE_1", @@ -1868,11 +1869,11 @@ func TestUnionFieldToOneOf(t *testing.T) { tests := []struct { name string inName string - inField *NodeDetails + inField *ygen.NodeDetails // inPath is populated with field.YANGDetails.Path if not set. inPath string - inMappedType *MappedType - inEnums map[string]*EnumeratedYANGType + inMappedType *ygen.MappedType + inEnums map[string]*ygen.EnumeratedYANGType inAnnotateEnumNames bool wantFields []*protoMsgField wantEnums map[string]*protoMsgEnum @@ -1881,14 +1882,14 @@ func TestUnionFieldToOneOf(t *testing.T) { }{{ name: "simple string union", inName: "FieldName", - inField: &NodeDetails{ + inField: &ygen.NodeDetails{ Name: "field-name", - Type: LeafNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.LeafNode, + YANGDetails: ygen.YANGNodeDetails{ Path: "/field-name", }, - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -1898,8 +1899,8 @@ func TestUnionFieldToOneOf(t *testing.T) { }, }, }, - inMappedType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + inMappedType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -1921,11 +1922,11 @@ func TestUnionFieldToOneOf(t *testing.T) { }, { name: "simple string union with a non-empty path argument", inName: "FieldName", - inField: &NodeDetails{ + inField: &ygen.NodeDetails{ Name: "field-name", - Type: LeafNode, - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + Type: ygen.LeafNode, + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -1935,8 +1936,8 @@ func TestUnionFieldToOneOf(t *testing.T) { }, }, }, - inMappedType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + inMappedType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -1959,14 +1960,14 @@ func TestUnionFieldToOneOf(t *testing.T) { }, { name: "decimal64 union", inName: "FieldName", - inField: &NodeDetails{ + inField: &ygen.NodeDetails{ Name: "field-name", - Type: LeafNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.LeafNode, + YANGDetails: ygen.YANGNodeDetails{ Path: "/field-name", }, - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -1976,8 +1977,8 @@ func TestUnionFieldToOneOf(t *testing.T) { }, }, }, - inMappedType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + inMappedType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -1999,14 +2000,14 @@ func TestUnionFieldToOneOf(t *testing.T) { }, { name: "union with an enumeration", inName: "FieldName", - inField: &NodeDetails{ + inField: &ygen.NodeDetails{ Name: "field-name", - Type: LeafNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.LeafNode, + YANGDetails: ygen.YANGNodeDetails{ Path: "/field-name", }, - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "SomeEnumType": { Index: 0, EnumeratedYANGTypeKey: "/field-name|enum", @@ -2017,8 +2018,8 @@ func TestUnionFieldToOneOf(t *testing.T) { }, }, }, - inMappedType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + inMappedType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "SomeEnumType": { Index: 0, EnumeratedYANGTypeKey: "/field-name|enum", @@ -2028,10 +2029,10 @@ func TestUnionFieldToOneOf(t *testing.T) { }, }, }, - inEnums: map[string]*EnumeratedYANGType{ + inEnums: map[string]*ygen.EnumeratedYANGType{ "/field-name|enum": { Name: "SomeEnumType", - Kind: SimpleEnumerationType, + Kind: ygen.SimpleEnumerationType, TypeName: "enumeration", ValToYANGDetails: []ygot.EnumDefinition{{ Name: "SPEED_2.5G", @@ -2064,14 +2065,14 @@ func TestUnionFieldToOneOf(t *testing.T) { }, { name: "union with an enumeration, but union is typedef", inName: "FieldName", - inField: &NodeDetails{ + inField: &ygen.NodeDetails{ Name: "field-name", - Type: LeafNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.LeafNode, + YANGDetails: ygen.YANGNodeDetails{ Path: "/field-name", }, - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "SomeEnumType": { Index: 0, EnumeratedYANGTypeKey: "/field-name|enum", @@ -2082,8 +2083,8 @@ func TestUnionFieldToOneOf(t *testing.T) { }, }, }, - inMappedType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + inMappedType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "SomeEnumType": { Index: 0, EnumeratedYANGTypeKey: "/field-name|enum", @@ -2093,10 +2094,10 @@ func TestUnionFieldToOneOf(t *testing.T) { }, }, }, - inEnums: map[string]*EnumeratedYANGType{ + inEnums: map[string]*ygen.EnumeratedYANGType{ "/field-name|enum": { Name: "SomeEnumType", - Kind: DerivedUnionEnumerationType, + Kind: ygen.DerivedUnionEnumerationType, TypeName: "derived-union", ValToYANGDetails: []ygot.EnumDefinition{{ Name: "SPEED_2.5G", @@ -2121,15 +2122,15 @@ func TestUnionFieldToOneOf(t *testing.T) { }, { name: "leaflist of union", inName: "FieldName", - inField: &NodeDetails{ + inField: &ygen.NodeDetails{ Name: "field-name", - Type: LeafListNode, - YANGDetails: YANGNodeDetails{ + Type: ygen.LeafListNode, + YANGDetails: ygen.YANGNodeDetails{ Name: "field-name", Path: "/parent/field-name", }, - LangType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + LangType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, @@ -2139,8 +2140,8 @@ func TestUnionFieldToOneOf(t *testing.T) { }, }, }, - inMappedType: &MappedType{ - UnionTypes: map[string]MappedUnionSubtype{ + inMappedType: &ygen.MappedType{ + UnionTypes: map[string]ygen.MappedUnionSubtype{ "string": { Index: 0, }, diff --git a/ygen/testdata/proto/cross-ref-src.formatted-txt b/protogen/testdata/proto/cross-ref-src.formatted-txt similarity index 100% rename from ygen/testdata/proto/cross-ref-src.formatted-txt rename to protogen/testdata/proto/cross-ref-src.formatted-txt diff --git a/ygen/testdata/proto/cross-ref-src.yang b/protogen/testdata/proto/cross-ref-src.yang similarity index 100% rename from ygen/testdata/proto/cross-ref-src.yang rename to protogen/testdata/proto/cross-ref-src.yang diff --git a/ygen/testdata/proto/cross-ref-target.yang b/protogen/testdata/proto/cross-ref-target.yang similarity index 100% rename from ygen/testdata/proto/cross-ref-target.yang rename to protogen/testdata/proto/cross-ref-target.yang diff --git a/ygen/testdata/proto/enum-union.compress.enums.formatted-txt b/protogen/testdata/proto/enum-union.compress.enums.formatted-txt similarity index 100% rename from ygen/testdata/proto/enum-union.compress.enums.formatted-txt rename to protogen/testdata/proto/enum-union.compress.enums.formatted-txt diff --git a/ygen/testdata/proto/enum-union.compress.formatted-txt b/protogen/testdata/proto/enum-union.compress.formatted-txt similarity index 100% rename from ygen/testdata/proto/enum-union.compress.formatted-txt rename to protogen/testdata/proto/enum-union.compress.formatted-txt diff --git a/ygen/testdata/proto/excluded-config-false.compressed.formatted-txt b/protogen/testdata/proto/excluded-config-false.compressed.formatted-txt similarity index 100% rename from ygen/testdata/proto/excluded-config-false.compressed.formatted-txt rename to protogen/testdata/proto/excluded-config-false.compressed.formatted-txt diff --git a/ygen/testdata/proto/excluded-config-false.config_false.compressed.formatted-txt b/protogen/testdata/proto/excluded-config-false.config_false.compressed.formatted-txt similarity index 100% rename from ygen/testdata/proto/excluded-config-false.config_false.compressed.formatted-txt rename to protogen/testdata/proto/excluded-config-false.config_false.compressed.formatted-txt diff --git a/ygen/testdata/proto/excluded-config-false.uncompressed.formatted-txt b/protogen/testdata/proto/excluded-config-false.uncompressed.formatted-txt similarity index 100% rename from ygen/testdata/proto/excluded-config-false.uncompressed.formatted-txt rename to protogen/testdata/proto/excluded-config-false.uncompressed.formatted-txt diff --git a/ygen/testdata/proto/fakeroot-multimod-one.yang b/protogen/testdata/proto/fakeroot-multimod-one.yang similarity index 100% rename from ygen/testdata/proto/fakeroot-multimod-one.yang rename to protogen/testdata/proto/fakeroot-multimod-one.yang diff --git a/ygen/testdata/proto/fakeroot-multimod-two.yang b/protogen/testdata/proto/fakeroot-multimod-two.yang similarity index 100% rename from ygen/testdata/proto/fakeroot-multimod-two.yang rename to protogen/testdata/proto/fakeroot-multimod-two.yang diff --git a/ygen/testdata/proto/fakeroot-multimod.formatted-txt b/protogen/testdata/proto/fakeroot-multimod.formatted-txt similarity index 100% rename from ygen/testdata/proto/fakeroot-multimod.formatted-txt rename to protogen/testdata/proto/fakeroot-multimod.formatted-txt diff --git a/ygen/testdata/proto/nested-messages.compressed.enums.formatted-txt b/protogen/testdata/proto/nested-messages.compressed.enums.formatted-txt similarity index 100% rename from ygen/testdata/proto/nested-messages.compressed.enums.formatted-txt rename to protogen/testdata/proto/nested-messages.compressed.enums.formatted-txt diff --git a/ygen/testdata/proto/nested-messages.compressed.nested_messages.formatted-txt b/protogen/testdata/proto/nested-messages.compressed.nested_messages.formatted-txt similarity index 100% rename from ygen/testdata/proto/nested-messages.compressed.nested_messages.formatted-txt rename to protogen/testdata/proto/nested-messages.compressed.nested_messages.formatted-txt diff --git a/ygen/testdata/proto/nested-messages.enums.formatted-txt b/protogen/testdata/proto/nested-messages.enums.formatted-txt similarity index 100% rename from ygen/testdata/proto/nested-messages.enums.formatted-txt rename to protogen/testdata/proto/nested-messages.enums.formatted-txt diff --git a/ygen/testdata/proto/nested-messages.nested_messages.formatted-txt b/protogen/testdata/proto/nested-messages.nested_messages.formatted-txt similarity index 100% rename from ygen/testdata/proto/nested-messages.nested_messages.formatted-txt rename to protogen/testdata/proto/nested-messages.nested_messages.formatted-txt diff --git a/ygen/testdata/proto/nested-messages.openconfig.formatted-txt b/protogen/testdata/proto/nested-messages.openconfig.formatted-txt similarity index 100% rename from ygen/testdata/proto/nested-messages.openconfig.formatted-txt rename to protogen/testdata/proto/nested-messages.openconfig.formatted-txt diff --git a/ygen/testdata/proto/nested-messages.yang b/protogen/testdata/proto/nested-messages.yang similarity index 100% rename from ygen/testdata/proto/nested-messages.yang rename to protogen/testdata/proto/nested-messages.yang diff --git a/ygen/testdata/proto/proto-anydata-test.yang b/protogen/testdata/proto/proto-anydata-test.yang similarity index 100% rename from ygen/testdata/proto/proto-anydata-test.yang rename to protogen/testdata/proto/proto-anydata-test.yang diff --git a/ygen/testdata/proto/proto-enums-addid.enums.formatted-txt b/protogen/testdata/proto/proto-enums-addid.enums.formatted-txt similarity index 77% rename from ygen/testdata/proto/proto-enums-addid.enums.formatted-txt rename to protogen/testdata/proto/proto-enums-addid.enums.formatted-txt index d83611f41..2cec76bb8 100644 --- a/ygen/testdata/proto/proto-enums-addid.enums.formatted-txt +++ b/protogen/testdata/proto/proto-enums-addid.enums.formatted-txt @@ -28,3 +28,10 @@ enum ProtoEnumsEnumUnionTypedefEnum { PROTOENUMSENUMUNIONTYPEDEFENUM_UNSET = 0; PROTOENUMSENUMUNIONTYPEDEFENUM_B_VAL = 1 [(yext.yang_name) = "B_VAL"]; } + +// ProtoEnumsIrefTypedef represents an enumerated type generated for the YANG identity BASE_IDENTITY. +enum ProtoEnumsIrefTypedef { + PROTOENUMSIREFTYPEDEF_UNSET = 0; + PROTOENUMSIREFTYPEDEF_DERIVED_IDENTITY = 191733515 [(yext.yang_name) = "DERIVED_IDENTITY"]; + PROTOENUMSIREFTYPEDEF_REMOTE_IDENTITY = 494029318 [(yext.yang_name) = "REMOTE_IDENTITY"]; +} diff --git a/ygen/testdata/proto/proto-enums-addid.formatted-txt b/protogen/testdata/proto/proto-enums-addid.formatted-txt similarity index 94% rename from ygen/testdata/proto/proto-enums-addid.formatted-txt rename to protogen/testdata/proto/proto-enums-addid.formatted-txt index 0caaff0c4..5552ab40c 100644 --- a/ygen/testdata/proto/proto-enums-addid.formatted-txt +++ b/protogen/testdata/proto/proto-enums-addid.formatted-txt @@ -28,4 +28,5 @@ message A { openconfig.enums.ProtoEnumsBASEIDENTITY e_protoenumsbaseidentity = 261975251; string e_string = 222327361; } + openconfig.enums.ProtoEnumsIrefTypedef f = 314438328; } diff --git a/ygen/testdata/proto/proto-enums-addid.yang b/protogen/testdata/proto/proto-enums-addid.yang similarity index 100% rename from ygen/testdata/proto/proto-enums-addid.yang rename to protogen/testdata/proto/proto-enums-addid.yang diff --git a/ygen/testdata/proto/proto-enums.enums.formatted-txt b/protogen/testdata/proto/proto-enums.enums.formatted-txt similarity index 80% rename from ygen/testdata/proto/proto-enums.enums.formatted-txt rename to protogen/testdata/proto/proto-enums.enums.formatted-txt index c302ac098..569b01644 100644 --- a/ygen/testdata/proto/proto-enums.enums.formatted-txt +++ b/protogen/testdata/proto/proto-enums.enums.formatted-txt @@ -24,3 +24,9 @@ enum ProtoEnumsEnumUnionTypedefEnum { PROTOENUMSENUMUNIONTYPEDEFENUM_UNSET = 0; PROTOENUMSENUMUNIONTYPEDEFENUM_B_VAL = 1; } + +// ProtoEnumsIrefTypedef represents an enumerated type generated for the YANG identity BASE_IDENTITY. +enum ProtoEnumsIrefTypedef { + PROTOENUMSIREFTYPEDEF_UNSET = 0; + PROTOENUMSIREFTYPEDEF_DERIVED_IDENTITY = 191733515; +} diff --git a/ygen/testdata/proto/proto-enums.formatted-txt b/protogen/testdata/proto/proto-enums.formatted-txt similarity index 93% rename from ygen/testdata/proto/proto-enums.formatted-txt rename to protogen/testdata/proto/proto-enums.formatted-txt index aa3694748..d53c600c6 100644 --- a/ygen/testdata/proto/proto-enums.formatted-txt +++ b/protogen/testdata/proto/proto-enums.formatted-txt @@ -26,4 +26,5 @@ message A { openconfig.enums.ProtoEnumsBASEIDENTITY e_protoenumsbaseidentity = 261975251; string e_string = 222327361; } + openconfig.enums.ProtoEnumsIrefTypedef f = 314438328; } diff --git a/ygen/testdata/proto/proto-enums.yang b/protogen/testdata/proto/proto-enums.yang similarity index 86% rename from ygen/testdata/proto/proto-enums.yang rename to protogen/testdata/proto/proto-enums.yang index e381e0a47..df0f4e05a 100644 --- a/ygen/testdata/proto/proto-enums.yang +++ b/protogen/testdata/proto/proto-enums.yang @@ -31,6 +31,12 @@ module proto-enums { } } + typedef iref-typedef { + type identityref { + base BASE_IDENTITY; + } + } + container a { leaf a { type enumeration { @@ -55,5 +61,10 @@ module proto-enums { leaf e { type union-identityref-typedef; } + + leaf f { + type iref-typedef; + } + } } diff --git a/ygen/testdata/proto/proto-test-a.compress.parent.child.formatted-txt b/protogen/testdata/proto/proto-test-a.compress.parent.child.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-a.compress.parent.child.formatted-txt rename to protogen/testdata/proto/proto-test-a.compress.parent.child.formatted-txt diff --git a/ygen/testdata/proto/proto-test-a.compress.parent.formatted-txt b/protogen/testdata/proto/proto-test-a.compress.parent.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-a.compress.parent.formatted-txt rename to protogen/testdata/proto/proto-test-a.compress.parent.formatted-txt diff --git a/ygen/testdata/proto/proto-test-a.nocompress.formatted-txt b/protogen/testdata/proto/proto-test-a.nocompress.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-a.nocompress.formatted-txt rename to protogen/testdata/proto/proto-test-a.nocompress.formatted-txt diff --git a/ygen/testdata/proto/proto-test-a.nocompress.parent.child.formatted-txt b/protogen/testdata/proto/proto-test-a.nocompress.parent.child.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-a.nocompress.parent.child.formatted-txt rename to protogen/testdata/proto/proto-test-a.nocompress.parent.child.formatted-txt diff --git a/ygen/testdata/proto/proto-test-a.nocompress.parent.formatted-txt b/protogen/testdata/proto/proto-test-a.nocompress.parent.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-a.nocompress.parent.formatted-txt rename to protogen/testdata/proto/proto-test-a.nocompress.parent.formatted-txt diff --git a/ygen/testdata/proto/proto-test-a.yang b/protogen/testdata/proto/proto-test-a.yang similarity index 100% rename from ygen/testdata/proto/proto-test-a.yang rename to protogen/testdata/proto/proto-test-a.yang diff --git a/ygen/testdata/proto/proto-test-b.compress.device.formatted-txt b/protogen/testdata/proto/proto-test-b.compress.device.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-b.compress.device.formatted-txt rename to protogen/testdata/proto/proto-test-b.compress.device.formatted-txt diff --git a/ygen/testdata/proto/proto-test-b.compress.formatted-txt b/protogen/testdata/proto/proto-test-b.compress.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-b.compress.formatted-txt rename to protogen/testdata/proto/proto-test-b.compress.formatted-txt diff --git a/ygen/testdata/proto/proto-test-b.yang b/protogen/testdata/proto/proto-test-b.yang similarity index 100% rename from ygen/testdata/proto/proto-test-b.yang rename to protogen/testdata/proto/proto-test-b.yang diff --git a/ygen/testdata/proto/proto-test-c.enums.formatted-txt b/protogen/testdata/proto/proto-test-c.enums.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-c.enums.formatted-txt rename to protogen/testdata/proto/proto-test-c.enums.formatted-txt diff --git a/ygen/testdata/proto/proto-test-c.proto-test-c.elists.elist.formatted-txt b/protogen/testdata/proto/proto-test-c.proto-test-c.elists.elist.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-c.proto-test-c.elists.elist.formatted-txt rename to protogen/testdata/proto/proto-test-c.proto-test-c.elists.elist.formatted-txt diff --git a/ygen/testdata/proto/proto-test-c.proto-test-c.elists.formatted-txt b/protogen/testdata/proto/proto-test-c.proto-test-c.elists.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-c.proto-test-c.elists.formatted-txt rename to protogen/testdata/proto/proto-test-c.proto-test-c.elists.formatted-txt diff --git a/ygen/testdata/proto/proto-test-c.proto-test-c.entity.formatted-txt b/protogen/testdata/proto/proto-test-c.proto-test-c.entity.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-c.proto-test-c.entity.formatted-txt rename to protogen/testdata/proto/proto-test-c.proto-test-c.entity.formatted-txt diff --git a/ygen/testdata/proto/proto-test-c.proto-test-c.formatted-txt b/protogen/testdata/proto/proto-test-c.proto-test-c.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-c.proto-test-c.formatted-txt rename to protogen/testdata/proto/proto-test-c.proto-test-c.formatted-txt diff --git a/ygen/testdata/proto/proto-test-c.yang b/protogen/testdata/proto/proto-test-c.yang similarity index 100% rename from ygen/testdata/proto/proto-test-c.yang rename to protogen/testdata/proto/proto-test-c.yang diff --git a/ygen/testdata/proto/proto-test-d.uncompressed.enums.formatted-txt b/protogen/testdata/proto/proto-test-d.uncompressed.enums.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-d.uncompressed.enums.formatted-txt rename to protogen/testdata/proto/proto-test-d.uncompressed.enums.formatted-txt diff --git a/ygen/testdata/proto/proto-test-d.uncompressed.proto-test-d.formatted-txt b/protogen/testdata/proto/proto-test-d.uncompressed.proto-test-d.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-d.uncompressed.proto-test-d.formatted-txt rename to protogen/testdata/proto/proto-test-d.uncompressed.proto-test-d.formatted-txt diff --git a/ygen/testdata/proto/proto-test-d.uncompressed.proto-test-d.test.formatted-txt b/protogen/testdata/proto/proto-test-d.uncompressed.proto-test-d.test.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-d.uncompressed.proto-test-d.test.formatted-txt rename to protogen/testdata/proto/proto-test-d.uncompressed.proto-test-d.test.formatted-txt diff --git a/ygen/testdata/proto/proto-test-d.yang b/protogen/testdata/proto/proto-test-d.yang similarity index 100% rename from ygen/testdata/proto/proto-test-d.yang rename to protogen/testdata/proto/proto-test-d.yang diff --git a/ygen/testdata/proto/proto-test-e.uncompressed.enums.formatted-txt b/protogen/testdata/proto/proto-test-e.uncompressed.enums.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-e.uncompressed.enums.formatted-txt rename to protogen/testdata/proto/proto-test-e.uncompressed.enums.formatted-txt diff --git a/ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.animal.formatted-txt b/protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.animal.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.animal.formatted-txt rename to protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.animal.formatted-txt diff --git a/ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.formatted-txt b/protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.formatted-txt rename to protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.animals.formatted-txt diff --git a/ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.bars.formatted-txt b/protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.bars.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.bars.formatted-txt rename to protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.bars.formatted-txt diff --git a/ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.foo.formatted-txt b/protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.foo.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.foo.formatted-txt rename to protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.foo.formatted-txt diff --git a/ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.formatted-txt b/protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.formatted-txt rename to protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.foos.formatted-txt diff --git a/ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.formatted-txt b/protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.formatted-txt rename to protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.formatted-txt diff --git a/ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.test.formatted-txt b/protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.test.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-e.uncompressed.proto-test-e.test.formatted-txt rename to protogen/testdata/proto/proto-test-e.uncompressed.proto-test-e.test.formatted-txt diff --git a/ygen/testdata/proto/proto-test-e.yang b/protogen/testdata/proto/proto-test-e.yang similarity index 100% rename from ygen/testdata/proto/proto-test-e.yang rename to protogen/testdata/proto/proto-test-e.yang diff --git a/ygen/testdata/proto/proto-test-f.yang b/protogen/testdata/proto/proto-test-f.yang similarity index 100% rename from ygen/testdata/proto/proto-test-f.yang rename to protogen/testdata/proto/proto-test-f.yang diff --git a/ygen/testdata/proto/proto-test-g.proto-test-g.formatted-txt b/protogen/testdata/proto/proto-test-g.proto-test-g.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-test-g.proto-test-g.formatted-txt rename to protogen/testdata/proto/proto-test-g.proto-test-g.formatted-txt diff --git a/ygen/testdata/proto/proto-test-g.yang b/protogen/testdata/proto/proto-test-g.yang similarity index 100% rename from ygen/testdata/proto/proto-test-g.yang rename to protogen/testdata/proto/proto-test-g.yang diff --git a/ygen/testdata/proto/proto-union-list-key.compressed.openconfig.formatted-txt b/protogen/testdata/proto/proto-union-list-key.compressed.openconfig.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-union-list-key.compressed.openconfig.formatted-txt rename to protogen/testdata/proto/proto-union-list-key.compressed.openconfig.formatted-txt diff --git a/ygen/testdata/proto/proto-union-list-key.compressed.openconfig.routing_policy.formatted-txt b/protogen/testdata/proto/proto-union-list-key.compressed.openconfig.routing_policy.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-union-list-key.compressed.openconfig.routing_policy.formatted-txt rename to protogen/testdata/proto/proto-union-list-key.compressed.openconfig.routing_policy.formatted-txt diff --git a/ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.formatted-txt b/protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.formatted-txt rename to protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.formatted-txt diff --git a/ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.formatted-txt b/protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.formatted-txt rename to protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.formatted-txt diff --git a/ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.formatted-txt b/protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.formatted-txt rename to protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.formatted-txt diff --git a/ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.policy.formatted-txt b/protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.policy.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.policy.formatted-txt rename to protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.policy.formatted-txt diff --git a/ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.sets.formatted-txt b/protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.sets.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.sets.formatted-txt rename to protogen/testdata/proto/proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.sets.formatted-txt diff --git a/ygen/testdata/proto/proto-union-list-key.yang b/protogen/testdata/proto/proto-union-list-key.yang similarity index 100% rename from ygen/testdata/proto/proto-union-list-key.yang rename to protogen/testdata/proto/proto-union-list-key.yang diff --git a/ygen/testdata/proto/proto-union-list_key.uncompressed.openconfig.formatted-txt b/protogen/testdata/proto/proto-union-list_key.uncompressed.openconfig.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto-union-list_key.uncompressed.openconfig.formatted-txt rename to protogen/testdata/proto/proto-union-list_key.uncompressed.openconfig.formatted-txt diff --git a/ygen/testdata/proto/proto_anydata_test.e.formatted-txt b/protogen/testdata/proto/proto_anydata_test.e.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto_anydata_test.e.formatted-txt rename to protogen/testdata/proto/proto_anydata_test.e.formatted-txt diff --git a/ygen/testdata/proto/proto_anydata_test.formatted-txt b/protogen/testdata/proto/proto_anydata_test.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto_anydata_test.formatted-txt rename to protogen/testdata/proto/proto_anydata_test.formatted-txt diff --git a/ygen/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.c.formatted-txt b/protogen/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.c.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.c.formatted-txt rename to protogen/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.c.formatted-txt diff --git a/ygen/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.formatted-txt b/protogen/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.formatted-txt rename to protogen/testdata/proto/proto_test_f.uncompressed.proto_test_f.a.formatted-txt diff --git a/ygen/testdata/proto/proto_test_f.uncompressed.proto_test_f.formatted-txt b/protogen/testdata/proto/proto_test_f.uncompressed.proto_test_f.formatted-txt similarity index 100% rename from ygen/testdata/proto/proto_test_f.uncompressed.proto_test_f.formatted-txt rename to protogen/testdata/proto/proto_test_f.uncompressed.proto_test_f.formatted-txt diff --git a/ygen/testdata/proto/union-list-key.enums.formatted-txt b/protogen/testdata/proto/union-list-key.enums.formatted-txt similarity index 100% rename from ygen/testdata/proto/union-list-key.enums.formatted-txt rename to protogen/testdata/proto/union-list-key.enums.formatted-txt diff --git a/ygen/testdata/proto/union-list-key.formatted-txt b/protogen/testdata/proto/union-list-key.formatted-txt similarity index 100% rename from ygen/testdata/proto/union-list-key.formatted-txt rename to protogen/testdata/proto/union-list-key.formatted-txt diff --git a/ygen/testdata/proto/union-list-key.union_list_key.formatted-txt b/protogen/testdata/proto/union-list-key.union_list_key.formatted-txt similarity index 100% rename from ygen/testdata/proto/union-list-key.union_list_key.formatted-txt rename to protogen/testdata/proto/union-list-key.union_list_key.formatted-txt diff --git a/ygen/testdata/proto/union-list-key.yang b/protogen/testdata/proto/union-list-key.yang similarity index 100% rename from ygen/testdata/proto/union-list-key.yang rename to protogen/testdata/proto/union-list-key.yang diff --git a/testdata/modules/openconfig-complex.yang b/testdata/modules/openconfig-complex.yang index ade53d669..e75192487 100644 --- a/testdata/modules/openconfig-complex.yang +++ b/testdata/modules/openconfig-complex.yang @@ -17,6 +17,12 @@ module openconfig-complex { default 5; } + typedef program { + type identityref { + base SOFTWARE; + } + } + typedef weekend-days { type enumeration { enum SATURDAY; @@ -61,6 +67,10 @@ module openconfig-complex { type identityref { base "SOFTWARE"; } } + leaf iref2 { + type program; + } + leaf simple-union-enum { type union { type uint64; diff --git a/ygen/codegen.go b/ygen/codegen.go index 987054fcc..bad983fb9 100644 --- a/ygen/codegen.go +++ b/ygen/codegen.go @@ -12,15 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package ygen contains a library to generate Go structs from a YANG model. +// Package ygen contains a library and base configuration options that can be +// extended to generate language-specific structs from a YANG model. // The Goyang parsing library is used to parse YANG. The output can consider // OpenConfig-specific conventions such that the schema is compressed. +// The output of this library is an intermediate representation (IR) designed +// to reduce the need for working with the Goyang parsing library's AST. package ygen import ( "errors" "fmt" - "sort" "strings" "github.com/openconfig/goyang/pkg/yang" @@ -32,42 +34,6 @@ import ( gpb "github.com/openconfig/gnmi/proto/gnmi" ) -// YANGCodeGenerator is a structure that is used to pass arguments as to -// how the output Go code should be generated. -type YANGCodeGenerator struct { - // Config stores the configuration parameters used for code generation. - Config GeneratorConfig -} - -// GeneratorConfig stores the configuration options used for code generation. -type GeneratorConfig struct { - // PackageName is the name that should be used for the generating package. - PackageName string - // Caller is the name of the binary calling the generator library, it is - // included in the header of output files for debugging purposes. If a - // string is not specified, the location of the library is utilised. - Caller string - // GenerateJSONSchema stores a boolean which defines whether to generate - // the JSON corresponding to the YANG schema parsed to generate the - // output code. - GenerateJSONSchema bool - // StoreRawSchema the raw JSON schema should be returned by the code - // generation function, such that it can be handled by an external - // library. - StoreRawSchema bool - // ParseOptions contains parsing options for a given set of schema files. - ParseOptions ParseOpts - // TransformationOptions contains options for how the generated code - // may be transformed from a simple 1:1 mapping with respect to the - // given YANG schema. - TransformationOptions TransformationOpts - // ProtoOptions stores a struct which contains Protobuf specific options. - ProtoOptions ProtoOpts - // IncludeDescriptions specifies that YANG entry descriptions are added - // to the JSON schema. Is false by default, to reduce the size of generated schema - IncludeDescriptions bool -} - // ParseOpts contains parsing configuration for a given schema. type ParseOpts struct { // ExcludeModules specifies any modules that are included within the set of @@ -79,6 +45,27 @@ type ParseOpts struct { // github.com/openconfig/goyang/pkg/yang library. These specify how the // input YANG files should be parsed. YANGParseOptions yang.Options +} + +// TransformationOpts specifies transformations to the generated code with +// respect to the input schema. +type TransformationOpts struct { + // CompressBehaviour specifies how the set of direct children of any + // entry should determined. Specifically, whether compression is + // enabled, and whether state fields in the schema should be excluded. + CompressBehaviour genutil.CompressBehaviour + // GenerateFakeRoot specifies whether an entity that represents the + // root of the YANG schema tree should be generated in the generated + // code. + GenerateFakeRoot bool + // FakeRootName specifies the name of the struct that should be generated + // representing the root. + FakeRootName string + // ExcludeState specifies whether config false values should be + // included in the generated code output. When set, all values that are + // not writeable (i.e., config false) within the YANG schema and their + // children are excluded from the generated code. + ExcludeState bool // SkipEnumDeduplication specifies whether leaves of type 'enumeration' that // are used in multiple places in the schema should share a common type within // the generated code that is output by ygen. By default (false), a common type @@ -107,31 +94,6 @@ type ParseOpts struct { // When it is disabled, two different enumerations (ModuleName_(State|Config)_Enabled) // will be output in the generated code. SkipEnumDeduplication bool -} - -// TransformationOpts specifies transformations to the generated code with -// respect to the input schema. -type TransformationOpts struct { - // CompressBehaviour specifies how the set of direct children of any - // entry should determined. Specifically, whether compression is - // enabled, and whether state fields in the schema should be excluded. - CompressBehaviour genutil.CompressBehaviour - // IgnoreShadowSchemaPaths indicates whether when OpenConfig path - // compression is enabled, that the shadowed paths are to be ignored - // while while unmarshalling. - IgnoreShadowSchemaPaths bool - // GenerateFakeRoot specifies whether an entity that represents the - // root of the YANG schema tree should be generated in the generated - // code. - GenerateFakeRoot bool - // FakeRootName specifies the name of the struct that should be generated - // representing the root. - FakeRootName string - // ExcludeState specifies whether config false values should be - // included in the generated code output. When set, all values that are - // not writeable (i.e., config false) within the YANG schema and their - // children are excluded from the generated code. - ExcludeState bool // ShortenEnumLeafNames removes the module name from the name of // enumeration leaves. ShortenEnumLeafNames bool @@ -147,58 +109,6 @@ type TransformationOpts struct { EnumerationsUseUnderscores bool } -// ProtoOpts stores Protobuf specific options for the code generation library. -type ProtoOpts struct { - // BaseImportPath stores the root URL or path for imports that are - // relative within the imported protobufs. - BaseImportPath string - // EnumPackageName stores the package name that should be used - // for the package that defines enumerated types that are used - // in multiple parts of the schema (identityrefs, and enumerations) - // that fall within type definitions. - EnumPackageName string - // YwrapperPath is the path to the ywrapper.proto file that stores - // the definition of the wrapper messages used to ensure that unset - // fields can be distinguished from those that are set to their - // default value. The path excluds the filename. - YwrapperPath string - // YextPath is the path to the yext.proto file that stores the - // definition of the extension messages that are used to annotat the - // generated protobuf messages. - YextPath string - // AnnotateSchemaPaths specifies whether the extensions defined in - // yext.proto should be used to annotate schema paths into the output - // protobuf file. See - // https://github.com/openconfig/ygot/blob/master/docs/yang-to-protobuf-transformations-spec.md#annotation-of-schema-paths - AnnotateSchemaPaths bool - // AnnotateEnumNames specifies whether the extensions defined in - // yext.proto should be used to annotate enum values with their - // original YANG names in the output protobuf file. - // See https://github.com/openconfig/ygot/blob/master/docs/yang-to-protobuf-transformations-spec.md#annotation-of-enums - AnnotateEnumNames bool - // NestedMessages indicates whether nested messages should be - // output for the protobuf schema. If false, a separate package - // is generated per package. - NestedMessages bool - // GoPackageBase specifies the base of the names that are used in - // the go_package file option for generated protobufs. Additional - // package identifiers are appended to the go_package - such that - // the format /// is used. - GoPackageBase string -} - -// NewYANGCodeGenerator returns a new instance of the YANGCodeGenerator -// struct to the calling function. -func NewYANGCodeGenerator(c *GeneratorConfig) *YANGCodeGenerator { - cg := &YANGCodeGenerator{} - - if c != nil { - cg.Config = *c - } - - return cg -} - // yangEnum represents an enumerated type in YANG that is to be output in the // Go code. The enumerated type may be a YANG 'identity' or enumeration. type yangEnum struct { @@ -212,189 +122,6 @@ type yangEnum struct { id string } -// GeneratedProto3 stores a set of generated Protobuf packages. -type GeneratedProto3 struct { - // Packages stores a map, keyed by the Protobuf package name, and containing the contents of the protobuf3 - // messages defined within the package. The calling application can write out the defined packages to the - // files expected by the protoc tool. - Packages map[string]Proto3Package -} - -// Proto3Package stores the code for a generated protobuf3 package. -type Proto3Package struct { - FilePath []string // FilePath is the path to the file that this package should be written to. - Header string // Header is the header text to be used in the package. - Messages []string // Messages is a slice of strings containing the set of messages that are within the generated package. - Enums []string // Enums is a slice of string containing the generated set of enumerations within the package. - UsesYwrapperImport bool // UsesYwrapperImport indicates whether the ywrapper proto package is used within the generated package. - UsesYextImport bool // UsesYextImport indicates whether the yext proto package is used within the generated package. -} - -// GenerateProto3 generates Protobuf 3 code for the input set of YANG files. -// The YANG schemas for which protobufs are to be created is supplied as the -// yangFiles argument, with included modules being searched for in includePaths. -// It returns a GeneratedProto3 struct containing the messages that are to be -// output, along with any associated values (e.g., enumerations). -func (cg *YANGCodeGenerator) GenerateProto3(yangFiles, includePaths []string) (*GeneratedProto3, util.Errors) { - basePackageName := cg.Config.PackageName - if basePackageName == "" { - basePackageName = DefaultBasePackageName - } - enumPackageName := cg.Config.ProtoOptions.EnumPackageName - if enumPackageName == "" { - enumPackageName = DefaultEnumPackageName - } - ywrapperPath := cg.Config.ProtoOptions.YwrapperPath - if ywrapperPath == "" { - ywrapperPath = DefaultYwrapperPath - } - yextPath := cg.Config.ProtoOptions.YextPath - if yextPath == "" { - yextPath = DefaultYextPath - } - - // This flag is always true for proto generation. - cg.Config.TransformationOptions.UseDefiningModuleForTypedefEnumNames = true - opts := IROptions{ - ParseOptions: cg.Config.ParseOptions, - TransformationOptions: cg.Config.TransformationOptions, - NestedDirectories: cg.Config.ProtoOptions.NestedMessages, - AbsoluteMapPaths: true, - AppendEnumSuffixForSimpleUnionEnums: true, - } - - ir, err := GenerateIR(yangFiles, includePaths, NewProtoLangMapper(basePackageName, enumPackageName), opts) - if err != nil { - return nil, util.NewErrs(err) - } - - protoEnums, err := writeProtoEnums(ir.Enums, cg.Config.ProtoOptions.AnnotateEnumNames) - if err != nil { - return nil, util.NewErrs(err) - } - - genProto := &GeneratedProto3{ - Packages: map[string]Proto3Package{}, - } - - // yerr stores errors encountered during code generation. - var yerr util.Errors - - // pkgImports lists the imports that are required for the package that is being - // written out. - pkgImports := map[string]map[string]interface{}{} - - // Only create the enums package if there are enums that are within the schema. - if len(protoEnums) > 0 { - // Sort the set of enumerations so that they are deterministically output. - sort.Strings(protoEnums) - fp := []string{basePackageName, enumPackageName, fmt.Sprintf("%s.proto", enumPackageName)} - genProto.Packages[fmt.Sprintf("%s.%s", basePackageName, enumPackageName)] = Proto3Package{ - FilePath: fp, - Enums: protoEnums, - UsesYextImport: cg.Config.ProtoOptions.AnnotateEnumNames, - } - } - - // Ensure that the slice of messages returned is in a deterministic order by - // sorting the message paths. We use the path rather than the name as the - // proto message name may not be unique. - for _, directoryPath := range ir.OrderedDirectoryPaths() { - m := ir.Directories[directoryPath] - - genMsg, errs := writeProto3Msg(m, ir, &protoMsgConfig{ - compressPaths: cg.Config.TransformationOptions.CompressBehaviour.CompressEnabled(), - basePackageName: basePackageName, - enumPackageName: enumPackageName, - baseImportPath: cg.Config.ProtoOptions.BaseImportPath, - annotateSchemaPaths: cg.Config.ProtoOptions.AnnotateSchemaPaths, - annotateEnumNames: cg.Config.ProtoOptions.AnnotateEnumNames, - nestedMessages: cg.Config.ProtoOptions.NestedMessages, - }) - - if errs != nil { - yerr = util.AppendErrs(yerr, errs) - continue - } - - // Check whether any messages were required for this schema element, writeProto3Msg can - // return nil if nested messages were being produced, and the message was encapsulated - // in another message. - if genMsg == nil { - continue - } - - if genMsg.PackageName == "" { - genMsg.PackageName = basePackageName - } else { - genMsg.PackageName = fmt.Sprintf("%s.%s", basePackageName, genMsg.PackageName) - } - - if pkgImports[genMsg.PackageName] == nil { - pkgImports[genMsg.PackageName] = map[string]interface{}{} - } - addNewKeys(pkgImports[genMsg.PackageName], genMsg.RequiredImports) - - // If the package does not already exist within the generated proto3 - // output, then create it within the package map. This allows different - // entries in the msgNames set to fall within the same package. - tp, ok := genProto.Packages[genMsg.PackageName] - if !ok { - genProto.Packages[genMsg.PackageName] = Proto3Package{ - FilePath: protoPackageToFilePath(genMsg.PackageName), - Messages: []string{}, - } - tp = genProto.Packages[genMsg.PackageName] - } - tp.Messages = append(tp.Messages, genMsg.MessageCode) - if genMsg.UsesYwrapperImport { - tp.UsesYwrapperImport = true - } - if genMsg.UsesYextImport { - tp.UsesYextImport = true - } - genProto.Packages[genMsg.PackageName] = tp - } - - for n, pkg := range genProto.Packages { - var gpn string - if cg.Config.ProtoOptions.GoPackageBase != "" { - gpn = fmt.Sprintf("%s/%s", cg.Config.ProtoOptions.GoPackageBase, strings.ReplaceAll(n, ".", "/")) - } - ywrapperPath := ywrapperPath - if !pkg.UsesYwrapperImport { - ywrapperPath = "" - } - yextPath := yextPath - if !pkg.UsesYextImport { - yextPath = "" - } - h, err := writeProto3Header(proto3Header{ - PackageName: n, - Imports: stringKeys(pkgImports[n]), - SourceYANGFiles: yangFiles, - SourceYANGIncludePaths: includePaths, - CompressPaths: cg.Config.TransformationOptions.CompressBehaviour.CompressEnabled(), - CallerName: cg.Config.Caller, - YwrapperPath: ywrapperPath, - YextPath: yextPath, - GoPackageName: gpn, - }) - if err != nil { - yerr = util.AppendErrs(yerr, []error{err}) - continue - } - pkg.Header = h - genProto.Packages[n] = pkg - } - - if yerr != nil { - return nil, yerr - } - - return genProto, nil -} - // processModules takes a list of the filenames of YANG modules (yangFiles), // and a list of paths in which included modules or submodules may be found, // and returns a processed set of yang.Entry pointers which correspond to the @@ -475,18 +202,18 @@ type mappedYANGDefinitions struct { // - yangFiles: an input set of YANG schema files and the paths that // - includePaths: the set of paths that are to be searched for included or // imported YANG modules. -// - cfg: the current generator's configuration. +// - opts: the current generator's configuration. // It returns a mappedYANGDefinitions struct populated with the directory, enum // entries in the input schemas as well as the calculated schema tree. -func mappedDefinitions(yangFiles, includePaths []string, cfg *GeneratorConfig) (*mappedYANGDefinitions, util.Errors) { - modules, errs := processModules(yangFiles, includePaths, cfg.ParseOptions.YANGParseOptions) +func mappedDefinitions(yangFiles, includePaths []string, opts IROptions) (*mappedYANGDefinitions, util.Errors) { + modules, errs := processModules(yangFiles, includePaths, opts.ParseOptions.YANGParseOptions) if errs != nil { return nil, errs } // Build a map of excluded modules to simplify lookup. excluded := map[string]bool{} - for _, e := range cfg.ParseOptions.ExcludeModules { + for _, e := range opts.ParseOptions.ExcludeModules { excluded[e] = true } @@ -497,9 +224,9 @@ func mappedDefinitions(yangFiles, includePaths []string, cfg *GeneratorConfig) ( var rootElems, treeElems []*yang.Entry for _, module := range modules { // Need to transform the AST based on compression behaviour. - genutil.TransformEntry(module, cfg.TransformationOptions.CompressBehaviour) + genutil.TransformEntry(module, opts.TransformationOptions.CompressBehaviour) - errs = append(errs, findMappableEntities(module, dirs, enums, cfg.ParseOptions.ExcludeModules, cfg.TransformationOptions.CompressBehaviour.CompressEnabled(), modules)...) + errs = append(errs, findMappableEntities(module, dirs, enums, opts.ParseOptions.ExcludeModules, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), modules)...) if module == nil { errs = append(errs, errors.New("found a nil module in the returned module set")) continue @@ -526,8 +253,8 @@ func mappedDefinitions(yangFiles, includePaths []string, cfg *GeneratorConfig) ( // If we were asked to generate a fake root entity, then go and find the top-level entities that // we were asked for. - if cfg.TransformationOptions.GenerateFakeRoot { - if err := createFakeRoot(dirs, rootElems, cfg.TransformationOptions.FakeRootName, cfg.TransformationOptions.CompressBehaviour.CompressEnabled()); err != nil { + if opts.TransformationOptions.GenerateFakeRoot { + if err := createFakeRoot(dirs, rootElems, opts.TransformationOptions.FakeRootName, opts.TransformationOptions.CompressBehaviour.CompressEnabled()); err != nil { return nil, []error{err} } } diff --git a/ygen/codegen_test.go b/ygen/codegen_test.go index a3aa64ad1..f56f46517 100644 --- a/ygen/codegen_test.go +++ b/ygen/codegen_test.go @@ -15,33 +15,13 @@ package ygen import ( - "bytes" - "fmt" - "io/ioutil" - "path/filepath" - "sort" "testing" "github.com/kylelemons/godebug/pretty" - "github.com/openconfig/gnmi/errdiff" "github.com/openconfig/goyang/pkg/yang" - "github.com/openconfig/ygot/genutil" "github.com/openconfig/ygot/internal/igenutil" - "github.com/openconfig/ygot/testutil" ) -const ( - // TestRoot is the root of the test directory such that this is not - // repeated when referencing files. - TestRoot string = "" - // deflakeRuns specifies the number of runs of code generation that - // should be performed to check for flakes. - deflakeRuns int = 10 -) - -// datapath is the path to common YANG test modules. -const datapath = "../testdata/modules" - // TestFindMappableEntities tests the extraction of elements that are to be mapped // into Go code from a YANG schema. func TestFindMappableEntities(t *testing.T) { @@ -401,75 +381,6 @@ func TestFindMappableEntities(t *testing.T) { } } -func TestGenerateErrs(t *testing.T) { - tests := []struct { - name string - inFiles []string - inPath []string - inConfig GeneratorConfig - wantGoOK bool - wantGoErrSubstring string - wantProtoOK bool - wantProtoErrSubstring string - wantSameErrSubstring bool - }{{ - name: "missing YANG file", - inFiles: []string{filepath.Join(TestRoot, "testdata", "errors", "doesnt-exist.yang")}, - wantGoErrSubstring: "no such file", - wantSameErrSubstring: true, - }, { - name: "bad YANG file", - inFiles: []string{filepath.Join(TestRoot, "testdata", "errors", "bad-module.yang")}, - wantGoErrSubstring: "syntax error", - wantSameErrSubstring: true, - }, { - name: "missing import due to path", - inFiles: []string{filepath.Join(TestRoot, "testdata", "errors", "missing-import.yang")}, - wantGoErrSubstring: "no such module", - wantSameErrSubstring: true, - }, { - name: "import satisfied due to path", - inFiles: []string{filepath.Join(TestRoot, "testdata", "errors", "missing-import.yang")}, - inPath: []string{filepath.Join(TestRoot, "testdata", "errors", "subdir")}, - wantGoOK: true, - wantProtoOK: true, - }} - - for _, tt := range tests { - cg := NewYANGCodeGenerator(&tt.inConfig) - - // TODO(wenbli): Move this to integration_tests. - /* - _, goErr := cg.GenerateGoCode(tt.inFiles, tt.inPath) - switch { - case tt.wantGoOK && goErr != nil: - t.Errorf("%s: cg.GenerateGoCode(%v, %v): got unexpected error, got: %v, want: nil", tt.name, tt.inFiles, tt.inPath, goErr) - case tt.wantGoOK: - default: - if diff := errdiff.Substring(goErr, tt.wantGoErrSubstring); diff != "" { - t.Errorf("%s: cg.GenerateGoCode(%v, %v): %v", tt.name, tt.inFiles, tt.inPath, diff) - } - } - */ - - if tt.wantSameErrSubstring { - tt.wantProtoErrSubstring = tt.wantGoErrSubstring - } - - _, protoErr := cg.GenerateProto3(tt.inFiles, tt.inPath) - switch { - case tt.wantProtoOK && protoErr != nil: - t.Errorf("%s: cg.GenerateProto3(%v, %v): got unexpected error, got: %v, want: nil", tt.name, tt.inFiles, tt.inPath, protoErr) - case tt.wantProtoOK: - default: - if diff := errdiff.Substring(protoErr, tt.wantProtoErrSubstring); diff != "" { - t.Errorf("%s: cg.GenerateProto3(%v, %v): %v", tt.name, tt.inFiles, tt.inPath, diff) - } - } - - } -} - func TestFindRootEntries(t *testing.T) { tests := []struct { name string @@ -573,469 +484,6 @@ func TestFindRootEntries(t *testing.T) { } } -func TestGenerateProto3(t *testing.T) { - tests := []struct { - name string - inFiles []string - inIncludePaths []string - inConfig GeneratorConfig - // wantOutputFiles is a map keyed on protobuf package name with a path - // to the file that is expected for each package. - wantOutputFiles map[string]string - wantErr bool - }{{ - name: "simple protobuf test with compression", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.compress.parent.formatted-txt"), - "openconfig.parent": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.compress.parent.child.formatted-txt"), - }, - }, { - name: "simple protobuf test without compression", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.yang")}, - wantOutputFiles: map[string]string{ - "openconfig.proto_test_a": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.nocompress.formatted-txt"), - "openconfig.proto_test_a.parent": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.nocompress.parent.formatted-txt"), - "openconfig.proto_test_a.parent.child": filepath.Join(TestRoot, "testdata", "proto", "proto-test-a.nocompress.parent.child.formatted-txt"), - }, - }, { - name: "enumeration under unions test with compression", - inFiles: []string{filepath.Join(datapath, "enum-union.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - UseDefiningModuleForTypedefEnumNames: true, - }, - ProtoOptions: ProtoOpts{ - AnnotateEnumNames: true, - NestedMessages: true, - GoPackageBase: "github.com/foo/bar", - }, - }, - wantOutputFiles: map[string]string{ - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "enum-union.compress.formatted-txt"), - "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "enum-union.compress.enums.formatted-txt"), - }, - }, { - name: "yang schema with a list", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-b.yang")}, - inConfig: GeneratorConfig{TransformationOptions: TransformationOpts{CompressBehaviour: genutil.PreferIntendedConfig}}, - wantOutputFiles: map[string]string{ - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "proto-test-b.compress.formatted-txt"), - "openconfig.device": filepath.Join(TestRoot, "testdata", "proto", "proto-test-b.compress.device.formatted-txt"), - }, - }, { - name: "yang schema with simple enumerations", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.yang")}, - inConfig: GeneratorConfig{ - ProtoOptions: ProtoOpts{ - GoPackageBase: "github.com/foo/baz", - }, - }, - wantOutputFiles: map[string]string{ - "openconfig.proto_test_c": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.proto-test-c.formatted-txt"), - "openconfig.proto_test_c.entity": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.proto-test-c.entity.formatted-txt"), - "openconfig.proto_test_c.elists": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.proto-test-c.elists.formatted-txt"), - "openconfig.proto_test_c.elists.elist": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.proto-test-c.elists.elist.formatted-txt"), - "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-test-c.enums.formatted-txt"), - }, - }, { - name: "yang schema with identityref and enumerated typedef, compression off", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-d.yang")}, - wantOutputFiles: map[string]string{ - "openconfig.proto_test_d": filepath.Join(TestRoot, "testdata", "proto", "proto-test-d.uncompressed.proto-test-d.formatted-txt"), - "openconfig.proto_test_d.test": filepath.Join(TestRoot, "testdata", "proto", "proto-test-d.uncompressed.proto-test-d.test.formatted-txt"), - "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-test-d.uncompressed.enums.formatted-txt"), - }, - }, { - name: "yang schema with unions", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - UseDefiningModuleForTypedefEnumNames: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig.proto_test_e": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.formatted-txt"), - "openconfig.proto_test_e.test": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.test.formatted-txt"), - "openconfig.proto_test_e.foos": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.foos.formatted-txt"), - "openconfig.proto_test_e.foos.foo": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.foos.foo.formatted-txt"), - "openconfig.proto_test_e.bars": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.bars.formatted-txt"), - "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.enums.formatted-txt"), - "openconfig.proto_test_e.animals": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.animals.formatted-txt"), - "openconfig.proto_test_e.animals.animal": filepath.Join(TestRoot, "testdata", "proto", "proto-test-e.uncompressed.proto-test-e.animals.animal.formatted-txt"), - }, - }, { - name: "yang schema with anydata", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-anydata-test.yang")}, - wantOutputFiles: map[string]string{ - "openconfig.proto_anydata_test": filepath.Join(TestRoot, "testdata", "proto", "proto_anydata_test.formatted-txt"), - "openconfig.proto_anydata_test.e": filepath.Join(TestRoot, "testdata", "proto", "proto_anydata_test.e.formatted-txt"), - }, - }, { - name: "yang schema with path annotations", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-f.yang")}, - inConfig: GeneratorConfig{ - ProtoOptions: ProtoOpts{ - AnnotateSchemaPaths: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig.proto_test_f": filepath.Join(TestRoot, "testdata", "proto", "proto_test_f.uncompressed.proto_test_f.formatted-txt"), - "openconfig.proto_test_f.a": filepath.Join(TestRoot, "testdata", "proto", "proto_test_f.uncompressed.proto_test_f.a.formatted-txt"), - "openconfig.proto_test_f.a.c": filepath.Join(TestRoot, "testdata", "proto", "proto_test_f.uncompressed.proto_test_f.a.c.formatted-txt"), - }, - }, { - name: "yang schema with leafrefs that point to the same path", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-test-g.yang")}, - inConfig: GeneratorConfig{ - ProtoOptions: ProtoOpts{ - GoPackageBase: "github.com/foo/baz", - NestedMessages: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig.proto_test_g": filepath.Join(TestRoot, "testdata", "proto", "proto-test-g.proto-test-g.formatted-txt"), - }, - }, { - name: "yang schema with fake root, path compression and union list key", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - GenerateFakeRoot: true, - }, - ProtoOptions: ProtoOpts{ - AnnotateSchemaPaths: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.compressed.openconfig.formatted-txt"), - "openconfig.routing_policy": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.compressed.openconfig.routing_policy.formatted-txt"), - }, - }, { - name: "yang schema with fakeroot, and union list key", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - }, - ProtoOptions: ProtoOpts{ - AnnotateSchemaPaths: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list_key.uncompressed.openconfig.formatted-txt"), - "openconfig.proto_union_list_key": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.formatted-txt"), - "openconfig.proto_union_list_key.routing_policy": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.formatted-txt"), - "openconfig.proto_union_list_key.routing_policy.policies": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.formatted-txt"), - "openconfig.proto_union_list_key.routing_policy.policies.policy": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.policies.policy.formatted-txt"), - "openconfig.proto_union_list_key.routing_policy.sets": filepath.Join(TestRoot, "testdata", "proto", "proto-union-list-key.uncompressed.openconfig.proto_union_list_key.routing_policy.sets.formatted-txt"), - }, - }, { - name: "enums: yang schema with various types of enums with underscores", - inFiles: []string{filepath.Join(TestRoot, "testdata", "proto", "proto-enums.yang")}, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - UseDefiningModuleForTypedefEnumNames: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-enums.enums.formatted-txt"), - "openconfig.proto_enums": filepath.Join(TestRoot, "testdata", "proto", "proto-enums.formatted-txt"), - }, - }, { - name: "enums: yang schema with identity that adds to previous module", - inFiles: []string{ - filepath.Join(TestRoot, "testdata", "proto", "proto-enums.yang"), - filepath.Join(TestRoot, "testdata", "proto", "proto-enums-addid.yang"), - }, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - UseDefiningModuleForTypedefEnumNames: true, - }, - ProtoOptions: ProtoOpts{ - AnnotateEnumNames: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "proto-enums-addid.enums.formatted-txt"), - "openconfig.proto_enums": filepath.Join(TestRoot, "testdata", "proto", "proto-enums-addid.formatted-txt"), - }, - }, { - name: "yang schema with nested messages requested - uncompressed with fakeroot", - inFiles: []string{ - filepath.Join(TestRoot, "testdata", "proto", "nested-messages.yang"), - }, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - UseDefiningModuleForTypedefEnumNames: true, - }, - ProtoOptions: ProtoOpts{ - AnnotateEnumNames: true, - AnnotateSchemaPaths: true, - NestedMessages: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.openconfig.formatted-txt"), - "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.enums.formatted-txt"), - "openconfig.nested_messages": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.nested_messages.formatted-txt"), - }, - }, { - name: "yang schema with nested messages - compressed with fakeroot", - inFiles: []string{ - filepath.Join(TestRoot, "testdata", "proto", "nested-messages.yang"), - }, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - CompressBehaviour: genutil.PreferIntendedConfig, - IgnoreShadowSchemaPaths: true, - GenerateFakeRoot: true, - UseDefiningModuleForTypedefEnumNames: true, - }, - ProtoOptions: ProtoOpts{ - AnnotateEnumNames: true, - AnnotateSchemaPaths: true, - NestedMessages: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.compressed.enums.formatted-txt"), - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "nested-messages.compressed.nested_messages.formatted-txt"), - }, - }, { - name: "yang schema with a leafref key to a union with enumeration", - inFiles: []string{ - filepath.Join(TestRoot, "testdata", "proto", "union-list-key.yang"), - }, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - UseDefiningModuleForTypedefEnumNames: true, - }, - ProtoOptions: ProtoOpts{ - AnnotateEnumNames: true, - AnnotateSchemaPaths: true, - NestedMessages: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig.enums": filepath.Join(TestRoot, "testdata", "proto", "union-list-key.enums.formatted-txt"), - "openconfig.union_list_key": filepath.Join(TestRoot, "testdata", "proto", "union-list-key.union_list_key.formatted-txt"), - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "union-list-key.formatted-txt"), - }, - }, { - name: "protobuf generation with excluded read only fields - compressed", - inFiles: []string{ - filepath.Join(datapath, "openconfig-config-false.yang"), - }, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - CompressBehaviour: genutil.UncompressedExcludeDerivedState, - }, - ProtoOptions: ProtoOpts{ - AnnotateEnumNames: true, - AnnotateSchemaPaths: true, - NestedMessages: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "excluded-config-false.compressed.formatted-txt"), - "openconfig.openconfig_config_false": filepath.Join(TestRoot, "testdata", "proto", "excluded-config-false.config_false.compressed.formatted-txt"), - }, - }, { - name: "protobuf generation with excluded read only fields - compressed", - inFiles: []string{ - filepath.Join(datapath, "openconfig-config-false.yang"), - }, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - CompressBehaviour: genutil.ExcludeDerivedState, - }, - ProtoOptions: ProtoOpts{ - AnnotateEnumNames: true, - AnnotateSchemaPaths: true, - NestedMessages: true, - GoPackageBase: "github.com/openconfig/a/package", - }, - }, - wantOutputFiles: map[string]string{ - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "excluded-config-false.uncompressed.formatted-txt"), - }, - }, { - name: "protobuf generation with leafref to a module excluded by the test", - inFiles: []string{ - filepath.Join(TestRoot, "testdata", "proto", "cross-ref-target.yang"), - filepath.Join(TestRoot, "testdata", "proto", "cross-ref-src.yang"), - }, - inConfig: GeneratorConfig{ - ParseOptions: ParseOpts{ - ExcludeModules: []string{"cross-ref-target"}, - }, - ProtoOptions: ProtoOpts{ - NestedMessages: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig.cross_ref_src": filepath.Join(TestRoot, "testdata", "proto", "cross-ref-src.formatted-txt"), - }, - }, { - name: "multimod with fakeroot and nested", - inFiles: []string{ - filepath.Join(TestRoot, "testdata", "proto", "fakeroot-multimod-one.yang"), - filepath.Join(TestRoot, "testdata", "proto", "fakeroot-multimod-two.yang"), - }, - inConfig: GeneratorConfig{ - TransformationOptions: TransformationOpts{ - GenerateFakeRoot: true, - CompressBehaviour: genutil.PreferIntendedConfig, - }, - ProtoOptions: ProtoOpts{ - NestedMessages: true, - AnnotateEnumNames: true, - AnnotateSchemaPaths: true, - }, - }, - wantOutputFiles: map[string]string{ - "openconfig": filepath.Join(TestRoot, "testdata", "proto", "fakeroot-multimod.formatted-txt"), - }, - }} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - sortedPkgNames := func(pkgs map[string]string) []string { - wantPkgs := []string{} - for k := range tt.wantOutputFiles { - wantPkgs = append(wantPkgs, k) - } - sort.Strings(wantPkgs) - return wantPkgs - } - - genCode := func() *GeneratedProto3 { - if tt.inConfig.Caller == "" { - // Override the caller if it is not set, to ensure that test - // output is deterministic. - tt.inConfig.Caller = "codegen-tests" - } - - cg := NewYANGCodeGenerator(&tt.inConfig) - gotProto, err := cg.GenerateProto3(tt.inFiles, tt.inIncludePaths) - if (err != nil) != tt.wantErr { - t.Fatalf("cg.GenerateProto3(%v, %v), config: %v: got unexpected error: %v", tt.inFiles, tt.inIncludePaths, tt.inConfig, err) - } - - if tt.wantErr || err != nil { - return nil - } - - return gotProto - } - - gotProto := genCode() - - allCode := bytes.Buffer{} - - seenPkg := map[string]bool{} - for n := range gotProto.Packages { - seenPkg[n] = false - } - - protoPkgs := func(m map[string]Proto3Package) []string { - a := []string{} - for k := range m { - a = append(a, k) - } - return a - } - - wantPkgs := sortedPkgNames(tt.wantOutputFiles) - for _, pkg := range wantPkgs { - wantFile := tt.wantOutputFiles[pkg] - wantCodeBytes, err := ioutil.ReadFile(wantFile) - if err != nil { - t.Errorf("%s: ioutil.ReadFile(%v): could not read file for package %s", tt.name, wantFile, pkg) - return - } - - gotPkg, ok := gotProto.Packages[pkg] - if !ok { - t.Fatalf("%s: cg.GenerateProto3(%v, %v): did not find expected package %s in output, got: %#v, want key: %v", tt.name, tt.inFiles, tt.inIncludePaths, pkg, protoPkgs(gotProto.Packages), pkg) - } - - // Mark this package as having been seen. - seenPkg[pkg] = true - - // Write the returned struct out to a buffer to compare with the - // testdata file. - var gotCodeBuf bytes.Buffer - fmt.Fprintf(&gotCodeBuf, gotPkg.Header) - - for _, gotMsg := range gotPkg.Messages { - fmt.Fprintf(&gotCodeBuf, "%s\n", gotMsg) - } - - for _, gotEnum := range gotPkg.Enums { - fmt.Fprintf(&gotCodeBuf, "%s", gotEnum) - } - - wantCode := string(wantCodeBytes) - - allCode.WriteString(gotCodeBuf.String()) - - if diff := pretty.Compare(gotCodeBuf.String(), wantCode); diff != "" { - if diffl, _ := testutil.GenerateUnifiedDiff(wantCode, gotCodeBuf.String()); diffl != "" { - diff = diffl - } - t.Errorf("%s: cg.GenerateProto3(%v, %v) for package %s, did not get expected code (code file: %v), diff(-want, +got):\n%s", tt.name, tt.inFiles, tt.inIncludePaths, pkg, wantFile, diff) - } - } - - for pkg, seen := range seenPkg { - if !seen { - t.Errorf("%s: cg.GenerateProto3(%v, %v) did not test received package %v", tt.name, tt.inFiles, tt.inIncludePaths, pkg) - } - } - - for i := 0; i < deflakeRuns; i++ { - got := genCode() - var gotCodeBuf bytes.Buffer - - wantPkgs := sortedPkgNames(tt.wantOutputFiles) - for _, pkg := range wantPkgs { - gotPkg, ok := got.Packages[pkg] - if !ok { - t.Fatalf("%s: cg.GenerateProto3(%v, %v): did not find expected package %s in output, got: %#v, want key: %v", tt.name, tt.inFiles, tt.inIncludePaths, pkg, protoPkgs(gotProto.Packages), pkg) - } - fmt.Fprintf(&gotCodeBuf, gotPkg.Header) - for _, gotMsg := range gotPkg.Messages { - fmt.Fprintf(&gotCodeBuf, "%s\n", gotMsg) - } - for _, gotEnum := range gotPkg.Enums { - fmt.Fprintf(&gotCodeBuf, "%s", gotEnum) - } - } - - if diff := pretty.Compare(gotCodeBuf.String(), allCode.String()); diff != "" { - diff, _ = testutil.GenerateUnifiedDiff(allCode.String(), gotCodeBuf.String()) - t.Fatalf("flaky code generation iter: %d, diff(-want, +got):\n%s", i, diff) - } - } - }) - } -} - func TestMakeFakeRoot(t *testing.T) { tests := []struct { name string diff --git a/ygen/directory.go b/ygen/directory.go index 29aec2049..8e5edbf30 100644 --- a/ygen/directory.go +++ b/ygen/directory.go @@ -22,7 +22,6 @@ import ( "fmt" "reflect" "sort" - "strings" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/genutil" @@ -47,17 +46,6 @@ type Directory struct { IsFakeRoot bool // IsFakeRoot indicates that the struct is a fake root struct, so specific mapping rules should be implemented. } -// isChildOfModule determines whether the Directory represents a container -// or list member that is the direct child of a module entry. -func (y *ParsedDirectory) isChildOfModule() bool { - if y.IsFakeRoot || len(strings.Split(y.Path, "/")) == 3 { - // If the message has a path length of 3, then it is a top-level entity - // within a module, since the path is in the format []{"", , }. - return true - } - return false -} - // YangListAttr is used to store the additional elements for a Go struct that // are required if the struct represents a YANG list. It stores the name of // the key elements, and their associated types, along with pointers to those diff --git a/ygen/enumgen.go b/ygen/enumgen.go index 9071a7883..c34f15019 100644 --- a/ygen/enumgen.go +++ b/ygen/enumgen.go @@ -25,9 +25,9 @@ import ( ) const ( - // enumeratedUnionSuffix is the type name suffix given to enumerations + // EnumeratedUnionSuffix is the type name suffix given to enumerations // defined as part of a union type. - enumeratedUnionSuffix = "Enum" + EnumeratedUnionSuffix = "Enum" ) // enumSet contains generated enum names which can be queried. @@ -282,9 +282,9 @@ func (s *enumSet) enumeratedTypedefKey(args resolveTypeArgs, noUnderscores, useD if (useDefiningModuleForTypedefEnumNames && definingType.Kind == yang.Yunion) || (!useDefiningModuleForTypedefEnumNames && args.contextEntry.Type.Kind == yang.Yunion) { // We specifically say that this is an enumeration within the leaf. if noUnderscores { - typeName = fmt.Sprintf("%s%s", definingType.Name, enumeratedUnionSuffix) + typeName = fmt.Sprintf("%s%s", definingType.Name, EnumeratedUnionSuffix) } else { - typeName = fmt.Sprintf("%s_%s", definingType.Name, enumeratedUnionSuffix) + typeName = fmt.Sprintf("%s_%s", definingType.Name, EnumeratedUnionSuffix) } } if args.contextEntry.Node == nil { @@ -336,7 +336,7 @@ func (s *enumSet) enumLeafKey(e *yang.Entry, compressPaths, noUnderscores, skipD nameElements = append([]string{genutil.ParentModulePrettyName(e.Node, enumOrgPrefixesToTrim...)}, nameElements...) } if addEnumeratedUnionSuffix { - nameElements = append(nameElements, enumeratedUnionSuffix) + nameElements = append(nameElements, EnumeratedUnionSuffix) } compressName = strings.Join(nameElements, delimiter) @@ -592,10 +592,14 @@ func findEnumSet(entries map[string]*yang.Entry, compressPaths, noUnderscores, s continue } if _, ok := genEnums[typeName]; !ok { + kind := DerivedEnumerationType + if e.Type.IdentityBase != nil { + kind = IdentityType + } genEnums[typeName] = &yangEnum{ name: typeName, entry: e, - kind: DerivedEnumerationType, + kind: kind, id: key, } } diff --git a/ygen/enumgen_test.go b/ygen/enumgen_test.go index ccf38ef6a..ceb50d3e4 100644 --- a/ygen/enumgen_test.go +++ b/ygen/enumgen_test.go @@ -4773,7 +4773,7 @@ func TestFindEnumSet(t *testing.T) { } for _, appendEnumSuffixForSimpleUnionEnums := range []bool{false, true} { if appendEnumSuffixForSimpleUnionEnums && tt.isSimpleEnumeratedUnionLeaf && useDefiningModuleForTypedefEnumNames { - enumSuffix := enumeratedUnionSuffix + enumSuffix := EnumeratedUnionSuffix if !tt.inOmitUnderscores { enumSuffix = "_" + enumSuffix } diff --git a/ygen/genir.go b/ygen/genir.go index 866445006..1f69508eb 100644 --- a/ygen/genir.go +++ b/ygen/genir.go @@ -71,15 +71,12 @@ func GenerateIR(yangFiles, includePaths []string, langMapper LangMapper, opts IR // Extract the entities to be mapped into structs and enumerations in the output // Go code. Extract the schematree from the modules provided such that it can be // used to reference entities within the tree. - mdef, errs := mappedDefinitions(yangFiles, includePaths, &GeneratorConfig{ - ParseOptions: opts.ParseOptions, - TransformationOptions: opts.TransformationOptions, - }) + mdef, errs := mappedDefinitions(yangFiles, includePaths, opts) if errs != nil { return nil, errs } - enumSet, genEnums, errs := findEnumSet(mdef.enumEntries, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.ParseOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.AppendEnumSuffixForSimpleUnionEnums, opts.TransformationOptions.EnumOrgPrefixesToTrim) + enumSet, genEnums, errs := findEnumSet(mdef.enumEntries, opts.TransformationOptions.CompressBehaviour.CompressEnabled(), !opts.TransformationOptions.EnumerationsUseUnderscores, opts.TransformationOptions.SkipEnumDeduplication, opts.TransformationOptions.ShortenEnumLeafNames, opts.TransformationOptions.UseDefiningModuleForTypedefEnumNames, opts.AppendEnumSuffixForSimpleUnionEnums, opts.TransformationOptions.EnumOrgPrefixesToTrim) if errs != nil { return nil, errs } diff --git a/ygen/genstate_test.go b/ygen/genstate_test.go index bfb0fbe3b..8f0f1a75d 100644 --- a/ygen/genstate_test.go +++ b/ygen/genstate_test.go @@ -1396,11 +1396,11 @@ func TestBuildDirectoryDefinitions(t *testing.T) { case golang: got, errs = buildDirectoryDefinitions(gogen, structs, IROptions{ ParseOptions: ParseOpts{ - SkipEnumDeduplication: false, }, TransformationOptions: TransformationOpts{ CompressBehaviour: c.compressBehaviour, GenerateFakeRoot: false, + SkipEnumDeduplication: false, ShortenEnumLeafNames: true, UseDefiningModuleForTypedefEnumNames: true, EnumOrgPrefixesToTrim: nil, @@ -1412,11 +1412,11 @@ func TestBuildDirectoryDefinitions(t *testing.T) { case protobuf: got, errs = buildDirectoryDefinitions(protogen, structs, IROptions{ ParseOptions: ParseOpts{ - SkipEnumDeduplication: false, }, TransformationOptions: TransformationOpts{ CompressBehaviour: c.compressBehaviour, GenerateFakeRoot: false, + SkipEnumDeduplication: false, ShortenEnumLeafNames: true, UseDefiningModuleForTypedefEnumNames: true, EnumOrgPrefixesToTrim: nil, @@ -1505,17 +1505,6 @@ func enumMapFromEntries(entries []*yang.Entry) map[string]*yang.Entry { return enumMap } -// enumMapFromEntries recursively finds enumerated values from a slice of -// resolveTypeArgs and returns an enumMap. The input enumMap is intended for -// findEnumSet. -func enumMapFromArgs(args []resolveTypeArgs) map[string]*yang.Entry { - enumMap := map[string]*yang.Entry{} - for _, a := range args { - addEnumsToEnumMap(a.contextEntry, enumMap) - } - return enumMap -} - // addEnumsToEnumMap recursively finds enumerated values and adds them to the // input enumMap. The input enumMap is intended for findEnumSet, so that tests // that need generated enumerated names have an easy time generating them, and @@ -2168,12 +2157,11 @@ func TestBuildListKey(t *testing.T) { } got, err := buildListKey(tt.in, s, IROptions{ - ParseOptions: ParseOpts{ - SkipEnumDeduplication: tt.inSkipEnumDedup, - }, + ParseOptions: ParseOpts{}, TransformationOptions: TransformationOpts{ CompressBehaviour: compressBehaviour, GenerateFakeRoot: true, + SkipEnumDeduplication: tt.inSkipEnumDedup, ShortenEnumLeafNames: true, UseDefiningModuleForTypedefEnumNames: true, EnumOrgPrefixesToTrim: nil, diff --git a/ygen/helpers.go b/ygen/helpers.go index d38be1799..490272a15 100644 --- a/ygen/helpers.go +++ b/ygen/helpers.go @@ -14,25 +14,7 @@ package ygen -// addNewKeys appends entries from the newKeys string slice to the -// existing map if the entry is not an existing key. The existing -// map is modified in place. -func addNewKeys(existing map[string]interface{}, newKeys []string) { - for _, n := range newKeys { - if _, ok := existing[n]; !ok { - existing[n] = true - } - } -} - -// stringKeys returns the keys of the supplied map as a slice of strings. -func stringKeys(m map[string]interface{}) []string { - var ss []string - for k := range m { - ss = append(ss, k) - } - return ss -} +import "github.com/openconfig/goyang/pkg/yang" // resolveRootName resolves the name of the fakeroot by taking configuration // and the default values, along with a boolean indicating whether the fake @@ -49,3 +31,19 @@ func resolveRootName(name, defName string, generateRoot bool) string { return name } + +// resolveTypeArgs is a structure used as an input argument to the yangTypeToGoType +// function which allows extra context to be handed on. This provides the ability +// to use not only the YangType but also the yang.Entry that the type was part of +// to resolve the possible type name. +type resolveTypeArgs struct { + // yangType is a pointer to the yang.YangType that is to be mapped. + yangType *yang.YangType + // contextEntry is an optional yang.Entry which is supplied where a + // type requires knowledge of the leaf that it is used within to be + // mapped. For example, where a leaf is defined to have a type of a + // user-defined type (typedef) that in turn has enumerated values - the + // context of the yang.Entry is required such that the leaf's context + // can be established. + contextEntry *yang.Entry +} diff --git a/ypathgen/pathgen.go b/ypathgen/pathgen.go index e395072f1..6c3047e73 100644 --- a/ypathgen/pathgen.go +++ b/ypathgen/pathgen.go @@ -249,14 +249,14 @@ func (cg *GenConfig) GeneratePathCode(yangFiles, includePaths []string) (map[str opts := ygen.IROptions{ ParseOptions: ygen.ParseOpts{ - YANGParseOptions: cg.YANGParseOptions, - ExcludeModules: cg.ExcludeModules, - SkipEnumDeduplication: cg.SkipEnumDeduplication, + YANGParseOptions: cg.YANGParseOptions, + ExcludeModules: cg.ExcludeModules, }, TransformationOptions: ygen.TransformationOpts{ CompressBehaviour: compressBehaviour, GenerateFakeRoot: true, FakeRootName: cg.FakeRootName, + SkipEnumDeduplication: cg.SkipEnumDeduplication, ShortenEnumLeafNames: cg.ShortenEnumLeafNames, EnumOrgPrefixesToTrim: cg.EnumOrgPrefixesToTrim, UseDefiningModuleForTypedefEnumNames: cg.UseDefiningModuleForTypedefEnumNames,