Skip to content

Commit

Permalink
feat: support for diffing presence containers
Browse files Browse the repository at this point in the history
Co-authored-by: Terje Lafton <terje@lafton.io>
  • Loading branch information
JonasKs and TerjeLafton committed Oct 2, 2024
1 parent 100bd44 commit 519ff68
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 4 deletions.
14 changes: 14 additions & 0 deletions gogen/gogen.go
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,12 @@ func (t *{{ .ParentReceiver }}) To_{{ .Name }}(i interface{}) ({{ .Name }}, erro
{{- end -}}
]", i, i)
}
`)
// presenceMethodTemplate provides a template to output a method
// indicating this is a presence container
presenceMethodTemplate = mustMakeTemplate("presenceMethodTemplate", `
// IsPresence returns nothing, but indicates that the receiver is a presence container.
func (t *{{ .StructName }}) IsPresence() {}
`)
)

Expand Down Expand Up @@ -1403,6 +1409,14 @@ func writeGoStruct(targetStruct *ygen.ParsedDirectory, goStructElements map[stri
errs = append(errs, err)
}

if goOpts.AddYangPresence {
if targetStruct.PresenceContainer {
if err := presenceMethodTemplate.Execute(&methodBuf, structDef); err != nil {
errs = append(errs, err)
}
}
}

return GoStructCodeSnippet{
StructName: structDef.StructName,
StructDef: structBuf.String(),
Expand Down
10 changes: 10 additions & 0 deletions ygen/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,16 @@ func getOrderedDirDetails(langMapper LangMapper, directory map[string]*Directory
}
default:
pd.Type = Container
if len(dir.Entry.Extra["presence"]) > 0 {
if v := dir.Entry.Extra["presence"][0].(*yang.Value); v != nil {
pd.PresenceContainer = true
} else {
return nil, fmt.Errorf(
"unable to retrieve presence statement, expected non-nil *yang.Value, got %v",
dir.Entry.Extra["presence"][0],
)
}
}
}

for i, entry := 0, dir.Entry; ; i++ {
Expand Down
2 changes: 2 additions & 0 deletions ygen/ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,8 @@ type ParsedDirectory struct {
//
// https://github.com/openconfig/public/blob/master/release/models/openconfig-extensions.yang#L154
CompressedTelemetryAtomic bool
// PresenceContainer indicates that this container is a YANG presence container
PresenceContainer bool
}

// OrderedFieldNames returns the YANG name of all fields belonging to the
Expand Down
20 changes: 16 additions & 4 deletions ygot/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ func findSetLeaves(s GoStruct, orderedMapAsLeaf bool, opts ...DiffOpt) (map[*pat
return
}

isYangPresence := util.IsYangPresence(ni.StructField)

var sp [][]string
if pathOpt != nil && pathOpt.PreferShadowPath {
// Try the shadow-path tag first to see if it exists.
Expand Down Expand Up @@ -314,7 +316,9 @@ func findSetLeaves(s GoStruct, orderedMapAsLeaf bool, opts ...DiffOpt) (map[*pat
// treating it as a leaf (since it is assumed to be
// telemetry-atomic in order to preserve ordering of entries).
if (!isOrderedMap || !orderedMapAsLeaf) && util.IsValueStructPtr(ni.FieldValue) {
return
if !isYangPresence {
return
}
}
if isOrderedMap && orderedMap.Len() == 0 {
return
Expand All @@ -334,9 +338,17 @@ func findSetLeaves(s GoStruct, orderedMapAsLeaf bool, opts ...DiffOpt) (map[*pat
}
}

outs := out.(map[*pathSpec]interface{})
outs[vp] = ival

// If the current field is tagged as a presence container,
// we set it's value to `nil` instead of returning earlier.
// This is because empty presence containers has a meaning,
// unlike a normal container.
if isYangPresence {
outs := out.(map[*pathSpec]interface{})
outs[vp] = nil
} else {
outs := out.(map[*pathSpec]interface{})
outs[vp] = ival
}
if isOrderedMap && orderedMapAsLeaf {
// We treat the ordered map as a leaf, so don't
// traverse any descendant elements.
Expand Down
5 changes: 5 additions & 0 deletions ygot/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,11 @@ func marshalStructOrOrderedList(s any, enc gnmipb.Encoding, cfg *RFC7951JSONConf
if reflect.ValueOf(s).IsNil() {
return nil, nil
}
// A presence container might not be empty, but we should still
// treat it as such
if _, ok := s.(PresenceContainer); ok {
return nil, nil
}

var (
j any
Expand Down
8 changes: 8 additions & 0 deletions ygot/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import (
"reflect"
)

// PresenceContainer is an interface which can be implemented by Go structs that are
// generated to represent a YANG presence container.
type PresenceContainer interface {
// IsPresence is a marker method that indicates that the struct
// implements the PresenceContainer interface.
IsPresence()
}

// GoStruct is an interface which can be implemented by Go structs that are
// generated to represent a YANG container or list member. It simply allows
// handling code to ensure that it is interacting with a struct that will meet
Expand Down

0 comments on commit 519ff68

Please sign in to comment.