This commit is contained in:
Daniel Smith 2019-07-19 15:34:30 -07:00
parent f55efb8dae
commit 3c81a9a1c9
16 changed files with 441 additions and 104 deletions

2
go.mod
View File

@ -467,7 +467,7 @@ replace (
modernc.org/strutil => modernc.org/strutil v1.0.0
modernc.org/xc => modernc.org/xc v1.0.0
sigs.k8s.io/kustomize => sigs.k8s.io/kustomize v2.0.3+incompatible
sigs.k8s.io/structured-merge-diff => sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143
sigs.k8s.io/structured-merge-diff => sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3
sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.1.0
vbom.ml/util => vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc
)

4
go.sum
View File

@ -484,8 +484,8 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc h1:MksmcCZQWAQJCTA5T0jgI/0sJ51AVm4Z41MrmfczEoc=

View File

@ -310,7 +310,7 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -59,7 +59,7 @@ require (
k8s.io/klog v0.3.1
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3
sigs.k8s.io/yaml v1.1.0
)

View File

@ -229,7 +229,7 @@ k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMI
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -266,7 +266,7 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -209,6 +209,6 @@ k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4y
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw=
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -263,7 +263,7 @@ modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 h1:9df9ZMJ21XLKSCuwZyI+qfbnQl/hDmrM7wjBK85iv8g=
sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 h1:yCY9zAYErawYwXdOYmwEBzcGCr/6eIUujYZE2DIQve8=
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

2
vendor/modules.txt vendored
View File

@ -1752,7 +1752,7 @@ sigs.k8s.io/kustomize/pkg/transformers
sigs.k8s.io/kustomize/pkg/transformers/config
sigs.k8s.io/kustomize/pkg/transformers/config/defaultconfig
sigs.k8s.io/kustomize/pkg/types
# sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143 => sigs.k8s.io/structured-merge-diff v0.0.0-20190628201129-059502f64143
# sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3 => sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3
sigs.k8s.io/structured-merge-diff/fieldpath
sigs.k8s.io/structured-merge-diff/merge
sigs.k8s.io/structured-merge-diff/schema

View File

