Skip to content

Commit

Permalink
Refactor how path structs are split.
Browse files Browse the repository at this point in the history
 * (M) ypathgen/pathgen.go
 * (M) ypathgen/pathgen_test.go
   - Change splits to include N structs per file based on iterating
     through them rather than precalculating.
   - Avoid creating files that are just the package header.
 * (A) ypathgen/testdata/splitstructs/*
   - Add new test case files.
   - Remove files that contained only the header.
  • Loading branch information
robshakir committed Aug 5, 2022
1 parent f9326f6 commit d33236c
Show file tree
Hide file tree
Showing 7 changed files with 440 additions and 73 deletions.
72 changes: 36 additions & 36 deletions ypathgen/pathgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,12 @@ func (goLangMapper) PopulateFieldFlags(nd ygen.NodeDetails, field *yang.Entry) m
// a map of package names to GeneratedPathCode structs. Each struct contains
// all the generated code of that package needed support the path-creation API.
// The important components of the generated code are listed below:
// 1. Struct definitions for each container, list, or leaf schema node,
// as well as the fakeroot.
// 2. Next-level methods for the fakeroot and each non-leaf schema node,
// which instantiate and return the next-level structs corresponding to
// its child schema nodes.
// 1. Struct definitions for each container, list, or leaf schema node,
// as well as the fakeroot.
// 2. Next-level methods for the fakeroot and each non-leaf schema node,
// which instantiate and return the next-level structs corresponding to
// its child schema nodes.
//
// With these components, the generated API is able to support absolute path
// creation of any node of the input schema.
// Also returned is the NodeDataMap of the schema, i.e. information about each
Expand Down Expand Up @@ -392,33 +393,31 @@ func (genCode GeneratedPathCode) String() string {
// output files can be roughly controlled.
func (genCode GeneratedPathCode) SplitFiles(fileN int) ([]string, error) {
structN := len(genCode.Structs)
if fileN < 1 || fileN > structN {
if fileN < 1 {
return nil, fmt.Errorf("requested %d files, but must be between 1 and %d (number of structs)", fileN, structN)
}

files := make([]string, 0, fileN)
files := []string{}
structsPerFile := int(math.Ceil(float64(structN) / float64(fileN)))
// Empty files could appear with certain structN/fileN combinations due
// to the ceiling numbers being used for structsPerFile.
// e.g. 4/3 gives two files of two structs.
// This is a little more complex, but spreads out the structs more evenly.
// If we instead use the floor number, and put all remainder structs in
// the last file, we might double the last file's number of structs if we get unlucky.
// e.g. 99/10 assigns 18 structs to the last file.
emptyFiles := fileN - int(math.Ceil(float64(structN)/float64(structsPerFile)))
var gotCode strings.Builder
gotCode.WriteString(genCode.CommonHeader)

fileContent := [][]string{}
structs := []string{}
for i, gotStruct := range genCode.Structs {
gotCode.WriteString(gotStruct.String())
// The last file contains the remainder of the structs.
if i == structN-1 || (i+1)%structsPerFile == 0 {
files = append(files, gotCode.String())
gotCode.Reset()
gotCode.WriteString(genCode.CommonHeader)
if i != 0 && i%structsPerFile == 0 {
fileContent = append(fileContent, structs)
structs = []string{}
}
structs = append(structs, gotStruct.String())
}
for i := 0; i != emptyFiles; i++ {
files = append(files, genCode.CommonHeader)
fileContent = append(fileContent, structs)
var gotCode strings.Builder
for _, f := range fileContent {
gotCode.WriteString(genCode.CommonHeader)
for _, s := range f {
gotCode.WriteString(s)
}
files = append(files, gotCode.String())
gotCode.Reset()
}

return files, nil
Expand Down Expand Up @@ -1258,18 +1257,19 @@ type keyParam struct {
// list of each parameter's types as a comment string.
// It outputs the parameters in the same order as in the given keyNames.
// e.g.
// in: &map[string]*ygen.ListKey{
// "fluorine": &ygen.ListKey{
// Name: "Fluorine", LangType: &ygen.MappedType{NativeType: "string"}
// },
// "iodine-liquid": &ygen.ListKey{
// Name: "IodineLiquid", LangType: &ygen.MappedType{NativeType: "A_Union", UnionTypes: {"Binary": 0, "uint64": 1}}
// },
// }
// KeyNames: []string{"fluorine", "iodine-liquid"},
//
// {name, varName, typeName} out: [{"fluroine", "Fluorine", "string"}, {"iodine-liquid", "IodineLiquid", "oc.A_Union"}]
// docstring out: ["string", "[oc.Binary, oc.UnionUint64]"]
// in: &map[string]*ygen.ListKey{
// "fluorine": &ygen.ListKey{
// Name: "Fluorine", LangType: &ygen.MappedType{NativeType: "string"}
// },
// "iodine-liquid": &ygen.ListKey{
// Name: "IodineLiquid", LangType: &ygen.MappedType{NativeType: "A_Union", UnionTypes: {"Binary": 0, "uint64": 1}}
// },
// }
// KeyNames: []string{"fluorine", "iodine-liquid"},
//
// {name, varName, typeName} out: [{"fluroine", "Fluorine", "string"}, {"iodine-liquid", "IodineLiquid", "oc.A_Union"}]
// docstring out: ["string", "[oc.Binary, oc.UnionUint64]"]
func makeKeyParams(keys map[string]*ygen.ListKey, keyNames []string, schemaStructPkgAccessor string) ([]keyParam, error) {
if len(keys) == 0 {
return nil, fmt.Errorf("makeKeyParams: invalid list - has no key; cannot process param list string")
Expand Down
47 changes: 25 additions & 22 deletions ypathgen/pathgen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1013,9 +1013,9 @@ func TestGeneratePathCodeSplitFiles(t *testing.T) {
}{{
name: "fileNumber is higher than total number of structs",
inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")},
inFileNumber: 5,
inSchemaStructPkgPath: "",
wantErr: true,
inFileNumber: 100,
inSchemaStructPkgPath: "github.com/openconfig/ygot/ypathgen/testdata/exampleoc",
wantStructsCodeFiles: []string{filepath.Join(TestRoot, "testdata/splitstructs/openconfig-simple.higher-0.path-txt"), filepath.Join(TestRoot, "testdata/splitstructs/openconfig-simple.higher-1.path-txt"), filepath.Join(TestRoot, "testdata/splitstructs/openconfig-simple.higher-2.path-txt"), filepath.Join(TestRoot, "testdata/splitstructs/openconfig-simple.higher-3.path-txt")},
}, {
name: "fileNumber is exactly the total number of structs",
inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")},
Expand All @@ -1027,7 +1027,7 @@ func TestGeneratePathCodeSplitFiles(t *testing.T) {
inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")},
inFileNumber: 3,
inSchemaStructPkgPath: "",
wantStructsCodeFiles: []string{filepath.Join(TestRoot, "testdata/splitstructs/openconfig-simple-30.path-txt"), filepath.Join(TestRoot, "testdata/splitstructs/openconfig-simple-31.path-txt"), filepath.Join(TestRoot, "testdata/splitstructs/openconfig-simple-32.path-txt")},
wantStructsCodeFiles: []string{filepath.Join(TestRoot, "testdata/splitstructs/openconfig-simple-30.path-txt"), filepath.Join(TestRoot, "testdata/splitstructs/openconfig-simple-31.path-txt")},
}, {
name: "fileNumber is half the total number of structs",
inFiles: []string{filepath.Join(datapath, "openconfig-simple.yang")},
Expand Down Expand Up @@ -1940,27 +1940,30 @@ func TestGetNodeDataMap(t *testing.T) {

// trimDocComments removes doc comments from the input code snippet string.
// Example:
// // foo does bar
// func foo() {
// // baz is need to do boo.
// baz()
// }
//
// // foo2 does bar2
// func foo2() {
// // baz2 is need to do boo2.
// baz2()
// }
// // foo does bar
// func foo() {
// // baz is need to do boo.
// baz()
// }
//
// // foo2 does bar2
// func foo2() {
// // baz2 is need to do boo2.
// baz2()
// }
//
// After:
// func foo() {
// // baz is need to do boo.
// baz()
// }
//
// func foo2() {
// // baz2 is need to do boo2.
// baz2()
// }
// func foo() {
// // baz is need to do boo.
// baz()
// }
//
// func foo2() {
// // baz2 is need to do boo2.
// baz2()
// }
func trimDocComments(snippet string) string {
var b strings.Builder
for i, line := range strings.Split(snippet, "\n") {
Expand Down
15 changes: 0 additions & 15 deletions ypathgen/testdata/splitstructs/openconfig-simple-32.path-txt

This file was deleted.

59 changes: 59 additions & 0 deletions ypathgen/testdata/splitstructs/openconfig-simple.higher-0.path-txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
Package ocstructs is a generated package which contains definitions
of structs which generate gNMI paths for a YANG schema. The generated paths are
based on a compressed form of the schema.

This package was generated by pathgen-tests
using the following YANG input files:
- ../testdata/modules/openconfig-simple.yang
Imported modules were sourced from:
*/
package ocstructs

