@@ -22,6 +22,7 @@ import (
2222 "fmt"
2323 "sort"
2424
25+ "github.com/google/go-cmp/cmp"
2526 "github.com/openconfig/goyang/pkg/yang"
2627 "github.com/openconfig/ygot/genutil"
2728 "github.com/openconfig/ygot/util"
@@ -169,3 +170,68 @@ func findSchemaPath(parent *Directory, fieldName string, shadowSchemaPaths, abso
169170 }
170171 return fieldSlicePath [len (parent .Path )- 1 :], nil
171172}
173+
174+ // findMapPaths takes an input field name for a parent Directory and calculates the set of schemapaths that it represents.
175+ // If absolutePaths is set, the paths are absolute otherwise they are relative to the parent. If
176+ // the input entry is a key to a list, and is of type leafref, then the corresponding target leaf's
177+ // path is also returned. If shadowSchemaPaths is set, then the path of the
178+ // field deprioritized via compression is returned instead of the prioritized paths.
179+ // The first returned path is the path of the direct child, with the shadow
180+ // child's path afterwards, and the key leafref, if any, last.
181+ func findMapPaths (parent * Directory , fieldName string , compressPaths , shadowSchemaPaths , absolutePaths bool ) ([][]string , error ) {
182+ childPath , err := findSchemaPath (parent , fieldName , shadowSchemaPaths , absolutePaths )
183+ if err != nil {
184+ return nil , err
185+ }
186+ var mapPaths [][]string
187+ if childPath != nil {
188+ mapPaths = append (mapPaths , childPath )
189+ }
190+ // Only for compressed data schema paths for list fields do we have the
191+ // possibility for a direct leafref path as a second path for the field.
192+ if ! compressPaths || parent .ListAttr == nil {
193+ return mapPaths , nil
194+ }
195+
196+ field , ok := parent .Fields [fieldName ]
197+ if ! ok {
198+ return nil , fmt .Errorf ("field name %s does not exist in Directory %s" , fieldName , parent .Path )
199+ }
200+ fieldSlicePath := util .SchemaPathNoChoiceCase (field )
201+
202+ // Handle specific issue of compressed path schemas, where a key of the
203+ // parent list is a leafref to this leaf.
204+ for _ , k := range parent .ListAttr .KeyElems {
205+ // If the key element has the same path as this element, and the
206+ // corresponding element that is within the parent's container is of
207+ // type leafref, then within an OpenConfig schema this means that
208+ // the key leaf was a pointer to this leaf. To this end, we set
209+ // isKey to true so that the struct field can be mapped to the
210+ // leafref leaf within the schema as well as the target of the
211+ // leafref.
212+ if k .Parent == nil || k .Parent .Parent == nil || k .Parent .Parent .Dir [k .Name ] == nil || k .Parent .Parent .Dir [k .Name ].Type == nil {
213+ return nil , fmt .Errorf ("invalid compressed schema, could not find the key %s or the grandparent of %s" , k .Name , k .Path ())
214+ }
215+
216+ // If a key of the list is a leafref that points to the field,
217+ // then add this as an alternative path.
218+ // Note: if k is a leafref, buildListKey() would have already
219+ // resolved it the field that the leafref points to. So, we
220+ // compare their absolute paths for equality.
221+ if k .Parent .Parent .Dir [k .Name ].Type .Kind == yang .Yleafref && cmp .Equal (util .SchemaPathNoChoiceCase (k ), fieldSlicePath ) {
222+ // The path of the key element is simply the name of the leaf under the
223+ // list, since the YANG specification enforces that keys are direct
224+ // children of the list.
225+ keyPath := []string {fieldSlicePath [len (fieldSlicePath )- 1 ]}
226+ if absolutePaths {
227+ // If absolute paths are required, then the 'config' or 'state' container needs to be omitted from
228+ // the complete path for the secondary mapping.
229+ keyPath = append ([]string {"" }, fieldSlicePath [1 :len (fieldSlicePath )- 2 ]... )
230+ keyPath = append (keyPath , fieldSlicePath [len (fieldSlicePath )- 1 ])
231+ }
232+ mapPaths = append (mapPaths , keyPath )
233+ break
234+ }
235+ }
236+ return mapPaths , nil
237+ }
0 commit comments