@ -35,7 +35,7 @@ type PathElement struct {
// Key selects the list element which has fields matching those given.
// The containing object must be an associative list with map typed
// elements.
Key []value.Field
Key *value.Map
// Value selects the list element with the given value. The containing
// object must be an associative list with a primitive typed element
@ -47,14 +47,62 @@ type PathElement struct {
Index *int
}
// Less provides an order for path elements.
func (e PathElement) Less(rhs PathElement) bool {
if e.FieldName != nil {
if rhs.FieldName == nil {
return true
}
return *e.FieldName < *rhs.FieldName
} else if rhs.FieldName != nil {
return false
}
if e.Key != nil {
if rhs.Key == nil {
return true
}
return e.Key.Less(rhs.Key)
} else if rhs.Key != nil {
return false
}
if e.Value != nil {
if rhs.Value == nil {
return true
}
return e.Value.Less(*rhs.Value)
} else if rhs.Value != nil {
return false
}
if e.Index != nil {
if rhs.Index == nil {
return true
}
return *e.Index < *rhs.Index
} else if rhs.Index != nil {
// Yes, I know the next statement is the same. But this way
// the obvious way of extending the function wil be bug-free.
return false
}
return false
}
// Equals returns true if both path elements are equal.
func (e PathElement) Equals(rhs PathElement) bool {
return !e.Less(rhs) && !rhs.Less(e)
}
// String presents the path element as a human-readable string.
func (e PathElement) String() string {
switch {
case e.FieldName != nil:
return "." + *e.FieldName
case len(e.Key) > 0:
strs := make([]string, len(e.Key))
for i, k := range e.Key {
case e.Key != nil:
strs := make([]string, len(e.Key.Items))
for i, k := range e.Key.Items {
strs[i] = fmt.Sprintf("%v=%v", k.Name, k.Value)
}
// The order must be canonical, since we use the string value
@ -92,62 +140,100 @@ func KeyByFields(nameValues ...interface{}) []value.Field {
// PathElementSet is a set of path elements.
// TODO: serialize as a list.
type PathElementSet struct {
// The strange construction is because there's no way to test
// PathElements for equality (it can't be used as a key for a map).
members map[string]PathElement
members sortedPathElements
}
type sortedPathElements []PathElement
// Implement the sort interface; this would permit bulk creation, which would
// be faster than doing it one at a time via Insert.
func (spe sortedPathElements) Len() int { return len(spe) }
func (spe sortedPathElements) Less(i, j int) bool { return spe[i].Less(spe[j]) }
func (spe sortedPathElements) Swap(i, j int) { spe[i], spe[j] = spe[j], spe[i] }
// Insert adds pe to the set.
func (s *PathElementSet) Insert(pe PathElement) {
serialized := pe.String()
if s.members == nil {
s.members = map[string]PathElement{
serialized: pe,
}
loc := sort.Search(len(s.members), func(i int) bool {
return !s.members[i].Less(pe)
})
if loc == len(s.members) {
s.members = append(s.members, pe)
return
}
if _, ok := s.members[serialized]; !ok {
s.members[serialized] = pe
if s.members[loc].Equals(pe) {
return
}
s.members = append(s.members, PathElement{})
copy(s.members[loc+1:], s.members[loc:])
s.members[loc] = pe
}
// Union returns a set containing elements that appear in either s or s2.
func (s *PathElementSet) Union(s2 *PathElementSet) *PathElementSet {
out := &PathElementSet{
members: map[string]PathElement{},
out := &PathElementSet{}
i, j := 0, 0
for i < len(s.members) && j < len(s2.members) {
if s.members[i].Less(s2.members[j]) {
out.members = append(out.members, s.members[i])
i++
} else {
out.members = append(out.members, s2.members[j])
if !s2.members[j].Less(s.members[i]) {
i++
}
j++
}
}
for k, v := range s.members {
out.members[k] = v
if i < len(s.members) {
out.members = append(out.members, s.members[i:]...)
}
for k, v := range s2.members {
out.members[k] = v
if j < len(s2.members) {
out.members = append(out.members, s2.members[j:]...)
}
return out
}
// Intersection returns a set containing elements which appear in both s and s2.
func (s *PathElementSet) Intersection(s2 *PathElementSet) *PathElementSet {
out := &PathElementSet{
members: map[string]PathElement{},
}
for k, v := range s.members {
if _, ok := s2.members[k]; ok {
out.members[k] = v
out := &PathElementSet{}
i, j := 0, 0
for i < len(s.members) && j < len(s2.members) {
if s.members[i].Less(s2.members[j]) {
i++
} else {
if !s2.members[j].Less(s.members[i]) {
out.members = append(out.members, s.members[i])
i++
}
j++
}
}
return out
}
// Difference returns a set containing elements which appear in s but not in s2.
func (s *PathElementSet) Difference(s2 *PathElementSet) *PathElementSet {
out := &PathElementSet{
members: map[string]PathElement{},
}
for k, v := range s.members {
if _, ok := s2.members[k]; !ok {
out.members[k] = v
out := &PathElementSet{}
i, j := 0, 0
for i < len(s.members) && j < len(s2.members) {
if s.members[i].Less(s2.members[j]) {
out.members = append(out.members, s.members[i])
i++
} else {
if !s2.members[j].Less(s.members[i]) {
i++
}
j++
}
}
if i < len(s.members) {
out.members = append(out.members, s.members[i:]...)
}
return out
}
@ -156,11 +242,16 @@ func (s *PathElementSet) Size() int { return len(s.members) }
// Has returns true if pe is a member of the set.
func (s *PathElementSet) Has(pe PathElement) bool {
if s.members == nil {
loc := sort.Search(len(s.members), func(i int) bool {
return !s.members[i].Less(pe)
})
if loc == len(s.members) {
return false
}
_, ok := s.members[pe.String()]
return ok
if s.members[loc].Equals(pe) {
return true
}
return false
}
// Equals returns true if s and s2 have exactly the same members.
@ -169,14 +260,14 @@ func (s *PathElementSet) Equals(s2 *PathElementSet) bool {
return false
}
for k := range s.members {
if _, ok := s2.members[k]; !ok {
if !s.members[k].Equals(s2.members[k]) {
return false
}
}
return true
}
// Iterate calls f for each PathElement in the set.
// Iterate calls f for each PathElement in the set. The order is deterministic.
func (s *PathElementSet) Iterate(f func(PathElement)) {
for _, pe := range s.members {
f(pe)

View File

@ -117,7 +117,7 @@ func GuessBestListPathElement(index int, item value.Value) PathElement {
keys = append(keys, *f)
}
if len(keys) > 0 {
return PathElement{Key: keys}
return PathElement{Key: &value.Map{Items: keys}}
}
return PathElement{Index: &index}
}

View File

@ -35,6 +35,40 @@ func (fp Path) String() string {
return strings.Join(strs, "")
}
// Equals returns true if the two paths are equivalent.
func (fp Path) Equals(fp2 Path) bool {
return !fp.Less(fp2) && !fp2.Less(fp)
}
// Less provides a lexical order for Paths.
func (fp Path) Less(rhs Path) bool {
i := 0
for {
if i >= len(fp) && i >= len(rhs) {
// Paths are the same length and all items are equal.
return false
}
if i >= len(fp) {
// LHS is shorter.
return true
}
if i >= len(rhs) {
// RHS is shorter.
return false
}
if fp[i].Less(rhs[i]) {
// LHS is less; return
return true
}
if rhs[i].Less(fp[i]) {
// RHS is less; return
return false
}
// The items are equal; continue.
i++
}
}
func (fp Path) Copy() Path {
new := make(Path, len(fp))
copy(new, fp)
@ -58,7 +92,7 @@ func MakePath(parts ...interface{}) (Path, error) {
if len(t) == 0 {
return nil, fmt.Errorf("associative list key type path elements must have at least one key (got zero)")
}
fp = append(fp, PathElement{Key: t})
fp = append(fp, PathElement{Key: &value.Map{Items: t}})
case value.Value:
// TODO: understand schema and verify that this is a set type
// TODO: make a copy of t

View File

@ -17,6 +17,7 @@ limitations under the License.
package fieldpath
import (
"sort"
"strings"
)
@ -172,24 +173,25 @@ type setNode struct {
// SetNodeMap is a map of PathElement to subset.
type SetNodeMap struct {
members map[string]setNode
members []setNode
}
// Descend adds pe to the set if necessary, returning the associated subset.
func (s *SetNodeMap) Descend(pe PathElement) *Set {
serialized := pe.String()
if s.members == nil {
s.members = map[string]setNode{}
loc := sort.Search(len(s.members), func(i int) bool {
return !s.members[i].pathElement.Less(pe)
})
if loc == len(s.members) {
s.members = append(s.members, setNode{pathElement: pe, set: &Set{}})
return s.members[loc].set
}
if n, ok := s.members[serialized]; ok {
return n.set
if s.members[loc].pathElement.Equals(pe) {
return s.members[loc].set
}
ss := &Set{}
s.members[serialized] = setNode{
pathElement: pe,
set: ss,
}
return ss
s.members = append(s.members, setNode{})
copy(s.members[loc+1:], s.members[loc:])
s.members[loc] = setNode{pathElement: pe, set: &Set{}}
return s.members[loc].set
}
// Size returns the sum of the number of members of all subsets.
@ -213,12 +215,14 @@ func (s *SetNodeMap) Empty() bool {
// Get returns (the associated set, true) or (nil, false) if there is none.
func (s *SetNodeMap) Get(pe PathElement) (*Set, bool) {
if s.members == nil {
loc := sort.Search(len(s.members), func(i int) bool {
return !s.members[i].pathElement.Less(pe)
})
if loc == len(s.members) {
return nil, false
}
serialized := pe.String()
if n, ok := s.members[serialized]; ok {
return n.set, true
if s.members[loc].pathElement.Equals(pe) {
return s.members[loc].set, true
}
return nil, false
}
@ -229,12 +233,11 @@ func (s *SetNodeMap) Equals(s2 *SetNodeMap) bool {
if len(s.members) != len(s2.members) {
return false
}
for k, v := range s.members {
v2, ok := s2.members[k]
if !ok {
for i := range s.members {
if !s.members[i].pathElement.Equals(s2.members[i].pathElement) {
return false
}
if !v.set.Equals(v2.set) {
if !s.members[i].set.Equals(s2.members[i].set) {
return false
}
}
@ -244,21 +247,28 @@ func (s *SetNodeMap) Equals(s2 *SetNodeMap) bool {
// Union returns a SetNodeMap with members that appear in either s or s2.
func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap {
out := &SetNodeMap{}
for k, sn := range s.members {
pe := sn.pathElement
if sn2, ok := s2.members[k]; ok {
*out.Descend(pe) = *sn.set.Union(sn2.set)
i, j := 0, 0
for i < len(s.members) && j < len(s2.members) {
if s.members[i].pathElement.Less(s2.members[j].pathElement) {
out.members = append(out.members, s.members[i])
i++
} else {
*out.Descend(pe) = *sn.set
if !s2.members[j].pathElement.Less(s.members[i].pathElement) {
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set.Union(s2.members[j].set)})
i++
} else {
out.members = append(out.members, s2.members[j])
}
j++
}
}
for k, sn2 := range s2.members {
pe := sn2.pathElement
if _, ok := s.members[k]; ok {
// already handled
continue
}
*out.Descend(pe) = *sn2.set
if i < len(s.members) {
out.members = append(out.members, s.members[i:]...)
}
if j < len(s2.members) {
out.members = append(out.members, s2.members[j:]...)
}
return out
}
@ -266,13 +276,20 @@ func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap {
// Intersection returns a SetNodeMap with members that appear in both s and s2.
func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap {
out := &SetNodeMap{}
for k, sn := range s.members {
pe := sn.pathElement
if sn2, ok := s2.members[k]; ok {
i := *sn.set.Intersection(sn2.set)
if !i.Empty() {
*out.Descend(pe) = i
i, j := 0, 0
for i < len(s.members) && j < len(s2.members) {
if s.members[i].pathElement.Less(s2.members[j].pathElement) {
i++
} else {
if !s2.members[j].pathElement.Less(s.members[i].pathElement) {
res := s.members[i].set.Intersection(s2.members[j].set)
if !res.Empty() {
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: res})
}
i++
}
j++
}
}
return out
@ -281,18 +298,30 @@ func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap {
// Difference returns a SetNodeMap with members that appear in s but not in s2.
func (s *SetNodeMap) Difference(s2 *Set) *SetNodeMap {
out := &SetNodeMap{}
for k, sn := range s.members {
pe := sn.pathElement
if sn2, ok := s2.Children.members[k]; ok {
diff := *sn.set.Difference(sn2.set)
// We aren't permitted to add nodes with no elements.
if !diff.Empty() {
*out.Descend(pe) = diff
}
i, j := 0, 0
for i < len(s.members) && j < len(s2.Children.members) {
if s.members[i].pathElement.Less(s2.Children.members[j].pathElement) {
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set})
i++
} else {
*out.Descend(pe) = *sn.set
if !s2.Children.members[j].pathElement.Less(s.members[i].pathElement) {
diff := s.members[i].set.Difference(s2.Children.members[j].set)
// We aren't permitted to add nodes with no elements.
if !diff.Empty() {
out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: diff})
}
i++
}
j++
}
}
if i < len(s.members) {
out.members = append(out.members, s.members[i:]...)
}
return out
}

View File

@ -40,6 +40,14 @@ func (c Conflict) Error() string {
return fmt.Sprintf("conflict with %q: %v", c.Manager, c.Path)
}
// Equals returns true if c == c2
func (c Conflict) Equals(c2 Conflict) bool {
if c.Manager != c2.Manager {
return false
}
return c.Path.Equals(c2.Path)
}
// Conflicts accumulates multiple conflicts and aggregates them by managers.
type Conflicts []Conflict
@ -74,6 +82,19 @@ func (conflicts Conflicts) Error() string {
return strings.Join(messages, "\n")
}
// Equals returns true if the lists of conflicts are the same.
func (c Conflicts) Equals(c2 Conflicts) bool {
if len(c) != len(c2) {
return false
}
for i := range c {
if !c[i].Equals(c2[i]) {
return false
}
}
return true
}
// ConflictsFromManagers creates a list of conflicts given Managers sets.
func ConflictsFromManagers(sets fieldpath.ManagedFields) Conflicts {
conflicts := []Conflict{}

View File

@ -197,6 +197,7 @@ func keyedAssociativeListItemToPathElement(list schema.List, index int, child va
if child.MapValue == nil {
return pe, errors.New("associative list with keys may not have non-map elements")
}
keyMap := &value.Map{}
for _, fieldName := range list.Keys {
var fieldValue value.Value
field, ok := child.MapValue.Get(fieldName)
@ -206,11 +207,9 @@ func keyedAssociativeListItemToPathElement(list schema.List, index int, child va
// Treat keys as required.
return pe, fmt.Errorf("associative list with keys has an element that omits key field %q", fieldName)
}
pe.Key = append(pe.Key, value.Field{
Name: fieldName,
Value: fieldValue,
})
keyMap.Set(fieldName, fieldValue)
}
pe.Key = keyMap
return pe, nil
}

View File

@ -18,6 +18,7 @@ package value
import (
"fmt"
"sort"
"strings"
)
@ -33,6 +34,85 @@ type Value struct {
Null bool // represents an explicit `"foo" = null`
}
// Less provides a total ordering for Value (so that they can be sorted, even
// if they are of different types).
func (v Value) Less(rhs Value) bool {
if v.FloatValue != nil {
if rhs.FloatValue == nil {
// Extra: compare floats and ints numerically.
if rhs.IntValue != nil {
return float64(*v.FloatValue) < float64(*rhs.IntValue)
}
return true
}
return *v.FloatValue < *rhs.FloatValue
} else if rhs.FloatValue != nil {
// Extra: compare floats and ints numerically.
if v.IntValue != nil {
return float64(*v.IntValue) < float64(*rhs.FloatValue)
}
return false
}
if v.IntValue != nil {
if rhs.IntValue == nil {
return true
}
return *v.IntValue < *rhs.IntValue
} else if rhs.IntValue != nil {
return false
}
if v.StringValue != nil {
if rhs.StringValue == nil {
return true
}
return *v.StringValue < *rhs.StringValue
} else if rhs.StringValue != nil {
return false
}
if v.BooleanValue != nil {
if rhs.BooleanValue == nil {
return true
}
if *v.BooleanValue == *rhs.BooleanValue {
return false
}
return *v.BooleanValue == false
} else if rhs.BooleanValue != nil {
return false
}
if v.ListValue != nil {
if rhs.ListValue == nil {
return true
}
return v.ListValue.Less(rhs.ListValue)
} else if rhs.ListValue != nil {
return false
}
if v.MapValue != nil {
if rhs.MapValue == nil {
return true
}
return v.MapValue.Less(rhs.MapValue)
} else if rhs.MapValue != nil {
return false
}
if v.Null {
if !rhs.Null {
return true
}
return false
} else if rhs.Null {
return false
}
// Invalid Value-- nothing is set.
return false
}
type Int int64
type Float float64
type String string
@ -49,6 +129,35 @@ type List struct {
Items []Value
}
// Less compares two lists lexically.
func (l *List) Less(rhs *List) bool {
i := 0
for {
if i >= len(l.Items) && i >= len(rhs.Items) {
// Lists are the same length and all items are equal.
return false
}
if i >= len(l.Items) {
// LHS is shorter.
return true
}
if i >= len(rhs.Items) {
// RHS is shorter.
return false
}
if l.Items[i].Less(rhs.Items[i]) {
// LHS is less; return
return true
}
if rhs.Items[i].Less(l.Items[i]) {
// RHS is less; return
return false
}
// The items are equal; continue.
i++
}
}
// Map is a map of key-value pairs. It represents both structs and maps. We use
// a list and a go-language map to preserve order.
//
@ -59,6 +168,58 @@ type Map struct {
// may be nil; lazily constructed.
// TODO: Direct modifications to Items above will cause serious problems.
index map[string]*Field
// may be empty; lazily constructed.
// TODO: Direct modifications to Items above will cause serious problems.
order []int
}
func (m *Map) computeOrder() {
if len(m.order) != len(m.Items) {
m.order = make([]int, len(m.Items))
for i := range m.order {
m.order[i] = i
}
sort.SliceStable(m.order, func(i, j int) bool {
return m.Items[m.order[i]].Name < m.Items[m.order[j]].Name
})
}
}
// Less compares two maps lexically.
func (m *Map) Less(rhs *Map) bool {
m.computeOrder()
rhs.computeOrder()
i := 0
for {
if i >= len(m.order) && i >= len(rhs.order) {
// Maps are the same length and all items are equal.
return false
}
if i >= len(m.order) {
// LHS is shorter.
return true
}
if i >= len(rhs.order) {
// RHS is shorter.
return false
}
fa, fb := &m.Items[m.order[i]], &rhs.Items[rhs.order[i]]
if fa.Name != fb.Name {
// the map having the field name that sorts lexically less is "less"
return fa.Name < fb.Name
}
if fa.Value.Less(fb.Value) {
// LHS is less; return
return true
}
if fb.Value.Less(fa.Value) {
// RHS is less; return
return false
}
// The items are equal; continue.
i++
}
}
// Get returns the (Field, true) or (nil, false) if it is not present
@ -82,6 +243,7 @@ func (m *Map) Set(key string, value Value) {
}
m.Items = append(m.Items, Field{Name: key, Value: value})
m.index = nil // Since the append might have reallocated
m.order = nil
}
// Delete removes the key from the set.
@ -94,6 +256,7 @@ func (m *Map) Delete(key string) {
}
m.Items = items
m.index = nil // Since the list has changed
m.order = nil
}
// StringValue returns s as a scalar string Value.