import (
oc "github.com/openconfig/ygot/ypathgen/testdata/exampleoc"
"github.com/openconfig/ygot/ygot"
)

// Device represents the /device YANG schema element.
type Device struct {
*ygot.DeviceRootBase
}

// DeviceRoot returns a new path object from which YANG paths can be constructed.
func DeviceRoot(id string) *Device {
return &Device{ygot.NewDeviceRootBase(id)}
}

// Parent (container): I am a parent container
// that has 4 children.
// ----------------------------------------
// Defining module: "openconfig-simple"
// Instantiating module: "openconfig-simple"
// Path from parent: "parent"
// Path from root: "/parent"
func (n *Device) Parent() *Parent {
return &Parent{
NodePath: ygot.NewNodePath(
[]string{"parent"},
map[string]interface{}{},
n,
),
}
}

// RemoteContainer (container):
// ----------------------------------------
// Defining module: "openconfig-remote"
// Instantiating module: "openconfig-simple"
// Path from parent: "remote-container"
// Path from root: "/remote-container"
func (n *Device) RemoteContainer() *RemoteContainer {
return &RemoteContainer{
NodePath: ygot.NewNodePath(
[]string{"remote-container"},
map[string]interface{}{},
n,
),
}
}
58 changes: 58 additions & 0 deletions ypathgen/testdata/splitstructs/openconfig-simple.higher-1.path-txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
Package ocstructs is a generated package which contains definitions
of structs which generate gNMI paths for a YANG schema. The generated paths are
based on a compressed form of the schema.

