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

initial commit for leaf setters #281

Open
wants to merge 5 commits 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
2 changes: 2 additions & 0 deletions generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ var (
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.")
generateDelete = flag.Bool("generate_delete", false, "If set to true, delete methods are generated for YANG lists (Go maps) within the Go code.")
generateLeafGetters = flag.Bool("generate_leaf_getters", false, "If set to true, getters for YANG leaves are generated within the Go code. Caution should be exercised when using leaf getters, since values that are explicitly set to the Go default/zero value are not distinguishable from those that are unset when retrieved via the GetXXX method.")
generateLeafSetters = flag.Bool("generate_leaf_setters", false, "If set to true, setters for YANG leaves are generated within the Go code.")
includeModelData = flag.Bool("include_model_data", false, "If set to true, a slice of gNMI ModelData messages are included in the generated Go code containing the details of the input schemas from which the code was generated.")
)

Expand Down Expand Up @@ -251,6 +252,7 @@ func main() {
GenerateDeleteMethod: *generateDelete,
GenerateAppendMethod: *generateAppend,
GenerateLeafGetters: *generateLeafGetters,
GenerateLeafSetters: *generateLeafSetters,
IncludeModelData: *includeModelData,
},
ExcludeState: *excludeState,
Expand Down
3 changes: 3 additions & 0 deletions ygen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ type GoOpts struct {
// 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
// GenerateLeafSetters specifies whether Set* methods should be created for
// leaf fields of a struct.
GenerateLeafSetters bool
// GNMIProtoPath specifies the path to the generated gNMI protobuf, which
// is used to store the catalogue entries for generated modules.
GNMIProtoPath string
Expand Down
71 changes: 71 additions & 0 deletions ygen/gogen.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,26 @@ type generatedLeafGetter struct {
Receiver string
}

// generatedLeafSetter is used to represent the parameters required to generate a
// setter for a leaf within the generated Go code.
type generatedLeafSetter struct {
// Name is the name of the field. It is used as a suffix to Get to generate
// the getter.
Name string
// Type is the type of the field, returned by the generated method.
Type string
// Zero is the value that should be returned if the field is set to nil.
Zero string
// Default is the default value specified in the YANG schema for the type
// or leaf.
Default *string
// IsPtr stores whether the value is a pointer, such that it can be checked
// against nil, or against the zero value.
IsPtr bool
// Receiver is the name of the receiver for the getter method.
Receiver string
}

var (
// goCommonHeaderTemplate is populated and output at the top of the generated code package
goCommonHeaderTemplate = `
Expand Down Expand Up @@ -730,6 +750,19 @@ func (t *{{ .Receiver }}) Get{{ .Name }}() {{ .Type }} {
}
return {{ if .IsPtr -}} * {{- end -}} t.{{ .Name }}
}
`

// goLeafSetterTemplate defines a template for a function that, for a
// particular leaf, generates a setter method.
goLeafSetterTemplate = `
// Set{{ .Name }} sets the value of the leaf {{ .Name }} from the {{ .Receiver }}
// struct.
func (t *{{ .Receiver }}) Set{{ .Name }}(val string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure that you can just take a string as an argument, there are different types of fields that we have within the generated structs. Given that this is the case, we need to handle different inputs here. Determining the type that is needed should likely be from the input type that is in the generatedLeafSetter that you created. There are some other details that you'll need though, such as the name of the generated union, such that you can call To_XXX for these types.

Copy link
Author

Choose a reason for hiding this comment

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

@robshakir
Thanks for your comments. That is a good point. I will start working on it but before that what types you would suggest to support? I will let you know if I have more questions.

Copy link
Author

@adibrastegarnia adibrastegarnia Apr 14, 2019

Choose a reason for hiding this comment

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

I think I figured that out.

Copy link
Author

@adibrastegarnia adibrastegarnia Apr 14, 2019

Choose a reason for hiding this comment

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

@robshakir

May I ask you how do you generate the expected output for the testcases? It says the following but it is not clear for me:
This package was generated by codegen-tests
using the following YANG input files: .....

Copy link
Author

@adibrastegarnia adibrastegarnia Apr 15, 2019

Choose a reason for hiding this comment

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

@robshakir

Please take a look at this code and I think it handles any type. Please let me know what you think. I also have a test case ready but I am not sure how do you generate the expected output in advance.

// goLeafSetterTemplate defines a template for a function that, for a
	// particular leaf, generates a setter method.
	goLeafSetterTemplate = `
// Set{{ .Name }} sets the value of the leaf {{ .Name }} from the {{ .Receiver }}
// struct. 
func (t *{{ .Receiver }}) Set{{ .Name }}(val interface{}) {
	if t == nil || val == nil {
		return
	}
	t.{{ .Name }} = ygot.ToPtr(val).(*{{ .Type }})
}

`

if t == nil || val == "" {
return
}
t.{{ .Name }} = ygot.String(val)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as above re: whether you can use string.

This likely highlights a test coverage gap -- we should add tests to schema_tests which give an integration test against a YANG model that has >1 different field type.

}
`

// goDeleteListTemplate defines a template for a function that, for a
Expand Down Expand Up @@ -1031,6 +1064,7 @@ func (t *{{ .ParentReceiver }}) To_{{ .Name }}(i interface{}) ({{ .Name }}, erro
"getList": makeTemplate("getList", goListGetterTemplate),
"getContainer": makeTemplate("getContainer", goContainerGetterTemplate),
"getLeaf": makeTemplate("getLeaf", goLeafGetterTemplate),
"setLeaf": makeTemplate("setLeaf", goLeafSetterTemplate),
}

// templateHelperFunctions specifies a set of functions that are supplied as
Expand Down Expand Up @@ -1190,6 +1224,11 @@ func writeGoStruct(targetStruct *yangDirectory, goStructElements map[string]*yan
// is set to true.
var associatedLeafGetters []*generatedLeafGetter

// associatedLeafSetters is a slice of structs which define the set of leaf setters
// to generated for the struct. It is only populated if the GenerateLeafSetters option
// is set to true.
var associatedLeafSetters []*generatedLeafSetter

// Sort the list of fields into alphabetical order to ensure determinstic output of code,
// and minimise diffs.
var fieldNames []string
Expand Down Expand Up @@ -1388,6 +1427,20 @@ func writeGoStruct(targetStruct *yangDirectory, goStructElements map[string]*yan
})
}

if goOpts.GenerateLeafSetters {
// If we are generating leaf setters, then append the relevant information
// to the associatedLeafSetters slice to be generated along with other
// associated methods.
associatedLeafSetters = append(associatedLeafSetters, &generatedLeafSetter{
Name: fieldName,
Type: fType,
Zero: zeroValue,
IsPtr: scalarField,
Receiver: targetStruct.name,
Default: defaultValue,
})
}

fieldDef = &goStructField{
Name: fieldName,
Type: fType,
Expand Down Expand Up @@ -1518,6 +1571,12 @@ func writeGoStruct(targetStruct *yangDirectory, goStructElements map[string]*yan
}
}

if goOpts.GenerateLeafSetters {
if err := generateLeafSetters(&methodBuf, associatedLeafSetters); err != nil {
errs = append(errs, err)
}
}

if err := generateGetListKey(&methodBuf, targetStruct, definedNameMap); err != nil {
errs = append(errs, err)
}
Expand Down Expand Up @@ -1653,6 +1712,18 @@ func generateLeafGetters(buf *bytes.Buffer, leaves []*generatedLeafGetter) error
return errs.Err()
}

// generateLeafSetters generates SetXXX methods for the leaf fields described by
// the supplied slice of generatedLeafSetter structs.
func generateLeafSetters(buf *bytes.Buffer, leaves []*generatedLeafSetter) error {
var errs errlist.List
for _, l := range leaves {
if err := goTemplates["setLeaf"].Execute(buf, l); err != nil {
errs.Add(err)
}
}
return errs.Err()
}

// generateGetOrCreateList generates a getter function similar to that created
// by the generateGetOrCreateStruct function for maps within the generated Go
// code (which represent YANG lists). It handles both simple and composite key
Expand Down