Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor how path structs are split. #719

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Comment on lines +406 to +412
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(optional): Move line 410 to just before line 406, change if condition to if i != 0 && (i%structsPerFile == 0 || i == structN-1) {, and you can remove the corner case after the for loop (delete line 412)

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