This package was generated by pathgen-tests
using the following YANG input files:
- ../testdata/modules/openconfig-simple.yang
Imported modules were sourced from:
*/
package ocstructs

import (
oc "github.com/openconfig/ygot/ypathgen/testdata/exampleoc"
"github.com/openconfig/ygot/ygot"
)

// Parent represents the /openconfig-simple/parent YANG schema element.
type Parent struct {
*ygot.NodePath
}

// ParentAny represents the wildcard version of the /openconfig-simple/parent YANG schema element.
type ParentAny struct {
*ygot.NodePath
}

// Child (container):
// ----------------------------------------
// Defining module: "openconfig-simple"
// Instantiating module: "openconfig-simple"
// Path from parent: "child"
// Path from root: "/parent/child"
func (n *Parent) Child() *Parent_Child {
return &Parent_Child{
NodePath: ygot.NewNodePath(
[]string{"child"},
map[string]interface{}{},
n,
),
}
}

// Child (container):
// ----------------------------------------
// Defining module: "openconfig-simple"
// Instantiating module: "openconfig-simple"
// Path from parent: "child"
// Path from root: "/parent/child"
func (n *ParentAny) Child() *Parent_ChildAny {
return &Parent_ChildAny{
NodePath: ygot.NewNodePath(
[]string{"child"},
map[string]interface{}{},
n,
),
}
}
Loading

0 comments on commit d33236c

Please sign in to comment.