15
15
// Package ygot contains helper methods for dealing with structs that represent
16
16
// a YANG schema. Particularly, it takes structs that represent a YANG schema -
17
17
// generated by ygen:
18
- // - Provides helper functions which simplify their usage such as functions
19
- // to return pointers to a type.
20
- // - Renders structs to other output formats such as JSON, or gNMI
21
- // notifications.
18
+ // - Provides helper functions which simplify their usage such as functions
19
+ // to return pointers to a type.
20
+ // - Renders structs to other output formats such as JSON, or gNMI
21
+ // notifications.
22
22
package ygot
23
23
24
24
import (
@@ -554,6 +554,17 @@ type MergeOverwriteExistingFields struct{}
554
554
// IsMergeOpt marks MergeStructOpt as a MergeOpt.
555
555
func (* MergeOverwriteExistingFields ) IsMergeOpt () {}
556
556
557
+ // MergeOverlappingSlices is a MergeOpt that allows control of the merge behaviour
558
+ // of MergeStructs and MergeStructInto functions.
559
+ //
560
+ // When used, slices (YANG leaf-lists) with overlapping elements will be merged
561
+ // such that only those elements of the source are appended to the destination
562
+ // that are not already present.
563
+ type MergeOverlappingSlices struct {}
564
+
565
+ // IsMergeOpt marks MergeStructOpt as a MergeOpt.
566
+ func (* MergeOverlappingSlices ) IsMergeOpt () {}
567
+
557
568
// MergeStructs takes two input ValidatedGoStructs and merges their contents,
558
569
// returning a new ValidatedGoStruct. If the input structs a and b are of
559
570
// different types, an error is returned.
@@ -620,6 +631,18 @@ func fieldOverwriteEnabled(opts []MergeOpt) bool {
620
631
return false
621
632
}
622
633
634
+ // mergeOverlappigSlicesEnabled returns true if MergeOverlappingSlices
635
+ // is present in the slice of MergeOpt.
636
+ func mergeOverlappigSlicesEnabled (opts []MergeOpt ) bool {
637
+ for _ , o := range opts {
638
+ switch o .(type ) {
639
+ case * MergeOverlappingSlices :
640
+ return true
641
+ }
642
+ }
643
+ return false
644
+ }
645
+
623
646
// copyStruct copies the fields of srcVal into the dstVal struct in-place.
624
647
func copyStruct (dstVal , srcVal reflect.Value , opts ... MergeOpt ) error {
625
648
if srcVal .Type () != dstVal .Type () {
@@ -870,32 +893,51 @@ func copySliceField(dstField, srcField reflect.Value, opts ...MergeOpt) error {
870
893
return nil
871
894
}
872
895
896
+ dstFieldUnqiue := map [interface {}]struct {}{}
873
897
if _ , ok := srcField .Interface ().([]Annotation ); ! ok {
874
898
if reflect .DeepEqual (srcField .Interface (), dstField .Interface ()) {
875
899
return nil
876
900
}
877
-
878
- unique , err := uniqueSlices (dstField , srcField )
901
+ _ , err := validateSlices (dstField , srcField )
879
902
if err != nil {
880
- return fmt .Errorf ("error checking src and dst for uniqueness , got: %v" , err )
903
+ return fmt .Errorf ("error validating src and dst, got: %v" , err )
881
904
}
882
-
883
- if ! unique {
884
- // YANG lists and leaf-lists must be unique.
885
- return fmt .Errorf ("source and destination lists must be unique, got src: %v, dst: %v" , srcField , dstField )
905
+ if ! mergeOverlappigSlicesEnabled (opts ) {
906
+ unique , err := uniqueSlices (dstField , srcField )
907
+ if err != nil {
908
+ return fmt .Errorf ("error checking src and dst for uniqueness, got: %v" , err )
909
+ }
910
+ if ! unique {
911
+ // YANG lists and leaf-lists must be unique.
912
+ return fmt .Errorf ("source and destination lists must be unique, got src: %v, dst: %v" , srcField , dstField )
913
+ }
914
+ } else {
915
+ for i := 0 ; i < dstField .Len (); i ++ {
916
+ dstFieldUnqiue [dstField .Index (i ).Interface ()] = struct {}{}
917
+ }
886
918
}
887
919
}
888
920
889
921
if ! util .IsTypeStructPtr (srcField .Type ().Elem ()) {
890
922
for i := 0 ; i < srcField .Len (); i ++ {
891
923
v := srcField .Index (i )
924
+ if mergeOverlappigSlicesEnabled (opts ) {
925
+ if _ , present := dstFieldUnqiue [v .Interface ()]; present {
926
+ continue
927
+ }
928
+ }
892
929
dstField .Set (reflect .Append (dstField , v ))
893
930
}
894
931
return nil
895
932
}
896
933
897
934
for i := 0 ; i < srcField .Len (); i ++ {
898
935
v := srcField .Index (i )
936
+ if mergeOverlappigSlicesEnabled (opts ) {
937
+ if _ , present := dstFieldUnqiue [v .Interface ()]; present {
938
+ continue
939
+ }
940
+ }
899
941
d := reflect .New (v .Type ().Elem ())
900
942
if err := copyStruct (d .Elem (), v .Elem (), opts ... ); err != nil {
901
943
return err
@@ -909,14 +951,6 @@ func copySliceField(dstField, srcField reflect.Value, opts ...MergeOpt) error {
909
951
// whether a and b are disjoint. It returns true if the slices have unique
910
952
// members, and false if not.
911
953
func uniqueSlices (a , b reflect.Value ) (bool , error ) {
912
- if ! util .IsValueSlice (a ) || ! util .IsValueSlice (b ) {
913
- return false , fmt .Errorf ("a and b must both be slices, got a: %v, b: %v" , a .Type ().Kind (), b .Type ().Kind ())
914
- }
915
-
916
- if a .Type ().Elem () != b .Type ().Elem () {
917
- return false , fmt .Errorf ("a and b do not contain the same type, got a: %v, b: %v" , a .Type ().Elem ().Kind (), b .Type ().Elem ().Kind ())
918
- }
919
-
920
954
for i := 0 ; i < a .Len (); i ++ {
921
955
for j := 0 ; j < b .Len (); j ++ {
922
956
if reflect .DeepEqual (a .Index (i ).Interface (), b .Index (j ).Interface ()) {
@@ -926,3 +960,13 @@ func uniqueSlices(a, b reflect.Value) (bool, error) {
926
960
}
927
961
return true , nil
928
962
}
963
+
964
+ func validateSlices (a , b reflect.Value ) (bool , error ) {
965
+ if ! util .IsValueSlice (a ) || ! util .IsValueSlice (b ) {
966
+ return false , fmt .Errorf ("a and b must both be slices, got a: %v, b: %v" , a .Type ().Kind (), b .Type ().Kind ())
967
+ }
968
+ if a .Type ().Elem () != b .Type ().Elem () {
969
+ return false , fmt .Errorf ("a and b do not contain the same type, got a: %v, b: %v" , a .Type ().Elem ().Kind (), b .Type ().Elem ().Kind ())
970
+ }
971
+ return true , nil
972
+ }
0 commit comments