@@ -59,6 +59,10 @@ type retrieveNodeArgs struct {
5959 // values are skipped (non-existing GoStructs are still initialized),
6060 // and any value retrieved is always nil.
6161 shadowPath bool
62+ // reverseShadowPath reverses the meaning of the "path" and
63+ // "shadow-path" tags when both are present while processing a
64+ // GoStruct.
65+ reverseShadowPath bool
6266}
6367
6468// retrieveNode is an internal function that retrieves the node specified by
@@ -134,11 +138,6 @@ func retrieveNodeContainer(schema *yang.Entry, root interface{}, path *gpb.Path,
134138 }
135139 }
136140
137- schPaths , err := util .SchemaPaths (ft )
138- if err != nil {
139- return nil , status .Errorf (codes .Unknown , "failed to get schema paths for %T, field %s: %s" , root , ft .Name , err )
140- }
141-
142141 checkPath := func (p []string , args retrieveNodeArgs ) ([]* TreeNode , error ) {
143142 to := len (p )
144143 if util .IsTypeMap (ft .Type ) {
@@ -195,18 +194,52 @@ func retrieveNodeContainer(schema *yang.Entry, root interface{}, path *gpb.Path,
195194 return retrieveNode (cschema , fv .Interface (), util .TrimGNMIPathPrefix (path , p [0 :to ]), np , args )
196195 }
197196
198- for _ , p := range schPaths {
199- if ! util .PathMatchesPrefix (path , p ) {
200- continue
197+ // Continue traversal on the first-encountered annotated
198+ // GoStruct path that forms a prefix of the input path.
199+ //
200+ // Note that we first look through the non-shadow path, and if
201+ // no matches are found, we then look through the shadow path
202+ // to find matches. If the input path matches a shadow path,
203+ // then the traversal continues, although the final operation
204+ // is marked as a no-op.
205+ //
206+ // If the user has opted to reverse the "shadow-path" and
207+ // "path" tags, then the order of the look-ups is reversed.
208+ args := args
209+ if args .reverseShadowPath {
210+ // Look through shadow paths first instead.
211+ schPaths := util .ShadowSchemaPaths (ft )
212+ for _ , p := range schPaths {
213+ if util .PathMatchesPrefix (path , p ) {
214+ return checkPath (p , args )
215+ }
216+ }
217+
218+ if len (schPaths ) != 0 {
219+ // Only if there exists shadow paths do we
220+ // treat the non-shadow paths as no-ops.
221+ // Otherwise, there is no reversal to do.
222+ args .shadowPath = true
201223 }
202- return checkPath (p , args )
203224 }
204- for _ , p := range util .ShadowSchemaPaths (ft ) {
205- if ! util .PathMatchesPrefix (path , p ) {
206- continue
225+ schPaths , err := util .SchemaPaths (ft )
226+ if err != nil {
227+ return nil , status .Errorf (codes .Unknown , "failed to get schema paths for %T, field %s: %s" , root , ft .Name , err )
228+ }
229+ for _ , p := range schPaths {
230+ if util .PathMatchesPrefix (path , p ) {
231+ return checkPath (p , args )
207232 }
233+ }
234+ if ! args .reverseShadowPath {
235+ // Look through shadow paths last, and mark operations
236+ // as no-ops.
208237 args .shadowPath = true
209- return checkPath (p , args )
238+ for _ , p := range util .ShadowSchemaPaths (ft ) {
239+ if util .PathMatchesPrefix (path , p ) {
240+ return checkPath (p , args )
241+ }
242+ }
210243 }
211244 }
212245
@@ -354,14 +387,23 @@ func retrieveNodeList(schema *yang.Entry, root interface{}, path, traversedPath
354387 return matches , nil
355388}
356389
390+ // GetOrCreateNodeOpt defines an interface that can be used to supply arguments to functions using GetOrCreateNode.
391+ type GetOrCreateNodeOpt interface {
392+ // IsGetOrCreateNodeOpt is a marker method that is used to identify an instance of GetOrCreateNodeOpt.
393+ IsGetOrCreateNodeOpt ()
394+ }
395+
357396// GetOrCreateNode function retrieves the node specified by the supplied path from the root which must have the
358397// schema supplied. It strictly matches keys in the path, in other words doesn't treat partial match as match.
359398// However, if there is no match, a new entry in the map is created. GetOrCreateNode also initializes the nodes
360399// along the path if they are nil.
361400// Function returns the value and schema of the node as well as error.
362401// Note that this function may modify the supplied root even if the function fails.
363- func GetOrCreateNode (schema * yang.Entry , root interface {}, path * gpb.Path ) (interface {}, * yang.Entry , error ) {
364- nodes , err := retrieveNode (schema , root , path , nil , retrieveNodeArgs {modifyRoot : true })
402+ func GetOrCreateNode (schema * yang.Entry , root interface {}, path * gpb.Path , opts ... GetOrCreateNodeOpt ) (interface {}, * yang.Entry , error ) {
403+ nodes , err := retrieveNode (schema , root , path , nil , retrieveNodeArgs {
404+ modifyRoot : true ,
405+ reverseShadowPath : hasGetOrCreateNodeReverseShadowPaths (opts ),
406+ })
365407 if err != nil {
366408 return nil , nil , err
367409 }
@@ -386,9 +428,10 @@ type TreeNode struct {
386428func GetNode (schema * yang.Entry , root interface {}, path * gpb.Path , opts ... GetNodeOpt ) ([]* TreeNode , error ) {
387429 return retrieveNode (schema , root , path , nil , retrieveNodeArgs {
388430 // We never want to modify the input root, so we specify modifyRoot.
389- modifyRoot : false ,
390- partialKeyMatch : hasPartialKeyMatch (opts ),
391- handleWildcards : hasHandleWildcards (opts ),
431+ modifyRoot : false ,
432+ partialKeyMatch : hasPartialKeyMatch (opts ),
433+ handleWildcards : hasHandleWildcards (opts ),
434+ reverseShadowPath : hasGetNodeReverseShadowPaths (opts ),
392435 })
393436}
394437
@@ -455,6 +498,7 @@ func SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface
455498 modifyRoot : hasInitMissingElements (opts ),
456499 val : val ,
457500 tolerateJSONInconsistenciesForVal : hasTolerateJSONInconsistencies (opts ),
501+ reverseShadowPath : hasSetNodeReverseShadowPaths (opts ),
458502 })
459503
460504 if err != nil {
@@ -511,13 +555,90 @@ func hasTolerateJSONInconsistencies(opts []SetNodeOpt) bool {
511555 return false
512556}
513557
558+ // DelNodeOpt defines an interface that can be used to supply arguments to functions using DeleteNode.
559+ type DelNodeOpt interface {
560+ // IsDelNodeOpt is a marker method that is used to identify an instance of DelNodeOpt.
561+ IsDelNodeOpt ()
562+ }
563+
564+ // ReverseShadowPaths signals to reverse the meaning of the "path" and
565+ // "shadow-path" tags when both are present while processing a GoStruct. This
566+ // means paths matching "shadow-path" will be unmarshalled, while paths
567+ // matching "path" will be silently ignored.
568+ type ReverseShadowPaths struct {}
569+
570+ // IsGetOrCreateNodeOpt implements the GetOrCreateNodeOpt interface.
571+ func (* ReverseShadowPaths ) IsGetOrCreateNodeOpt () {}
572+
573+ // IsGetNodeOpt implements the GetNodeOpt interface.
574+ func (* ReverseShadowPaths ) IsGetNodeOpt () {}
575+
576+ // IsSetNodeOpt implements the SetNodeOpt interface.
577+ func (* ReverseShadowPaths ) IsSetNodeOpt () {}
578+
579+ // IsDelNodeOpt implements the DelNodeOpt interface.
580+ func (* ReverseShadowPaths ) IsDelNodeOpt () {}
581+
582+ // hasGetOrCreateNodeReverseShadowPaths determines whether there is an instance
583+ // of ReverseShadowPaths within the supplied GetOrCreateNodeOpt slice. It is
584+ // used to determine whether to reverse the meaning of the "path" and
585+ // "shadow-path" tags when both are present while processing a GoStruct.
586+ func hasGetOrCreateNodeReverseShadowPaths (opts []GetOrCreateNodeOpt ) bool {
587+ for _ , o := range opts {
588+ if _ , ok := o .(* ReverseShadowPaths ); ok {
589+ return true
590+ }
591+ }
592+ return false
593+ }
594+
595+ // hasGetNodeReverseShadowPaths determines whether there is an instance of
596+ // ReverseShadowPaths within the supplied GetNodeOpt slice. It is used to
597+ // determine whether to reverse the meaning of the "path" and "shadow-path"
598+ // tags when both are present while processing a GoStruct.
599+ func hasGetNodeReverseShadowPaths (opts []GetNodeOpt ) bool {
600+ for _ , o := range opts {
601+ if _ , ok := o .(* ReverseShadowPaths ); ok {
602+ return true
603+ }
604+ }
605+ return false
606+ }
607+
608+ // hasSetNodeReverseShadowPaths determines whether there is an instance of
609+ // ReverseShadowPaths within the supplied SetNodeOpt slice. It is used to
610+ // determine whether to reverse the meaning of the "path" and "shadow-path"
611+ // tags when both are present while processing a GoStruct.
612+ func hasSetNodeReverseShadowPaths (opts []SetNodeOpt ) bool {
613+ for _ , o := range opts {
614+ if _ , ok := o .(* ReverseShadowPaths ); ok {
615+ return true
616+ }
617+ }
618+ return false
619+ }
620+
621+ // hasDelNodeReverseShadowPaths determines whether there is an instance of
622+ // ReverseShadowPaths within the supplied DelNodeOpt slice. It is used to
623+ // determine whether to reverse the meaning of the "path" and "shadow-path"
624+ // tags when both are present while processing a GoStruct.
625+ func hasDelNodeReverseShadowPaths (opts []DelNodeOpt ) bool {
626+ for _ , o := range opts {
627+ if _ , ok := o .(* ReverseShadowPaths ); ok {
628+ return true
629+ }
630+ }
631+ return false
632+ }
633+
514634// DeleteNode zeroes the value of the node specified by the supplied path from
515635// the specified root, whose schema must also be supplied. If the node
516636// specified by that path is already its zero value, or an intermediate node
517637// in the path is nil (implying the node is already deleted), then the call is a no-op.
518- func DeleteNode (schema * yang.Entry , root interface {}, path * gpb.Path ) error {
638+ func DeleteNode (schema * yang.Entry , root interface {}, path * gpb.Path , opts ... DelNodeOpt ) error {
519639 _ , err := retrieveNode (schema , root , path , nil , retrieveNodeArgs {
520- delete : true ,
640+ delete : true ,
641+ reverseShadowPath : hasDelNodeReverseShadowPaths (opts ),
521642 })
522643
523644 return err
0 commit comments